I guess most of us have experimented with Docker at some point, maybe just on our local machine or on a cloud-hosted virtual machine. There are some things that you should keep in the back of your head when using it in the cloud.

Did you know?

One thing I think a lot of people don’t realize by using the -p argument (when using CLI) or the ports (when using Docker Compose) is that this will bypass your host firewall.

Let me illustrate with an example.

On my host I have Nginx installed and the host firewall activated. sudo ufw status verbose returns the following:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                           Action      From
--                           ------      ----
80,443/tcp (Nginx Full)      ALLOW IN    Anywhere
80,443/tcp (Nginx Full (v6)) ALLOW IN    Anywhere (v6)

As you can see, the host firewall is active and blocking all traffic except on ports related to Nginx.

Now I deploy and start the following container

docker run -it --rm -d -p 8080:80 --name web nginx

Let us take a look at what docker ps returns:

CONTAINER ID   IMAGE  ... PORTS                  NAMES
315fb926d486   nginx  ... 0.0.0.0:8080->80/tcp   web

We see our Nginx container listed here, as well as the port 0.0.0.0:8080. But what does this mean? Using the bind address 0.0.0.0 means that the container will listen on port 8080 on all network interfaces.

Taking a look at the host firewall again, we see that there have been no changes.

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                           Action      From
--                           ------      ----
80,443/tcp (Nginx Full)      ALLOW IN    Anywhere
80,443/tcp (Nginx Full (v6)) ALLOW IN    Anywhere (v6)

ufw provides an abstraction layer but uses iptables in the background. Docker modifies iptables directly when adding port and network configuration. If we take a look in the Docker documentation we find the following statements (Please read the documentation for full context):

This means that if you expose a port through Docker, this port gets exposed no matter what rules your firewall has configured.

By default, all external source IPs are allowed to connect to the Docker host. To allow only a specific IP or network to access the containers, insert a negated rule at the top of the DOCKER-USER filter chain.

It is not possible to completely prevent Docker from creating iptables rules….

So even if we haven’t specified any rules to allow our container to access the internet the underlying rules created by Docker will automatically publish access to your container. After deploying the container above, try going to <your-public-virtal-machine-ip>:8080 and you should see the following:

Nginx Welcome

Fixing the issue

It is easy to not think about this when deploying your containers, but it can pose a huge security risk to your data, host, and application.

There are several ways we can fix this. The simple fix is to set iptables to false in /etc/docker/daemon.json. This should make Docker honor the ufw rules and not expose container ports.

I want to look back to the third excerpt from the documentation I posted earlier:

It is not possible to completely prevent Docker from creating iptables rules….

As I do fully trust the fix over, I advise everyone that uses Docker to host their application to use an external firewall to maintain better control on what traffic is allowed from and to a host.

Firewalls

I will not go into details about how to deploy or configure your firewall, because that depends from provider to provider. Deploying this external firewall will have both pros and cons. Just to mention some:

  • Traffic on disallowed ports is rejected before it even reaches your host (depending on the provider)
  • You maintain better control over what traffic is allowed in and out

There are also some cons to it:

  • One more step to perform when needing to deploy new services or change ports for existing applications
  • Can increase cost and latency

Some other tips for securing your application

  • Never use the default port for anything
  • Perform regular security audits of your infrastructure
  • Always secure your applications and side services with authentication, even if they are just running on the local network.

Thank you for the read!