let’s dev GmbH & Co. KG - The brand for groundbreaking custom software

Blog

Top!

let’s dev | Scroll to top
let’s dev | Scroll to next content item

Hello

let’s dev | Scroll to previous content item
let’s dev Blog |
by
01. Januar 1970

Who hasn't heard of it? Docker and its popular containers are supposed to accelerate and simplify the deployment of applications. But if you're not careful, you can unknowingly open yourself up to hackers. We explain how this can happen and how to protect against it with the following example.

We need an application launched in a Docker container. The application runs inside the container on port 8080 and should be reachable on our Docker host at 9090. Why the port 9090 for the Docker host? For example, we might have other containers running that cannot all share port 8080 of the host and need to be reachable separately.

This is pretty much the first thing you come into contact with when you first start working with Docker:

docker run -p 9090:8080

And the application runs...

docker ps
eb77b9f37140 … 0.0.0.0:9090->8080/tcp

Huch? Now it says something about 0.0.0.0:9090, but we didn't specify that. But that doesn't matter - it's standard, isn't it?

Huch? Now it says something about 0.0.0.0:9090, but we didn't have that in mind. Well, it depends on where we are, because 0.0.0.0 means the port is open for all addresses. To better understand what the problem is, we need to look at the network layer. But that doesn't matter - it's standard, isn't it?

Network Interfaces

Let's first illustrate the setup on a non-Docker host system. That is, Docker is running in a virtual machine (VM) on the host.

First, we have our container - all this sounds quite simple on its own.

Docker Container Step 1

The network interface eth0 as well as lo (loopback) are open for port 8080. eth0 offers this interface to the outside.

Outwardly means first of all for the docker-engine.

The docker engine manages its containers for us and provides the interface for communication - docker0. Our container is connected via another virtual interface vethXY through docker0.

Thus, requests travel via docker0, via vethXY finally to the eth0 interface of our container.

Docker Container Step 2

To direct our request to docker0 there is the docker-proxy. This listens for the desired ports and forwards accordingly.

Docker Container Step 3
ps -ef | grep docker-proxy
… /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 9090 -container-ip 172.17.0.2 -container-port 8080

Here we now also see where our magic 0.0.0.0 comes from.
To illustrate the problem again, we complete our diagram with our host machine.

Docker Container Step 4

The 0.0.0.0 hangs on the interface of the VM and is not further released to the outside. Thus, we can access the application under the port from our browser, but a third party from the outside cannot. The port 9090 is not shared on our host machine directly but in the VM.

Now we move our test application to a production server from the cloud, a Linux-based, Docker-enabled host.

Docker Container Step 5

The VM is gone because our host can run Docker directly. Now the docker proxies listen directly on our host interface and expose ports that we may not want visible to the outside.

We can check this by means of nmap

nmap my.server -p 9090
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-28 14:40 CEST
Nmap scan report for my.server
Host is up (0.056s latency).
PORT     STATE …
9090/tcp open  …

So our port 9090 is revealed to the whole world, consciously or unconsciously.
We want to prevent this if necessary and thus close a security gap. Here we come back to the mentioned lo (loopback) interface.

Save port

The whole thing is also not particularly difficult. We can simply tell our container to listen to the lo interface instead of 0.0.0.0.

docker run -p 127.0.0.1:9090:8080

With this restriction, our container is still accessible within our system on port 9090, but blocked to the outside.

Let's check that again.

ps -ef | grep docker-proxy
… /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 9090 -container-ip 172.17.0.2 -container-port 8080
nmap my.server -p 9090
Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-28 15:04 CEST
Nmap scan report for my.server
Host is up (0.061s latency).
PORT     STATE …
9090/tcp closed  …

This already looks much safer.

Takeaway

The entry into the Docker world is simple and allows many gimmicks to quickly provide services. To keep it simple, a lot happens under the hood, which can lead to nasty surprises.

If you leave a secured test environment, you have to deal with the issue of security anyway. Opening the ports to the outside is certainly not always intended and therefore a nasty pitfall if you use Docker without checking it as most tutorials do.


Fatal error: Uncaught Error: Call to a member function getAll() on null in /homepages/12/d366218748/htdocs/websites/letsdev3.0/en/modules/module_blog_article_slider_all.php:2 Stack trace: #0 /homepages/12/d366218748/htdocs/websites/letsdev3.0/en/blog/possible-security-gap-with-port-releases-in-docker-container.php(27): include() #1 {main} thrown in /homepages/12/d366218748/htdocs/websites/letsdev3.0/en/modules/module_blog_article_slider_all.php on line 2