Exploring Docker Networking – Ports and Forwarding

If you are only kinda-sorta familiar with networking, I like to correlate this with home networking because most of us have faced these issues before.  The problem we are facing is similar to one where you are running a service behind your home internet router and wish to access it from the outside world.  So e.g. you are running a web server on port 80 of a computer on your personal network, maybe 192.168.100.50:80, and when you go off and do cool things for work you want to be able to access that on the road.  Well obviously you can’t hit 192.168.100.50 anywhere but on your home network so that isn’t going to work.  And if you hit your public IP for your router that isn’t going to work either because your router isn’t running a web server.

What you need is port forwarding.  You need to tell your router, “whenever you see a port 80 inbound request, pass that on to 192.168.100.50.”  Just the same here, we need to tell our server OS “when you see an inbound request on some port, forward it to the container on port 80.”  And as luck would have it, we have just a way to do that in Docker bridge networking.

When I run a container there are two flags that will enable port forwarding.  One is the -P (that is capital P) flag.  This flag tells Docker to expose all available ports on the container (in our case here that is just 80) through the outside network on some random, available, high port.  Let’s try it out.

[root@dockernet2 ~]# docker run -d -P nginx
584bfb31e4d1f81d4e0a3180df9bb220ddc392bc86b682237fe85cea8fdff422
[root@dockernet2 ~]# docker container ls
CONTAINER ID IMAGE COMMAND                CREATED        STATUS        PORTS                 NAMES
584bfb31e4d1 nginx "nginx -g 'daemon ..." 10 seconds ago Up 10 seconds 0.0.0.0:32768->80/tcp vibrant_davinci
4d5d54275890 nginx "nginx -g 'daemon ..." 31 minutes ago Up 31 minutes 80/tcp                goofy_stallman

I’ve left the other container running for two reasons.  One is so you can see that in bridge mode, running multiple containers with web servers on port 80 is A-OK.  No problem at all.  The other is so you can see the difference in the Ports output from the docker container ls.  The original container just lists 80/tcp which is open on the container, while this new entry actually shows that the host server is mapping port 80 on the container to every IP on the outside using port 32768 in this case.

Let’s do some nmap scans again:

#scan new container
[root@dockernet2 ~]# nmap 172.17.0.3

Starting Nmap 6.40 ( http://nmap.org ) at 2017-08-03 22:08 EDT
Nmap scan report for 172.17.0.3
Host is up (0.0000060s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
80/tcp open http
MAC Address: 02:42:AC:11:00:03 (Unknown)

#scan public facing host IP
[root@dockernet2 ~]# nmap 10.0.0.206

Starting Nmap 6.40 ( http://nmap.org ) at 2017-08-03 22:09 EDT
Nmap scan report for dockernet2.homelab.bonk (10.0.0.206)
Host is up (0.0000050s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
32768/tcp filtered filenet-tms

Nmap done: 1 IP address (1 host up) scanned in 1.23 seconds

#scan docker0 bridge IP
[root@dockernet2 ~]# nmap 172.17.0.1

Starting Nmap 6.40 ( http://nmap.org ) at 2017-08-03 22:09 EDT
Nmap scan report for 172.17.0.1
Host is up (0.0000050s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
32768/tcp filtered filenet-tms

#netstat
[root@dockernet2 ~]# netstat -natul
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 10.0.0.206:22 10.0.0.10:59964 ESTABLISHED
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
tcp6 0 0 :::32768 :::* LISTEN

As you might expect, port 80 is open on the host, and 32768 is available on the host.  I did want to point out here that port 32768 is listening on ALL the host IPs, including the docker0 bridge.  Again we instructed Docker to forward all available ports in the container on all available interfaces on the host with our -P flag.

At this point I can browse to 10.0.0.206:32768 on my local network and tada I see this:

nginx

Hooray we got a website!

All well and good but you are probably wondering about that high random port.  That isn’t too spiffy, is it?  Luckily we have another method to expose ports with the -p (lowercase p) flag.  This functionality is similar in that it does the port expose, however it requires you to manually specify how you want the ports done in the format of hostport:containerport.  Let’s give this a shot with two new containers.

[root@dockernet2 ~]# docker run -d -p 80:80 nginx
0d54c84a1e9ba57384ac7061af3f977fa7224d2eed3cc37deb522ca77ce69630
[root@dockernet2 ~]# docker run -d -p 10.0.0.206:81:80 nginx
b06e659cea6d3b795b2a9ac5e2d75ce298510a76d536354be6f7e1fbb5145a8e
[root@dockernet2 ~]# docker container list
CONTAINER ID IMAGE COMMAND                CREATED           STATUS           PORTS                 NAMES
b06e659cea6d nginx "nginx -g 'daemon ..." 6 seconds ago     Up 5 seconds     10.0.0.206:81->80/tcp mystifying_archimedes
0d54c84a1e9b nginx "nginx -g 'daemon ..." 34 seconds ago    Up 33 seconds    0.0.0.0:80->80/tcp    xenodochial_thompson
584bfb31e4d1 nginx "nginx -g 'daemon ..." 24 minutes ago    Up 24 minutes    0.0.0.0:32768->80/tcp vibrant_davinci
4d5d54275890 nginx "nginx -g 'daemon ..." About an hour ago Up About an hour 80/tcp                goofy_stallman

With my first command, I tell Docker to create a new container and map all host address port 80’s to the container port 80 (similar to how the -P flag worked except I’m specifying the host port manually).  And with the second command, I tell it to only use the public facing 10.0.0.206 address for the mapping.  If I run my nmap scan again against the host and the docker0 bridge we should see a difference.

[root@dockernet2 ~]# nmap 10.0.0.206

Starting Nmap 6.40 ( http://nmap.org ) at 2017-08-03 22:32 EDT
Nmap scan report for dockernet2.homelab.bonk (10.0.0.206)
Host is up (0.0000050s latency).
Not shown: 996 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp filtered http
81/tcp filtered hosts2-ns
32768/tcp filtered filenet-tms

Nmap done: 1 IP address (1 host up) scanned in 1.23 seconds
[root@dockernet2 ~]# nmap 172.17.0.1

Starting Nmap 6.40 ( http://nmap.org ) at 2017-08-03 22:32 EDT
Nmap scan report for 172.17.0.1
Host is up (0.0000050s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp filtered http
32768/tcp filtered filenet-tms

Sweet success.  The port 81 forwarding is only available through the public facing interface as I specified that in the run command.

Leave a Reply

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 )

Connecting to %s