Wearing your docker.sock on your sleeve? Use a proxy instead
Certain containers need access to your docker socket, which allows them to view and have access to all docker containers and settings. Portainer is a great example of this.
One way of allowing this is to map the following volume to the container:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
But doing this, especially in a publicly accessible container, is a security risk.
Instead we use a socket proxy to limit access and keep our system protected, and for that, you guessed it, we need to create another container. Luckily this works pretty easily with minimal fuss.
Prerequisites
- Docker and docker-compose installed on your machine
- sudo privileges to be able to run docker commands as root, or you've added your user to the docker group to bypass that
- The ability to SSH / use CLI/terminal on your machine, or use Portainer to spin up your stacks
Creating the container
- Create your
docker-compose.yml
file, and then copy the below into it:
networks:
socket_proxy:
name: socket_proxy
ipam:
config:
- subnet: 172.100.0.0/24 #change subnet as necessary
services:
socket-proxy:
container_name: socket-proxy
image: tecnativa/docker-socket-proxy
restart: always
networks:
- socket_proxy
# privileged: true # true for VM. False for unprivileged LXC container.
ports:
# - "127.0.0.1:2375:2375" # Port 2375 should only ever get exposed to the internal network. When possible use this line.
# I use the next line instead, as I want portainer to manage multiple docker endpoints within my home network.
- "2375:2375"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
environment:
- LOG_LEVEL=info # debug,info,notice,warning,err,crit,alert,emerg
## Variables match the URL prefix (i.e. AUTH blocks access to /auth/* parts of the API, etc.).
# 0 to revoke access.
# 1 to grant access.
## Granted by Default
- EVENTS=1
- PING=1
- VERSION=1
## Revoked by Default
# Security critical
- AUTH=0
- SECRETS=0
- POST=1 # Watchtower
# Not always needed
- BUILD=0
- COMMIT=0
- CONFIGS=0
- CONTAINERS=1 # Traefik, portainer, etc.
- DISTRIBUTION=0
- EXEC=0
- IMAGES=1 # Portainer
- INFO=1 # Portainer
- NETWORKS=1 # Portainer
- NODES=0
- PLUGINS=0
- SERVICES=1 # Portainer
- SESSION=0
- SWARM=0
- SYSTEM=0
- TASKS=1 # Portainer
- VOLUMES=1 # Portainer
What are we doing here?
- We're creating a docker bridge network called
socket_proxy
- We're creating a container called
socket-proxy
and connecting it to our docker network - We're making this container accessible at port
2375
- I want it to always restart whenever docker or the system restarts, as other containers depend on it
- We are giving it access to and specifying which parts of the
docker.sock
it can read (0
= no access,1
= access)
- Spin up the container using Portainer or your terminal (
docker-compose up -d
)
Connecting a container to the socket proxy
Now that the container is up and running, we can use it in place of the volume mapping mentioned above for those containers needing docker socket access.
To do this, we need to add one env variable, and connect the container in question to the socket_proxy
docker network. Let's take Watchtower as an example, and the following docker-compose.yml
for it:
networks:
default:
name: socket_proxy
external: true
services:
watchtower: #automatic container version monitoring and updating
container_name: watchtower
image: containrrr/watchtower:latest-dev
environment:
- TZ=$TZ
- DEBUG=true
- WATCHTOWER_LABEL_ENABLE=true
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_INCLUDE_RESTARTING=true
- WATCHTOWER_INCLUDE_STOPPED=true
- WATCHTOWER_NOTIFICATIONS=shoutrrr
- WATCHTOWER_NOTIFICATION_URL=pushover://shoutrrr:[email protected]$PUSHKEY/?devices=$PUSHDEVICE
- DOCKER_HOST=tcp://socket-proxy:2375
command: --interval 21600
restart: unless-stopped
volumes:
- $LOCALTIME
networks:
- socket_proxy
- Note the
DOCKER_HOST
in theenvironment
block, and thenetworks
- This assumes the socket-proxy container is in a different docker-compose file to Watchtower. If they were in the same compose file, you could simply write
DOCKER_HOST=[servicename]
, so in our case,DOCKER_HOST=socket-proxy
You now have a fully functioning proxy to protect your docker.sock and you can make the above changes to your associated compose files as necessary.
Related Articles


Have some feedback or something to add? Comments are welcome!
Please note comments should be respectful, and may be moderated