https://www.baeldung.com/linux/docker-container-published-po...
https://www.baeldung.com/linux/docker-container-published-po...
Everything but 80/443 blocked by the firewall. "Surely it is safe to run my app server on port 8000 because no one can access that port externally."
Docker stepped in to help by making sure people could access that port.
Thanks, my ass, and give back my time trying to diagnose [a container with no diagnostic tools, because it's a lightweight container] why it can't connect to someservice.on.my.lan.
I've recently had to debug a bug with a container (probably not Docker itself) unable to reach the internet. It turns out the machine is behind a NAT64 and has IPv6 addresses only, yet something in the container helpfully reconfigures the resolver to be 8.8.8.8 which of course is unreachable.
> 464XLAT allows private IPv4 clients to connect to an IPv4 host over an IPv6 network. It only supports client-server, not peer-to-peer communication or inbound connections.
...A brief description for anyone else who didn't know.
Docker also has special handling for systemd-resolved: if resolv.conf points at 127.0.0.53, it will use the managed resolv.conf located in /run.
> So you'd need to set the DNS to the gateway IP
This is the least friction way. Run your own resolver there to resolve names for containers, forward to the host's loopback for everything else.
> or dynamically by resolving `host.docker.internal`
Resolving. Name. Of the DNS server. Right?
This would require adding a script to the image itself, for each image. No go.
Surprise enused.
- ports: 127.0.0.1:8000:8000
(Not disagreeing with your point -- yours is a great/quick workaround -- it just seems that many people don't know about rootless Docker)
More seriously, I believe nftables has the capacity to help here: now there can be several chains which all attach to the same hook. So docker can put rules into the FORWARD base chain of the filter table (belonging to iptables, mine is empty because I don't use iptables):
# nft list chain ip filter FORWARD
table ip filter {
chain FORWARD {
type filter hook forward priority filter; policy accept;
}
}
... but then the filter_FORWARD base chain of the firewalld table (belonging to firewalld) _also_ gets to process packets. Note the numerically higher filter priority, which means that this chain runs later: # nft list chain inet firewalld filter_FORWARD
table inet firewalld {
chain filter_FORWARD {
type filter hook forward priority filter + 10; policy accept;
ct state { established, related } accept
ct status dnat accept
iifname "lo" accept
ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } log prefix "RFC3964_IPv4_REJECT: " reject with icmpv6 type addr-unreachable
jump filter_FORWARD_POLICIES_pre
jump filter_FORWARD_IN_ZONES
jump filter_FORWARD_OUT_ZONES
jump filter_FORWARD_POLICIES_post
ct state { invalid } meta pkttype host log prefix "STATE_INVALID_DROP: "
ct state { invalid } drop
meta pkttype host log prefix "FINAL_REJECT: "
reject with icmpx type admin-prohibited
}
}
Now if only tools would learn to stay in their lane, cease assuming they are the only tool managing the nftables state, we'd have ufw managing its own table, docker managing its own table, firewalld managing its own...It's annoying, but the fault lies squarely with the lack of reporting of other chains in UFW.
Clearly people want firewall rules that take priority over everything else.
Different lists are a powerful nftables feature that is used in many system services and applications for things like routing and specialized forwarding. You can't just disable that without breaking tons of software.
Instead, tools like UFW should make it clear which ports are or aren't open and what firewall rules actually apply to the current system. They exist to save users from nftables and eBPF programming, and they should work as advertised.
Ufw advertises itself with "Ufw stands for Uncomplicated Firewall, and is program for managing a netfilter firewall" not "Ufw stands for Uncomplicated Firewall, which lets you add some firewall rules but doesn't manage all of them so you need to be extra careful if you use it."
This could be solved by more tooling.
UFW, on the other hand, pretends to make firewalls simple and easy to use when they're really not. I'm not expecting UFW to start managing Docker's/K8s'/libvirtd's firewall rules, I'm just expecting them to add a section of "ports opened by other applications" that show the open ports that UFW can't control.
I'm gradually getting rid of Docker (NixOS containers powered by systemd-nspawn are more convenient for me at the moment), so I don't have any fancy networking for it - but I still run it. I have a bunch of static nftables rules for the bridge (`iifname "docker0" ct state established,related counter accept` etc.) so the containers can access what they need. And then Docker uses docker-proxy for the exposed ports (paired with appropriate firewall rules when it's not on `lo`, where I have an umbrella `iif "lo" accept`) and it seem to works just fine.
By enabling port forwarding and exposing, you're essentially asking docker to configure the host for it. It's likely a surprising behaviour, but seems like ufw has a bug if you have multiple chains and groups instead of just the default one.
... yes, it adds a forwarding rule. Which skips over the rest of my firewall rules. One might even say that it bypasses them.
> By enabling port forwarding and exposing, you're essentially asking docker to configure the host for it.
No, I'm asking docker to listen on that port and pass it through to the container. If I tell nginx to listen on port 80, would you expect it to take that as a "do anything you can to make sure you get port 80, including rewriting the firewall tables to ignore the rule that blocks port 80"?
That said it is a mistake to consider Docker in the same class as nginx. Docker is a system for telling the Linux kernel how to set up the environments of processes. Doing whatever it can at the absolute lowest level of abstraction the kernel offers is kind of its entire gig.
> I feel Docker should have tried to play nicer with the firewall.
The problem is on the UFW side not setting up properly when other chains are present. UFW is just a front-end for iptables, and docker integrates with iptables. Can the situation be made better? Likely on the UFW side. https://docs.docker.com/network/packet-filtering-firewalls/#...
The docker behaviour is documented...
UFW seems buggy in that it only operates on set of prefixed groups instead of looking at all the groups in iptables. Now, looking at the UFW code: https://git.launchpad.net/ufw/tree/src/backend_iptables.py?h... it seems to setup it's own chains and ignores everything else. It even filters out all other chains unless they're part of UFW.
Btw, ouch at that 1400 line python file..
Seems like introducing unnecessary overhead to me, but I'm also sure there's a reason it exists, so...
... which you explicitly asked it to do by using the -p option.
Just because it was meant to be useful and harmless doesn't mean they didn't actually bypass the firewall rules by adding their own rules.
Don't blame ufw incompetence on iptables.
There is separate issue with bridging aptly described here
https://wiki.libvirt.org/Net.bridge.bridge-nf-call_and_sysct...
but IIRC docker doesn't work in that config by default.
IIRC Centos/RHEL comes with bridging iptables pre-broken because they're bad at making firewall rules there.
Here's the only fix I was able to find, which doesn't seem well documented. Run
kubectl -n kube-system edit configmap kube-proxy
and edit in this: nodePortAddresses:
- 192.168.0.0/16
...where the CIDR block should correspond to the local network on your machine.https://docs.docker.com/network/packet-filtering-firewalls/
It says explicitly that docker and ufw should be considered incompatible. Docker also has a configuration key to prevent it from modifying iptables.
Imagine a civil engineer telling someone they don't have time to read the documentation on the bolts their using to hold up a bridge.
Then imagine getting upset because the bolts don't work as you thought they did. You go do additional research and find the information right in that book you ignored! Too late, 100,000 bolts have already been used so now we gotta deal with workarounds and other additonal problems. Maybe should have read the book.
I agree. The problem is that it's too much upfront information.
> Then imagine getting upset because the bolts don't work as you thought they did.
I'm more imagining that the engineer glances at the documentation and rejects the complicated bolts unless there is a really really good reason to prefer them.
Programmers, on most projects, are backed into a corner of horrific complexity. The proper comparison to engineering is refusing to do the project entirely. An engineer wouldn't be reading all these details either. So you shouldn't be using that as your factor to compare professions. It's not just "Would they read everything? Yes/No" when there's orders of magnitude difference in the amount to read.
And your average webpage isn't so important you have to refuse on ethical grounds.