Preferences

If you can, do not expose ports like this 8080:8080, but do this "192.168.0.1:8080:8080" so its bound to a private IP. Then use any old method expose only what you want to the world.

In my own use I have 10.0.10.11 on the vm that I host docker stuff. It doesn't even have its own public IP meaning I could actually expose to 0.0.0.0 if I wanted to but things might change in the future so it's a precaution. That IP is only accessible via wireguard and by the other machines that share the same subnet so reverse proxying with caddy on a public IP is super easy.


It's really a trap. I'm surprised they never changed the default to 127.0.0.1 instead of 0.0.0.0. So you would need to explicitly specify it, if you want to bind to all interfaces.
The reason is convenience. There would be a lot more friction if they didn't do it like this for everything other than local development.

Docker also has more traps and not quite as obvious as this. For example, it can change the private IP block its using without telling you. I got hit by this once due to a clash with a private block I was using for some other purpose. There's a way to fix it in the config but it won't affect already created containers.

By the way. While we're here. A public service announcement. You probably do NOT need the userland-proxy and can disable it.

/etc/docker/daemon.json

{ "userland-proxy": false }

Is there a guide that lists some common options / gotchas in Docker like this?

Some quick searching yields generic advice about keeping everything updated or running in rootless mode.

Not that I'm aware of. Sorry. Here's one my daemon.json files though. It tames the log file size and sets its format. And fixes the IP block so it won't change like I mentioned above.

  {
    "log-driver": "json-file",
    "log-opts": {
      "labels": "production_status",
      "tag": "{{.ImageName}}|{{.Name}}|{{.ImageFullID}}|{{.FullID}}",
      "env": "os,customer",
      "max-size": "10m"
    },
    "bip": "172.17.1.1/24",
    "default-address-pools": [
      {"base": "172.17.0.0/16", "size": 24}
    ]
  }
Yup the regular "8080:8080" bind resulted in a ransom note in my database on day 1. Bound it to localhost only now.
I had the same experience (postgres/postgres on default port). It took me a few days to find out, because the affected database was periodically re-built from another source. I just noticed that for some periods the queries failed until the next rebuild.
Yea plenty of bots scouting the internet for these kinda vulnerabilities, good learning experience, wont happen again :D
one thing I always forget about, is that you have a whole network of 127.0.0.0/8 , not just one IP.

So you can create multiple addresses with multiple separate "domains" mapped statically in /etc/hosts, and allow multiple apps to listen on "the same" port without conflicts.

I never thought of using localhost like that, I'm surprised that works actually. Typically, if you want a private /8 you would use 10.0.0.0/8 but the standard 192.168.0.0/16 gives you a lot of address space ( 255^2 - 2 IPs (iirc) ) too.

..actually this is very weird. Are you saying you can bind to 127.0.0.2:80 without adding a virtual IP to the NIC? So the concept of "localhost" is really an entire class A network? That sounds like a network stack bug to me heh.

edit: yeah my route table on osx confirms it. very strange (at least to me)

That was deliberate. Works on Linux and Windows as well. I think this is the current RFC: https://datatracker.ietf.org/doc/html/rfc5735

You can do:

python3 -m http.server -b 127.0.0.1 8080

python3 -m http.server -b 127.0.0.2 8080

python3 -m http.server -b 127.0.0.3 8080

and all will be available.

Private network ranges don't really have the same purpose, they can be routed, you have to always consider conflicts and so on. But here with 127/8 you are in your own world and you don't worry about anything. You can also do tests where you need to expose more than 65k ports :)

You have to also remember these are things established likely before even DNS was a thing, IP space was considered so big that anyone could have a huge chunk of it, and it was mostly managed manually.

I didn't really know the mechanism of how this worked but if you check your resolv file you might find that the nameserver IP for your localhost is 127.0.0.53 . It is so in recent Linux distros. (Probably a systemd thing)

This item has no comments currently.