Tecnativa / docker-socket-proxy

Proxy over your Docker socket to restrict which requests it accepts
Apache License 2.0
1.51k stars 166 forks source link

[Q] Does a docker socket proxy improve security? #87

Open lonix1 opened 1 year ago

lonix1 commented 1 year ago

Originally posted at StackExchange and the docker forums.

I am using this docker image and think it's great, so please don't get offended by my question... I'm not trolling, I am very interested in this topic. Also note that this image has very high visibility because major apps (like Traefik) recommend it. So many people would be interested in this question.


It's dangerous to expose the docker socket to a container, as malicious code could break out into the host. There are various ways to avoid this; a simple/popular approach is to use a proxy between the container and the docker daemon, which filters naughty requests. (The most widely used is Tecnativa Docker Socket Proxy.)

But surely that just moves the risk from the operational container (e.g. Traefik or Portainer, which need access to the docker API) to the socket-proxying container (e.g. Tecnativa)? Just as there could be malicious code inside Traefik that would abuse the docker socket, there could be malicious code inside Tecnativa that would abuse the docker socket.

One could even make the argument that it's more sensible to trust Traefik / Portainer (which have huge user bases, and large numbers of developers) to a docker socket proxy. (In other cases, of random never-heard-of container images that wants docker socket access, I'd obviously trust the socket proxy more.)

Is this an example of copy-and-paste-blog-advice, or is there more to it that I don't understand?

SuperSandro2000 commented 1 year ago

Just as there could be malicious code inside Traefik that would abuse the docker socket, there could be malicious code inside Tecnativa that would abuse the docker socket.

That's no the attack vector and very unlikely. The problem with exposing the socket inside of Traefik is that if it has a RCE or it could be tricked to redirect the request to the proxy, you have a privilege escalation. Having the proxy in between first of all filters the requests only allowing read operations for an application like Traefik and now you not only need to trick Traefik with a forged request but also the haproxy, too which is much more involved. Also if there are multiple containers needing access to the socket, it only gets mounted to one container.

lonix1 commented 1 year ago

I have significantly less knowledge of this topic than you, so firstly thank you, and secondly please excuse me for oversimplifying.

In my limited understanding, there is "a risk" of letting Traefik (or Portainer, or ...) access the socket. So we place a proxying mechanism in between them. But "some risk" could exist in the proxy itself.

So ultimately, it's a choice between me trusting the Traefik image (and its authors), or trusting the proxy image (and its authors).

Given that this is a standard way to solve this problem, and so many people are using it (10M+ downloads on docker hub), the risk factor is low (an exploit would be detected quickly). But it's not a "solution", it's just shifting risk around.

Not sure if I've explained myself properly... hope that make sense. Please educate me if I'm misunderstanding!

SuperSandro2000 commented 1 year ago

But "some risk" could exist in the proxy itself.

yeah, sure but worse case there is no improvement at all. It can't get any worse than a complete open socket.

and so many people are using it

that download count is highly inflated. I have myself images with 100k downloads which are definitely not that popular.

lonix1 commented 1 year ago

Thanks Sandro for your advice.

that download count is highly inflated. I have myself images with 100k downloads which are definitely not that popular.

I knew it! I always suspected those numbers are not real, especially for those 1B+ containers.

polarathene commented 2 months ago

it's just shifting risk around.

Not when you have more than one container needing socket access. As explained above, it adds a layer of indirection, the attacker is far more likely to have access to a publicly service they can reach such as through Traefik which may offer more opportunities than the socket proxy.

The other concern with trust in a project and the image published is fair, but that could happen at so many different places (not just Traefik itself for example, but a dependency it uses slipping in a vulnerability to exploit, similar to the xz compromise). Not much you can do about that pragmatically.


One thing that this project could probably do better is avoid using Alpine (with shell and package manager present) in the event it were compromised, such access to tools in the container may better enable them. Traefik image has the same concern, as does Caddy, usually for convenience with troubleshooting (shell access and common commands).

Not too familiar with HAProxy, but Caddy and Traefik can both be built as static binaries and I think the Google distroless images have the supplementary external files they (or Go in general) accesses.

mbrodala commented 1 day ago

Maybe summarized once more: the main benefit is that you avoid having a single service which is both public-facing and has full access to the Docker socket, in this case Traefik. You separate these two concerns by adding the Docker socket proxy.

Assuming an attacker is somehow able to enter the Traefik container, he can only perform readonly safe Docker API requests the same way Traefik is allowed to. He cannot perform write API requests and he also cannot access other API like secrets.

The later is another considerable difference from exposing the Docker socket as readonly to Traefik.

polarathene commented 21 hours ago

the main benefit is that you avoid having a single service which is both public-facing and has full access to the Docker socket, in this case Traefik.

Not really Traefik specific. You could use traefik, nginx, caddy, etc or your own specialized API entrypoint program to provide the same functionality as a separate container from the main public reverse proxy.

Just like this uses HAProxy, you could have HAProxy in front of it too 🤷‍♂️