At work I had to block some very annoying spammers from POSTing to the contact form on the website. I ssh'ed into the Ubuntu server and blocked their IP addresses with ufw:
$ ufw insert 1 deny from 203.17.245.205
Unfortunately, this did not work. The spammers were still able to access the nginx webserver in the Docker container:
203.17.245.205 - - [17/Apr/2025:11:58:28 +0200] "POST /contact HTTP/1.1" 200 24111 "https://example.org/contact" "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 OPR/89.0.4447.51"
It turns out that Docker heavily uses iptables for its container networking, and that the rules in the default INPUT chain of the filter table that are generated by ufw are too late in the game - earlier rules already route the packets into the containers.
The iptables section in the Docker documentation tells us that rules need to be put into the DOCKER-USER chain:
$ iptables -I DOCKER 1 -j DROP -s 203.17.245.205
In the end, the chain looked like this:
$ iptables -L DOCKER-USER --numeric --line-numbers Chain DOCKER-USER (1 references) num target prot opt source destination 1 DROP 0 -- 103.106.241.170 0.0.0.0/0 2 DROP 0 -- 113.176.64.56 0.0.0.0/0 3 DROP 0 -- 176.102.128.140 0.0.0.0/0 4 DROP 0 -- 193.163.116.88 0.0.0.0/0 5 DROP 0 -- 103.255.9.53 0.0.0.0/0 6 DROP 0 -- 116.212.106.162 0.0.0.0/0 7 DROP 0 -- 5.254.26.39 0.0.0.0/0 8 DROP 0 -- 5.254.26.37 0.0.0.0/0 9 DROP 0 -- 203.17.245.205 0.0.0.0/0 10 DROP 0 -- 172.111.204.6 0.0.0.0/0 11 DROP 0 -- 94.43.48.194 0.0.0.0/0 12 DROP 0 -- 188.169.38.71 0.0.0.0/0 13 DROP 0 -- 181.204.9.178 0.0.0.0/0 14 DROP 0 -- 103.246.84.78 0.0.0.0/0 15 DROP 0 -- 122.175.12.83 0.0.0.0/0 16 DROP 0 -- 92.255.57.64 0.0.0.0/0 17 DROP 0 -- 185.208.8.200 0.0.0.0/0 18 RETURN 0 -- 0.0.0.0/0 0.0.0.0/0