capture nuremberg-a firewall rules in pez-infra (#15)

Add firewall_alpine role for Alpine hosts with iptables persistence
and fail2ban SSH jails. Wire it into nuremberg-a's deploy stage.

Mail ports are already exposed via Docker port mappings in the
poste-io docker-compose — this captures the surrounding iptables
and fail2ban config that was previously undocumented.

Closes PESO-96
This commit is contained in:
Rasmus Wejlgaard 2026-03-29 14:40:10 +01:00 committed by GitHub
parent 258a38aeb5
commit a7a71e4f87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 104 additions and 1 deletions

View file

@ -65,6 +65,7 @@
hosts: nuremberg-a hosts: nuremberg-a
tags: [services, mail] tags: [services, mail]
roles: roles:
- role: firewall_alpine
- role: docker_services - role: docker_services
# copenhagen-a: Gaming servers # copenhagen-a: Gaming servers

View file

@ -4,4 +4,6 @@ host_description: "Mail server (poste.io)"
host_location: "Hetzner Cloud" host_location: "Hetzner Cloud"
ansible_python_interpreter: /usr/bin/python3 ansible_python_interpreter: /usr/bin/python3
# NOTE: Alpine host — UFW tasks are Debian-only. # 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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 }}

View file

@ -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