Remote and secure use of Docker API with Python (Part II of II)

As we saw in Part I, we can easily secure HTTP connections using SSH tunnels. Similarly, we could apply the same strategy to communicate with a server running a Docker daemon. Since we’ll be doing this with Python, to communicate with docker we will use docker-py.

Let’s say that we want to connect from mycomputer (which is in a LAN where other users are connected, like in Part I) to a remote dockerdeamon to execute docker commands using docker-py, as shown in (Fig.1).  For this example, let’s pretend that we would like to list all the docker images in dockerdeamon. First, we need to install docker-py via pip in mycomputer.

bernardo@mycomputer:~$ pip install docker-py

By default, docker daemon listens on unix:///var/run/docker.sock to allow only local connections by the root user.  For remote access, we need to tell the daemon to listen on 0.0.0.0:5555 in order to give remote access, through TCP port 5555. We can do this by editing the file /etc/default/docker in dockerdaemon:

DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:5555"

We load this new configuration and enable the docker daemon service executing the following command in dockerdaemon:

dockerdaemon_user@dockerdaemon:~$ sudo service docker start
Fig.1: In blue, secure docker request through an SSH tunnel. In red, and insecure docker request and docker requests blocked by "dockerdeamon"

Fig.1: In blue, secure docker request through an SSH tunnel. In red, and insecure docker request and docker requests blocked by “dockerdeamon”

Then, we create a python script to achieve our request.

import docker

def list_images(host,port):

	client = docker.Client(base_url="http://%s:%s" % (host,port))
	return client.images()

if __name__ == '__main__':
	remote_docker_daemon_host = "dockerdaemon"
	default_docker_port = 5555
	print list_images(remote_docker_daemon_host, default_docker_port)

If we execute this python script, you guessed it right, it will print all the docker images in dockerdaemon. Any of the other users connected to our LAN could also send commands to our dockerdaemon. Potentially, they could also eavesdrop our communication and know what are we doing with docker (same problem as in Part I). But what matters most in this situation and what we would like to avoid (see Fig.1) is that anyone who is aware of the existence of docker daemon listening on port 5555 can interact with it, whether they are part of our LAN or not, simply by being connected to the Internet. By doing this, a malicious individual could execute any command they wish to the docker daemon, who would simply listen and execute. These issues can be overcome using SSH tunnels.

As a first step, we will need to install openssh-server in the dockerdaemon server and append our public key into:

/home/dockerdaemon_user/.ssh/authorized_keys

This allows us to connect from mycomputer using our private key through ssh. Then, we will open the port TCP 22 to allow us to connect via ssh and we will block any other port (even docker daemon port TCP 5555), thus malicious individuals cannot execute any command in dockerdaemon.

Futhermore, we will need to add the bgtunnel module to our python script to create SSH tunnels in python.

bernardo@mycomputer:~$ pip install bgtunnel

After that, we modified our python script to do our remote docker request through a SSH tunnel as follows:

import docker
import bgtunnel

def list_images(host,port):

	forwarder = bgtunnel.open(ssh_user=dockerdaemon_user,
				ssh_address=host,
				host_port=str(port))

	client = docker.Client(base_url="http://localhost:%d"
					% forwarder.bind_port)
	return client.images()

if __name__ == '__main__':
	remote_docker_daemon_host = "dockerdaemon"
	default_docker_port = 5555
	print list_images(remote_docker_daemon_host, default_docker_port)

First, we open an SSH tunnel to dockerdaemon using bgtunnel giving us the local bound port to send commands through forwarder.bind_port. Then, we  send all docker requests through this tunnel creating a new docker client through localhost:forwarder.bind_port. By doing this, nobody except a user that holds the right SSH certificate can connect and send request to the dockerdaemon. Thus, we avoid other local users potentially eavesdropping or intercepting our remote docker requests, as well as any user in the Internet communicating with our dockerdaemon.

That should be all for now. I hope it helps you communicating with your docker daemon remotely and securely. If you have any questions, please comment below or reach out to me directly on twitter at @bebespaniard.

Posted in Tutorial
2 comments on “Remote and secure use of Docker API with Python (Part II of II)
  1. dis says:

    hi bebe, great tutorial on an alternate solution

    although you mention that the ports should be locked off, you don’t demonstrate how to. that is a bit dangerous approach for a tutorial, especially after demonstrating docker’s global exposure through 0.0.0.0:5555. instead you can expose docker through 127.0.0.1:5555, as you only need to access it through localhost anyway, and it won’t be exposed globally

    cheers

  2. Anvika says:

    Hi. I am following your blog to learn the API. But i am stuck in the second step :
    sudo service start docker. It says docker : service unrecognised. Please help me to resolve this error.

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories
%d bloggers like this: