diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 80b7bfe..55e787a 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -65,6 +65,7 @@ hosts: nuremberg-a tags: [services, mail] roles: + - role: firewall_alpine - role: docker_services # copenhagen-a: Gaming servers diff --git a/ansible/inventory/host_vars/nuremberg-a.yml b/ansible/inventory/host_vars/nuremberg-a.yml index 2061d0d..e06d9db 100644 --- a/ansible/inventory/host_vars/nuremberg-a.yml +++ b/ansible/inventory/host_vars/nuremberg-a.yml @@ -4,4 +4,6 @@ host_description: "Mail server (poste.io)" host_location: "Hetzner Cloud" ansible_python_interpreter: /usr/bin/python3 # NOTE: Alpine host — UFW tasks are Debian-only. -# Firewall rules for mail ports (25,465,587,993,143,80,443) managed separately. +# Firewall: iptables + fail2ban managed by firewall_alpine role. +# Mail ports (25,80,110,143,443,465,587,993,995) exposed via Docker +# port mappings in ansible/services/poste-io/docker-compose.yml. diff --git a/ansible/roles/firewall_alpine/defaults/main.yml b/ansible/roles/firewall_alpine/defaults/main.yml new file mode 100644 index 0000000..b096e28 --- /dev/null +++ b/ansible/roles/firewall_alpine/defaults/main.yml @@ -0,0 +1,9 @@ +--- +# firewall_alpine defaults + +# Enable iptables persistence via OpenRC +firewall_alpine_persist: true + +# fail2ban SSH protection +firewall_alpine_fail2ban_enabled: true +firewall_alpine_fail2ban_maxretry: 10 diff --git a/ansible/roles/firewall_alpine/handlers/main.yml b/ansible/roles/firewall_alpine/handlers/main.yml new file mode 100644 index 0000000..9cb6b17 --- /dev/null +++ b/ansible/roles/firewall_alpine/handlers/main.yml @@ -0,0 +1,9 @@ +--- +- name: Restore iptables + ansible.builtin.command: iptables-restore < /etc/iptables/rules-save + changed_when: true + +- name: Restart fail2ban + ansible.builtin.service: + name: fail2ban + state: restarted diff --git a/ansible/roles/firewall_alpine/tasks/main.yml b/ansible/roles/firewall_alpine/tasks/main.yml new file mode 100644 index 0000000..f8743f8 --- /dev/null +++ b/ansible/roles/firewall_alpine/tasks/main.yml @@ -0,0 +1,52 @@ +--- +# Firewall management for Alpine hosts. +# Manages iptables persistence and fail2ban for SSH protection. +# +# NOTE: Docker manages port-forwarding rules for published container ports +# (e.g. mail ports on nuremberg-a). This role only handles non-Docker rules. + +- name: Install iptables and fail2ban + community.general.apk: + name: + - iptables + - fail2ban + state: present + +# --- iptables persistence --- + +- name: Ensure /etc/iptables directory exists + ansible.builtin.file: + path: /etc/iptables + state: directory + mode: '0700' + +- name: Deploy iptables rules + ansible.builtin.template: + src: rules.v4.j2 + dest: /etc/iptables/rules-save + mode: '0600' + notify: Restore iptables + when: firewall_alpine_persist | bool + +- name: Ensure iptables starts on boot + ansible.builtin.service: + name: iptables + enabled: true + when: firewall_alpine_persist | bool + +# --- fail2ban --- + +- name: Deploy fail2ban Alpine SSH jail + ansible.builtin.template: + src: alpine-ssh.conf.j2 + dest: /etc/fail2ban/jail.d/alpine-ssh.conf + mode: '0644' + notify: Restart fail2ban + when: firewall_alpine_fail2ban_enabled | bool + +- name: Enable fail2ban + ansible.builtin.service: + name: fail2ban + state: started + enabled: true + when: firewall_alpine_fail2ban_enabled | bool diff --git a/ansible/roles/firewall_alpine/templates/alpine-ssh.conf.j2 b/ansible/roles/firewall_alpine/templates/alpine-ssh.conf.j2 new file mode 100644 index 0000000..77854f9 --- /dev/null +++ b/ansible/roles/firewall_alpine/templates/alpine-ssh.conf.j2 @@ -0,0 +1,16 @@ +# {{ ansible_managed }} +# fail2ban SSH jails for Alpine Linux + +[sshd] +enabled = true +filter = alpine-sshd +port = ssh +logpath = /var/log/messages +maxretry = {{ firewall_alpine_fail2ban_maxretry }} + +[sshd-ddos] +enabled = true +filter = alpine-sshd-ddos +port = ssh +logpath = /var/log/messages +maxretry = {{ firewall_alpine_fail2ban_maxretry }} diff --git a/ansible/roles/firewall_alpine/templates/rules.v4.j2 b/ansible/roles/firewall_alpine/templates/rules.v4.j2 new file mode 100644 index 0000000..5182207 --- /dev/null +++ b/ansible/roles/firewall_alpine/templates/rules.v4.j2 @@ -0,0 +1,14 @@ +# {{ ansible_managed }} +# iptables rules for {{ inventory_hostname }} +# +# Docker and Tailscale manage their own chains automatically. +# This file captures non-Docker, non-Tailscale rules only. +# +# Mail ports (25,80,110,143,443,465,587,993,995) are exposed via +# Docker port mappings in the poste-io docker-compose.yml — not here. + +*filter +:INPUT ACCEPT [0:0] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [0:0] +COMMIT