selfhostedops-stack/hardening/ufw-after.rules
Maximilian Grimm bed41cfaf3 Episode 10: hardening - firewall rules Docker cannot bypass, Caddy access log and rate limiting, fail2ban in the right chain
- hardening/: ufw after.rules block for DOCKER-USER (ctorigdstport, not dport),
  fail2ban jail.local with sshd (systemd backend) and caddy-probes (chain DOCKER-USER),
  filter for the JSON access log, sshd hardening.conf, README with the order and
  the two traps measured in the episode
- caddy/Dockerfile: xcaddy build with caddy-ratelimit, compose builds the image
- Caddyfile: common snippet (JSON access log, Server header stripped), vault /admin 404,
  rate_limit zones on the vault token endpoint and the git login form
- HARDENING.md: firewall and fail2ban sections rewritten around what the port scan saw

Claude-Session: https://claude.ai/code/session_017iP581CYtPfxdZYPPAyWZA
2026-09-11 10:20:56 +02:00

29 lines
1.5 KiB
Text

# episode 10 - paste this block at the END of /etc/ufw/after.rules
# (and the same block in /etc/ufw/after6.rules for IPv6).
#
# Why: Docker publishes ports by DNAT in the FORWARD path, not INPUT. ufw only
# filters INPUT, so `ufw deny incoming` does NOT close a published container
# port - it stays wide open. DOCKER-USER is the one chain Docker runs first in
# FORWARD and never flushes on its own, so the fix goes here.
#
# The catch inside the catch: DNAT rewrites the destination port to the
# container's port BEFORE this chain sees the packet. Matching --dport would
# match the wrong (translated) port. So match the ORIGINAL published port with
# conntrack --ctorigdstport instead.
#
# Only eth0 (the public NIC) is filtered. wt0 (the NetBird mesh) and the docker
# bridges are left alone on purpose - a rule without `-i eth0` here breaks the
# mesh route to n8n (episode 8) and traffic between containers.
#
# Replace eth0 with your public interface, and the port list with what you
# actually publish.
*filter
:DOCKER-USER - [0:0]
-A DOCKER-USER -i eth0 -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 443 -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 2222 -j RETURN
-A DOCKER-USER -i eth0 -p udp -m conntrack --ctorigdstport 443 -j RETURN
-A DOCKER-USER -i eth0 -p udp -m conntrack --ctorigdstport 3478 -j RETURN
-A DOCKER-USER -i eth0 -j DROP
COMMIT