Syncthing removal (was already decided, now fully removed): - roles/base/tasks/firewall.yml: remove 3 UFW rules (ports 22000/21027) - inventory/group_vars/all/main.yml: remove domain_sync, domain_mon, syncthing_basic_auth_htpasswd - roles/services/templates/env.j2: remove DOMAIN_SYNC - roles/services/templates/authelia/configuration.yml.j2: remove Syncthing 2FA rule - roles/services/tasks/directories.yml: remove syncthing/config and syncthing/data dirs - roles/services/defaults/main.yml: remove syncthing_image - roles/services/tasks/main.yml: remove syncthing image pull Security hardening: - inventory/group_vars/all/main.yml: move cloudflare_zone_id to vault - inventory/group_vars/all/vault.yml: add vault_cloudflare_zone_id .gitignore improvements: - add *.env, acme.json, *.log, editor dirs, venv, temp files Documentation (new): - docs/STATUS.md: all services, servers, known issues - docs/BACKLOG.md: prioritized task list, done/todo - docs/DECISIONS.md: architecture decisions and rationale - CLAUDE.md: rewritten with read-first docs, rules, full arch reference Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
152 lines
3.4 KiB
YAML
152 lines
3.4 KiB
YAML
---
|
|
- 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: 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 = ^<HOST> - \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
|