diff --git a/scripts/setup/machine-setup/ufw-docker-rules.conf b/scripts/setup/machine-setup/ufw-docker-rules.conf index a82ab97f..98f5fb44 100644 --- a/scripts/setup/machine-setup/ufw-docker-rules.conf +++ b/scripts/setup/machine-setup/ufw-docker-rules.conf @@ -2,8 +2,18 @@ # Append these to /etc/ufw/after.rules (after the existing COMMIT) # Blocks all external access to Docker-published ports except: # - Trusted IPs (add your own) -# - Explicitly allowed public ports (80, 443) +# - Explicitly allowed public ports (80, 443, 22) # - Docker internal and loopback traffic +# +# This chain is the ONLY thing gating Docker-published ports from the internet: +# Docker writes its own DNAT/FORWARD rules and bypasses ufw entirely, so a +# `ufw allow ` has no effect on a published container port. If a container +# port must be reachable publicly, it needs a RETURN line below. +# +# Failure mode to recognise: the port appears open locally and `docker ps` shows +# it published, but external clients hang at TCP connect with no refusal. Local +# tests pass because they arrive via `lo` and match the loopback RETURN above, +# never reaching the DROP. Test from another machine, not from the host itself. *filter :DOCKER-USER - [0:0] @@ -23,6 +33,9 @@ # Allow public ports -A DOCKER-USER -i eth0 -p tcp --dport 80 -j RETURN -A DOCKER-USER -i eth0 -p tcp --dport 443 -j RETURN +# gitea SSH (NPM stream -> gitea:22). Host sshd does not use :22 publicly on a +# box that serves gitea — it moves to :2022 plus the tailnet IP, see ssh.socket. +-A DOCKER-USER -i eth0 -p tcp --dport 22 -j RETURN # Drop everything else from external -A DOCKER-USER -i eth0 -j DROP