Everyday Docker is becoming more and more popular. It’s easier to find developers who know Docker and its easier to build tooling around it. The popularity of container management platforms such as Amazon’s ECS, Google’s ECS and Hashicorp’s Nomad are proof that Docker is not slowing down anytime soon. Now, with the growing exposure of this technology, organizations are finding special needs and edge cases to Docker’s uses.

Thankfully, Docker offers a remote API that developers can use to build tooling for handling these special needs. However, enabling Docker’s remote API is a bit intrusive, requiring a configuration change to the Docker daemon and restarting the service. The pain is eased with Docker’s compatibility with RunC, allowing the daemon to be restarted without affecting the state of containers. But then theres also the issue of opening the docker daemon to vulnerabilities; once the remote API is enabled, everything that can access the server at the enabled port will have access to the full Docker host’s command.

Still, what if your organization needs changes now? After all, today’s tech world is all about speed and TTM. Will you need to modify all hosts in your org to enable the remote API? Or what if you are running a version of docker pre-1.11? How do you mitigate the downtime? What about the fear of opening up your hosts?

I've looked around and found several blog posts of what others have done, but they all seem to involve modifying the Docker daemon + restarting. I thought there had to be an easier way to do this. That we should be able to enable remote API without making changes to the daemon, and that access control to the API should be configurable. After looking for a few hours with no luck, I figured I'd build something myself.

The first thought on my mind was that Docker's remote API is a RESTful API. This means that it can be controlled by a reverse-proxy--NGINX. Then I remembered that NGINX can control quite about anything on the HTTP level, including URI pathing, headers, and query params. But how does one take NGINX and build a configurable system that can adapt to a user's need? It'd be a PITA to have to manipulate and reload NGINX configuration files for any changes. It needed to be easier.

So... let's wrap it in python! Lets' make it configurable! Let's make it easy for anyone to use! Let's throw it into a Docker container! Viola! Sherpa was born.

Sherpa is an NGINX reverse proxy, wrapped in python for configurability, that opens a path to the Docker's docker.sock unix socket; the socket enabled by default for all Docker daemons.

You can follow the popular methods of changing the Docker daemon config, then restarting the Docker daemon, OR you can run:

docker run -d \ -v / var /run/docker.sock: /tmp/ docker.sock \ -p 4550 : 4550 \ djenriquez/sherpa --allow

Want to allow just access to container information?:

docker run -d \ -e CONFIG ='[ { "Path" : "/containers/json" , "Access" : "allow" , "Methods" : [ "GET" ] } ]' \ -v / var /run/docker.sock:/tmp/docker.sock \ -p 4550 : 4550 \ djenriquez/sherpa

Then if you attempt to access a different endpoint that is implicitly blocked:

Or what if you want to provide access to everything but the ability to kill containers?:

docker run -d \ --name sherpa \ -e CONFIG ='[ { "Path" : "/containers/*/kill" , "Access" : "deny" } ]' \ -v / var /run/docker.sock:/tmp/docker.sock \ -p 4550 : 4550 \ djenriquez/sherpa --allow

Sherpa gives the ability to set implicit denies or allows. Then, explicitly define specific paths and HTTP methods to allow or deny.

With Sherpa, enabling Docker's remote API is as simple as running a docker container. No more modifying configuration, no more restarting Docker daemons.

Check out Sherpa's Github and DockerHub repos for more information! I'm definitely not an expert developer, so contributions and feedback are encouraged and sincerely welcomed; please help make Sherpa better!

Update: Sherpa v0.3.0 was just released. This version now includes source address filtering so that you can run something like:

docker run -d \ --name sherpa \ -e CONFIG ='[ { "Path" : "/" , "Access" : "allow" , "Addresses" : [ "172.0.0.0/8" ] } ]' \ -v / var /run/docker.sock:/tmp/docker.sock \ -p 4550 : 4550 \ djenriquez/sherpa --allow

The above run will provide full access but ONLY if the client exists in a private IP space listed above. You can specify single IPs, IPv6 addresses or even unix sockets.

See more of my blog posts at djenriquez.com