Drop packets with PREROUTING in iptables
I had a similar problem which was the need to "harden" network traffic even if somebody would deploy a container that was binding the application to any address: 0.0.0.0:port
.
Docker provides a DOCKER-USER
filter chain but it looks like all the magic happens in the DOCKER
nat chain that is referenced in PREROUTING
.
So no way around this nat
happens before filtering and I don't want to touch too much at the docker rules.
I don't like the idea of having to change the packet once again so I came up with a scheme that returns everything by default and jumps to another chain in the PREROUTING
before DOCKER
gets called.
I then selectively jump back to DOCKER
when I consider the traffic good.
Here's the code:
iptables -t nat -N DOCKER-BLOCK
iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -j RETURN
iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -j DOCKER-BLOCK
That's it!
The packet will jump to the DOCKER-BLOCK
chain, and if that chain is empty, it'll go out the chain and continue on PREROUTING
jumping to RETURN
and it'll be blocked.
When you enable a port:
iptables -t nat -I DOCKER-BLOCK -p tcp -m tcp --dport 1234 -j DOCKER
It'll make the packet jump back to the DOCKER
chain where it is managed by docker
. Docker should handle the packet and the RETURN
from PREROUTING
should never be reached.
The nice way about it is that you never have to touch to the PREROUTING
table anymore, if you want to flush, flush directly DOCKER-BLOCK
.
Comments
-
Drakes over 1 year
The filter table is best place to drop packets, agreed.
But, out of the box, Docker bypasses INPUT filter rules with PREROUTING to its own FORWARD rules making Docker containers world-accessible. Inserting DOCKER-named filter INPUT/FORWARD rules fails because when Docker is restarted they are deleted then inserted (not appended).
My best attempt is to insert another PREROUTING chain before Docker's and send unwanted packets from eth0 (WAN) to a black hole - 0.0.0.1 - because you cannot DROP/REJECT in a nat table anymore.
# Route anything but TCP 80,443 and ICMP to an IPv4 black hole iptables -t nat -N BLACKHOLE iptables -t nat -A BLACKHOLE ! -i eth0 -j RETURN iptables -t nat -A BLACKHOLE -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN iptables -t nat -A BLACKHOLE -p tcp --dport 80 -j RETURN iptables -t nat -A BLACKHOLE -p tcp --dport 443 -j RETURN iptables -t nat -A BLACKHOLE -p icmp -j RETURN iptables -t nat -A BLACKHOLE -p all -j DNAT --to 0.0.0.1 iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -j BLACKHOLE
Here are what the NAT chains looks like with Docker and one container running:
This seems to work well, though, is there a way to explicitly reject packets before reaching the other pre-routing rule?
(Alpine Linux 3.6.2, Docker v17.05.0-ce)
-
Ram almost 6 yearsCan you please explain on "That's all, by default everything coming from egress will end up in the filter table where I do have a catchall that drops everything."?
-
tehmoon over 5 years@Ram Editing my answer, if you create the
DOCKER-BLOCK
chain and it is empty, all the packets will traverse that chain without being block. When they go back to thePREROUTING
chain, they will jump toRETURN
blocking them by default.