--- - name: Allow SSH community.general.ufw: rule: allow port: "{{ sshd_port }}" proto: tcp comment: "SSH" - name: Allow Forgejo SSH community.general.ufw: rule: allow port: "2222" proto: tcp comment: "Forgejo SSH" - name: Allow HTTP from Docker bridge networks (runner + internal services) community.general.ufw: rule: allow port: "80" proto: tcp src: "{{ item }}" comment: "HTTP from Docker networks" loop: - "172.16.0.0/12" - "10.0.0.0/8" - name: Allow HTTPS from Docker bridge networks (runner + internal services) community.general.ufw: rule: allow port: "443" proto: tcp src: "{{ item }}" comment: "HTTPS from Docker networks" loop: - "172.16.0.0/12" - "10.0.0.0/8" - name: Allow HTTP from Cloudflare IPs only community.general.ufw: rule: allow port: "80" proto: tcp src: "{{ item }}" comment: "HTTP via Cloudflare" loop: - "173.245.48.0/20" - "103.21.244.0/22" - "103.22.200.0/22" - "103.31.4.0/22" - "141.101.64.0/18" - "108.162.192.0/18" - "190.93.240.0/20" - "188.114.96.0/20" - "197.234.240.0/22" - "198.41.128.0/17" - "162.158.0.0/15" - "104.16.0.0/13" - "104.24.0.0/14" - "172.64.0.0/13" - "131.0.72.0/22" - name: Allow HTTPS from Cloudflare IPs only community.general.ufw: rule: allow port: "443" proto: tcp src: "{{ item }}" comment: "HTTPS via Cloudflare" loop: - "173.245.48.0/20" - "103.21.244.0/22" - "103.22.200.0/22" - "103.31.4.0/22" - "141.101.64.0/18" - "108.162.192.0/18" - "190.93.240.0/20" - "188.114.96.0/20" - "197.234.240.0/22" - "198.41.128.0/17" - "162.158.0.0/15" - "104.16.0.0/13" - "104.24.0.0/14" - "172.64.0.0/13" - "131.0.72.0/22" - name: Allow Syncthing sync TCP community.general.ufw: rule: allow port: "22000" proto: tcp comment: "Syncthing sync" - name: Allow Syncthing sync UDP community.general.ufw: rule: allow port: "22000" proto: udp comment: "Syncthing sync" - name: Allow Syncthing discovery UDP community.general.ufw: rule: allow port: "21027" proto: udp comment: "Syncthing discovery" - name: Set UFW default deny incoming community.general.ufw: direction: incoming policy: deny - name: Set UFW default allow outgoing community.general.ufw: direction: outgoing policy: allow - name: Enable UFW community.general.ufw: state: enabled - name: Deploy fail2ban Traefik filter ansible.builtin.copy: dest: /etc/fail2ban/filter.d/traefik-auth.conf content: | [Definition] # Match lines where Traefik returned 401 or 403 failregex = ^ - \S+ \[.*\] ".*" (401|403) .*$ ignoreregex = mode: "0644" notify: Restart fail2ban - name: Ensure fail2ban is configured ansible.builtin.copy: dest: /etc/fail2ban/jail.local content: | [DEFAULT] bantime = 3600 findtime = 600 maxretry = 5 [sshd] enabled = true port = {{ sshd_port }} logpath = %(sshd_log)s backend = %(sshd_backend)s # Forgejo git-over-SSH (port 2222, bypasses Cloudflare — ban is effective) [forgejo-ssh] enabled = true port = 2222 filter = sshd logpath = %(sshd_log)s backend = %(sshd_backend)s maxretry = 3 bantime = 86400 # Traefik HTTP 401/403 — brute force on protected routes [traefik-auth] enabled = true port = http,https filter = traefik-auth logpath = /opt/services/traefik/logs/access.log maxretry = 10 findtime = 300 bantime = 3600 mode: "0644" notify: Restart fail2ban - name: Ensure fail2ban is started and enabled ansible.builtin.systemd: name: fail2ban state: started enabled: true