Exploring Local Docker Bridge Networks

TL;DR Docker helps you build and manage containers. It cannot protect you from making application layer mistakes.

Lock illustration provided by Guillermo Mont.

I’m gearing up to write chapter 5 of Docker in Action which is going to be about container linking and the network as configured by Docker. Having focused on other parts of the tool recently, I took the day to refamiliarize myself with links and dive deep into networking.

I want to show you something.

This won’t be anything new for those familiar with bridge networks. But I have a feeling that this is something some developers using Docker will not have anticipated. I want to show you how to explore the network created when you launch a few local containers. Doing so will help you understand what Docker container links are.

The following will walk you through a nice little experiment involving accessing a contained MongoDB server from a separate container.

Start your target container

In this case our target is a vanilla MongoDB server. You can install and start one up with the following simple command.

# docker run --name some-mongo -d mongo:latest

Start your rogue container

This is a container running a shell, started from an ubuntu image. You will be inspecting your local network and connecting to your mongo instance from this container.

# docker run -it --rm ubuntu:latest /bin/bash
root@XXX:/#

Start the image in interactive mode so you can install the tools you need and poke around without upsetting the state of your host system.

Get your hacker tools

The tools you need include the Mongo CLI and nmap.

root@XXX:/# apt-get -y install nmap
root@XXX:/# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
root@XXX:/# echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
root@XXX:/# apt-get update
root@XXX:/# apt-get install -y mongodb-org-shell

Since you’re running as the root user inside the container, you needn’t worry about the standard sudo command prefixing. Once these commands have run, you’ll be ready to launch the experiment.

Scan your network

Find the IP address of your container so you can guess where your target might be.

root@XXX:/# MY_IP=`/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`

Look for hosts in the same subnet that are listening on port 27017. Do this with nmap to perform a 27017 port scan of the subnet:

root@XXX:/# nmap -sS -p 27017 $MY_IP/24

The results of the network port scan in my test environment were as follows:

Starting Nmap 6.40 ( http://nmap.org ) at 2014-12-09 15:52 UTC
Nmap scan report for 172.17.0.33
Host is up (0.000030s latency).
PORT STATE SERVICE
27017/tcp closed unknown
MAC Address: 02:42:AC:11:00:21 (Unknown)
Nmap scan report for 172.17.0.34
Host is up (0.000021s latency).
PORT STATE SERVICE
27017/tcp closed unknown
MAC Address: 02:42:AC:11:00:22 (Unknown)
Nmap scan report for 172.17.0.96
Host is up (-0.079s latency).
PORT STATE SERVICE
27017/tcp open unknown
MAC Address: 02:42:AC:11:00:60 (Unknown)
Nmap scan report for 172.17.0.131
Host is up (-0.084s latency).
PORT STATE SERVICE
27017/tcp closed unknown
MAC Address: 02:42:AC:11:00:83 (Unknown)
Nmap scan report for XXX (172.17.0.132)
Host is up (0.000055s latency).
PORT STATE SERVICE
27017/tcp closed unknown
Nmap done: 256 IP addresses (5 hosts up) scanned in 4.10 seconds

In the output above you can see that my test environment was running five containers (including the one running the command). Of the five, one of them was open on port 27017. The IP address with the open port is the IP address of the container running your MongoDB instance. This is interesting, and might come as a surprise to some. I’ll talk about this shortly. First let’s finish the experiment.

Access the database

Using the mongo CLI you installed earlier, you should be able to access the MongoDB instance running in the other container. Before you get carried away, this is not a Docker vulnerability or a Linux Kernel vulnerability. This is what happens when you run servers that are open to the world.

When you run the following command use the IP address that you discovered in the nmap output.

root@XXX:/# mongo --host 172.17.0.96 # replace the IP address
MongoDB shell version: 2.6.6
connecting to: 172.17.0.96:27017/test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
>_

There it is. You’re connected to the MongoDB instance in a different container. Getting access to the database was easy. Surprising? It shouldn’t be. You’re running a vanilla mongod instance without an authentication requirement. Don’t think that firewalls or network topology will protect your poorly secured services.

Docker Abstractions and What They Communicate

Earlier in my adventures with Docker, for some reason I was under the impression that network jails were built up around the containers. This is true. There are firewalls that separate the interfaces. However, I didn’t initially expect these to even be routable without specific linking. I think this was reinforced by Docker container links.

Like others, when I was learning how to access other containers the first thing I found was container linking. This is the process by which you can specify at container creation time that you want access to another existing container. When you create links Docker injects environment variables that hold IP and port information into the newly created container. Additionally, the other container’s IP address is added to the /etc/hosts file for the name of the container.

When I learned about links I stopped looking for other tools… at least for a while. Links are great for convenience, but they really only provide a convenience. They reduce the obscurity a bit by specifically telling your new container the IP and port information of some other specific container. That is it. The bridge network isn’t anything fancy.

You can access the external interfaces of other containers just like they were other machines on a network. This should be a good thing. We know how to do that already. We have a ton of tools for doing so. But I fear that over eager adopters made the same early assumptions that I did. Don’t panic if you did. Just fix the obvious security issues that you may have introduced when you assumed that Docker really would magically make all the security happen.

Docker helps you build strong containers. It cannot protect you from making application layer mistakes. Remember that as you move forward and build bigger and better things.

P.S. Please remember to stop and remove the containers that you created in this experiment.

# docker kill some-mongo
# docker rm -v some-mongo

Lock illustration provided by Guillermo Mont. Hire him if you need custom illustration work.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.