diff --git a/roles/base/tasks/firewall.yml b/roles/base/tasks/firewall.yml index 5a7ea7f..346ed1e 100644 --- a/roles/base/tasks/firewall.yml +++ b/roles/base/tasks/firewall.yml @@ -118,20 +118,51 @@ community.general.ufw: state: enabled -- name: Ensure fail2ban is configured for SSH +- 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 + bantime = 3600 findtime = 600 maxretry = 5 [sshd] enabled = true - port = {{ sshd_port }} + 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