Or why you should never use default logins for internet facing services.

So you found a new application to run over docker, maybe an application with a database that you run on your homeserver or even worse… a cloud VM. you run docker run -p 3000:3000 some-shiny-new-app. It’s a container so it should be safe right? Well before we begin let’s talk about

IPTables

Let’s say you got a shiny new Ubuntu distro setup. You followed some tutorial and used ufw to get a sane default firewall setup.

Let’s say you wanted to allow everything going out, deny everything coming in. You did something like

sudo ufw default deny incoming
sudo ufw default allow outgoing

Let’s see what ufw says

$ ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

and iptables

$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ufw-before-logging-input  all  --  anywhere             anywhere
ufw-before-input  all  --  anywhere             anywhere
ufw-after-input  all  --  anywhere             anywhere
ufw-after-logging-input  all  --  anywhere             anywhere
ufw-reject-input  all  --  anywhere             anywhere
ufw-track-input  all  --  anywhere             anywhere
<snip>

Looks good.

Now say you installed docker following their tutorials

Check your iptables again…

If you know what you are looking for, you’ll realize that docker installed its own chains before ufw! This is well documented

https://docs.docker.com/network/packet-filtering-firewalls/

Docker installs two custom iptables chains named DOCKER-USER and DOCKER, and it ensures that incoming packets are always checked by these two chains first. These chains are part of the FORWARD chain.

This means that if you are not behind a router that blocks incoming traffic by default, your docker containers are completely visible to the open internet!

This problem is even worse if you have a dedicated ipv4 address. Bots nowadays can scan the entire ipv4 range in a matter of hours. It really doesn’t take much to go through each ip, each port and try common exploits. I imagine it is something like:

for ip in 0.0.0.0/0:
	for port in 20..65535:
		try_ssh()
		try_ftp()
		try_php_my_admin()
		try_django_admin()
		try_log4j()
		try_postgres()
		try_msql()
		...

If you are running a postgres container with default port 5432 and some default password? Consider yourself hacked. Seriously, this is far more common than you think. Try spinning up a cloud vm without a firewall and run a Postgres database with some dummy data in it and watch yourself get hacked.

What Can You Do?

  • Bind your docker containers to 127.0.0.1 or if you use Tailscale, you can just bind them to the Tailscale IP!
  • Use ufw-docker. This is designed to fix the aforementioned issues.
  • Do not expose anything outside your docker networks and only use docker networks to communicate between containers.
  • Run your docker apps over UNIX sockets.
  • Never ever ever ever ever leave public facing services with default logins!