I use AlmaLinux 9, I understand that there is new backend service nftables which can be managed by iptables-nft command, so I set some rules and my rule set looks like:
# Warning: table ip nat is managed by iptables-nft, do not touch!
table ip nat {
chain DOCKER {
iifname "docker0" counter packets 0 bytes 0 return
iifname != "docker0" tcp dport 8080 counter packets 3 bytes 180 dnat to 172.17.0.2:80
iifname != "docker0" tcp dport 9001 counter packets 4 bytes 240 dnat to 172.17.0.3:9001
}
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 172.17.0.0/16 oifname != "docker0" counter packets 0 bytes 0 masquerade
ip saddr 172.17.0.0/16 oifname != "docker0" counter packets 0 bytes 0 masquerade
ip saddr 172.17.0.0/16 oifname != "docker0" counter packets 0 bytes 0 masquerade
ip saddr 172.17.0.2 ip daddr 172.17.0.2 tcp dport 9001 counter packets 0 bytes 0 masquerade
ip saddr 172.17.0.2 ip daddr 172.17.0.2 tcp dport 9001 counter packets 0 bytes 0 masquerade
ip saddr 172.17.0.2 ip daddr 172.17.0.2 tcp dport 80 counter packets 0 bytes 0 masquerade
ip saddr 172.17.0.3 ip daddr 172.17.0.3 tcp dport 9001 counter packets 0 bytes 0 masquerade
}
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
fib daddr type local counter packets 236 bytes 12653 jump DOCKER
fib daddr type local counter packets 39 bytes 1732 jump DOCKER
fib daddr type local counter packets 39 bytes 1732 jump DOCKER
fib daddr type local counter packets 23 bytes 1056 jump DOCKER
fib daddr type local counter packets 23 bytes 1056 jump DOCKER
}
chain OUTPUT {
type nat hook output priority dstnat; policy accept;
ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
}
}
# Warning: table ip filter is managed by iptables-nft, do not touch!
table ip filter {
chain FORWARD {
type filter hook forward priority filter; policy drop;
counter packets 178 bytes 55546 jump DOCKER-USER
counter packets 178 bytes 55546 jump DOCKER-ISOLATION-STAGE-1
oifname "docker0" ct state related,established counter packets 84 bytes 11338 accept
oifname "docker0" counter packets 7 bytes 420 jump DOCKER
iifname "docker0" oifname != "docker0" counter packets 87 bytes 43788 accept
iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
}
chain DOCKER-USER {
counter packets 178 bytes 55546 return
}
chain INPUT {
type filter hook input priority filter; policy drop;
ct state established,related counter packets 195 bytes 18089 accept
tcp dport 22 counter packets 2 bytes 120 accept
tcp dport 443 counter packets 0 bytes 0 accept
}
chain DOCKER {
ip daddr 172.17.0.2 iifname != "docker0" oifname "docker0" tcp dport 80 counter packets 3 bytes 180 accept
ip daddr 172.17.0.3 iifname != "docker0" oifname "docker0" tcp dport 9001 counter packets 4 bytes 240 accept
}
chain DOCKER-ISOLATION-STAGE-1 {
iifname "docker0" oifname != "docker0" counter packets 87 bytes 43788 jump DOCKER-ISOLATION-STAGE-2
counter packets 178 bytes 55546 return
}
chain DOCKER-ISOLATION-STAGE-2 {
oifname "docker0" counter packets 0 bytes 0 drop
counter packets 87 bytes 43788 return
}
}
that rule set is saved in /etc/sysconfig/nftables.conf
so it is loaded everytime server restarts, as you can see in chain INPUT, everything should be dropped except port 22 and 443, so I tried to run testing nginx server on port 8080 and I can still access it as well, my firewall is not blocking anything.. why? even when INPUT policy is set to drop.
EDIT2:
so I read that docker use DOCKER-USER chain for user defined rules, so I did:
table ip filter {
chain FORWARD {
type filter hook forward priority filter; policy drop;
counter packets 0 bytes 0 jump DOCKER-USER
counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-1
oifname "docker0" ct state related,established counter packets 0 bytes 0 accept
oifname "docker0" counter packets 0 bytes 0 jump DOCKER
iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
oifname "docker0" ct state established,related counter packets 184 bytes 27570 accept
oifname "docker0" ct state established,related counter packets 85 bytes 28483 accept
}
chain DOCKER-USER {
counter packets 534 bytes 178277 return
tcp dport 8080 drop
tcp dport 9001 drop
}
chain INPUT {
type filter hook input priority filter; policy drop;
ct state established,related counter packets 584 bytes 54071 accept
tcp dport 22 counter packets 8 bytes 460 accept
}
chain DOCKER {
ip daddr 172.17.0.2 iifname != "docker0" oifname "docker0" tcp dport 80 counter packets 0 bytes 0 accept
ip daddr 172.17.0.3 iifname != "docker0" oifname "docker0" tcp dport 9001 counter packets 0 bytes 0 accept
}
chain DOCKER-ISOLATION-STAGE-1 {
iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
counter packets 0 bytes 0 return
}
chain DOCKER-ISOLATION-STAGE-2 {
oifname "docker0" counter packets 0 bytes 0 drop
counter packets 0 bytes 0 return
}
}
and still doesnt work, I can access both ports 8080 and 9001, tried even to restart server.
EDIT 3:
changed my chain to this:
chain DOCKER-USER {
tcp dport 8080 drop
tcp dport 9001 drop
return
}
restarted server and port 8080 is still accesible
Then the traffic is not 'input' from the host OS perspective – it may look like it should be, but you have
nat/prerouting
rules which rewrite packets to instead go to a different IP address (the container's), having the host act as a router in front of the containers, and making it actually forwarded traffic (i.e. routed) which then falls underhook forward
.(In other words: the 'routing' step is where the decision between input vs forward is made, therefore it depends on the outcome of 'prerouting' chains.)