This commit introduces new features to register IPSETs and update firewall reload notifications. It also conditionally validates Proxmox firewall configurations based on changes, enhancing the robustness of the system.
294 lines
8.7 KiB
YAML
294 lines
8.7 KiB
YAML
---
|
|
# -------------------------------------------------
|
|
# Deploy Fail2Ban integrated with Proxmox Firewall
|
|
# -------------------------------------------------
|
|
|
|
#################################################
|
|
# Detect cluster
|
|
#################################################
|
|
|
|
- name: fail2ban | Detect Proxmox cluster
|
|
ansible.builtin.stat:
|
|
path: /etc/pve/corosync.conf
|
|
register: cluster_status
|
|
|
|
- name: fail2ban | Set cluster fact
|
|
ansible.builtin.set_fact:
|
|
pve_clustered: "{{ cluster_status.stat.exists }}"
|
|
|
|
#################################################
|
|
# Determine Correct Firewall File
|
|
#################################################
|
|
|
|
- name: fail2ban | Get Proxmox node name
|
|
ansible.builtin.command: hostname
|
|
register: pve_node
|
|
changed_when: false
|
|
|
|
- name: fail2ban | Set firewall config path
|
|
ansible.builtin.set_fact:
|
|
pve_firewall_config: >-
|
|
{{
|
|
'/etc/pve/firewall/cluster.fw'
|
|
if pve_clustered
|
|
else '/etc/pve/firewall/' + pve_node.stdout + '.fw'
|
|
}}
|
|
|
|
#################################################
|
|
# Detect firewall configuration
|
|
#################################################
|
|
|
|
- name: fail2ban | Check firewall config exists
|
|
ansible.builtin.stat:
|
|
path: "{{ pve_firewall_config }}"
|
|
register: fw_stat
|
|
|
|
- name: fail2ban | Read firewall config
|
|
ansible.builtin.slurp:
|
|
src: "{{ pve_firewall_config }}"
|
|
register: fw_content
|
|
when: fw_stat.stat.exists
|
|
|
|
- name: fail2ban | Determine if firewall enabled
|
|
ansible.builtin.set_fact:
|
|
pve_firewall_enabled: >-
|
|
{{
|
|
fw_stat.stat.exists and
|
|
(fw_content.content | b64decode) is search('enable:\s*1')
|
|
}}
|
|
|
|
- name: fail2ban | Warn if firewall not enabled
|
|
ansible.builtin.debug:
|
|
msg: >
|
|
WARNING: Proxmox firewall is disabled in configuration.
|
|
Fail2Ban will not actively block traffic.
|
|
when: not pve_firewall_enabled
|
|
|
|
#################################################
|
|
# Validate firewall runtime state
|
|
#################################################
|
|
|
|
- name: fail2ban | Check firewall runtime status
|
|
ansible.builtin.command: pve-firewall status
|
|
register: pve_fw_status
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: fail2ban | Abort if firewall daemon not running
|
|
ansible.builtin.fail:
|
|
msg: >
|
|
Proxmox firewall service is not running.
|
|
Run: systemctl enable --now pve-firewall
|
|
when: pve_fw_status.rc != 0
|
|
|
|
#################################################
|
|
# Corosync safety validation
|
|
#################################################
|
|
|
|
- name: fail2ban | Validate corosync firewall rules
|
|
ansible.builtin.command: pve-firewall compile
|
|
register: compiled_fw
|
|
changed_when: false
|
|
failed_when: false
|
|
when: cluster_status.stat.exists
|
|
|
|
- name: fail2ban | Fail if corosync ports are being dropped
|
|
ansible.builtin.fail:
|
|
msg: >
|
|
Firewall configuration appears to affect Corosync ports (5404/5405).
|
|
Refusing to continue to prevent cluster outage.
|
|
when:
|
|
- cluster_status.stat.exists
|
|
- compiled_fw.stdout is search('5404.*DROP|5405.*DROP')
|
|
|
|
#################################################
|
|
# Install Fail2Ban
|
|
#################################################
|
|
|
|
- name: fail2ban | Install fail2ban
|
|
ansible.builtin.apt:
|
|
name: fail2ban
|
|
state: present
|
|
update_cache: true
|
|
|
|
#################################################
|
|
# Create Proxmox firewall IPSet
|
|
#################################################
|
|
|
|
- name: fail2ban | Add Fail2Ban IPSet to firewall
|
|
ansible.builtin.blockinfile:
|
|
path: "{{ pve_firewall_config }}"
|
|
marker: "# {mark} ANSIBLE FAIL2BAN IPSET"
|
|
insertbefore: BOF
|
|
block: |
|
|
[IPSET {{ f2b_ipset_name }}]
|
|
comment: Fail2Ban dynamic blacklist
|
|
create: false
|
|
register: ipset_change
|
|
notify: Reload pve firewall
|
|
# noqa risky-file-permissions
|
|
|
|
- name: fail2ban | Add drop rule for Fail2Ban IPSet
|
|
ansible.builtin.blockinfile:
|
|
path: "{{ pve_firewall_config }}"
|
|
marker: "# {mark} ANSIBLE FAIL2BAN RULE"
|
|
insertafter: '^\[RULES\]'
|
|
block: |
|
|
IN DROP -source +{{ f2b_ipset_name }}
|
|
create: false
|
|
register: rule_change
|
|
notify: Reload pve firewall
|
|
# noqa risky-file-permissions
|
|
|
|
- name: fail2ban | Extract corosync ring0 address
|
|
ansible.builtin.shell: |
|
|
set -o pipefail
|
|
grep ring0_addr /etc/pve/corosync.conf | awk '{print $2}'
|
|
args:
|
|
executable: /bin/bash
|
|
register: corosync_ip
|
|
changed_when: false
|
|
when: cluster_status.stat.exists
|
|
|
|
- name: fail2ban | Validate Proxmox firewall configuration
|
|
ansible.builtin.command: pve-firewall compile
|
|
when: ipset_change.changed or rule_change.changed
|
|
changed_when: false
|
|
failed_when: fw_compile_check.rc != 0
|
|
register: fw_compile_check
|
|
|
|
# Then automatically whitelist it in Fail2Ban:
|
|
# ignoreip = 127.0.0.1/8 {{ corosync_ip.stdout }}
|
|
|
|
#################################################
|
|
# Create Fail2Ban Proxmox action
|
|
#################################################
|
|
|
|
- name: fail2ban | Create Proxmox firewall action
|
|
ansible.builtin.copy:
|
|
dest: /etc/fail2ban/action.d/proxmox-fw.conf
|
|
mode: '0644'
|
|
content: |
|
|
[Definition]
|
|
actionstart =
|
|
actionstop =
|
|
actioncheck =
|
|
actionban = /usr/sbin/pve-firewall ipset add {{ f2b_ipset_name }} <ip>
|
|
actionunban = /usr/sbin/pve-firewall ipset del {{ f2b_ipset_name }} <ip>
|
|
|
|
#################################################
|
|
# Configure AD-aware jail
|
|
#################################################
|
|
|
|
- name: fail2ban | Ensure jail.d exists
|
|
ansible.builtin.file:
|
|
path: /etc/fail2ban/jail.d
|
|
state: directory
|
|
mode: '0755'
|
|
|
|
- name: fail2ban | Configure Fail2Ban jails
|
|
ansible.builtin.copy:
|
|
dest: /etc/fail2ban/jail.d/proxmox.conf
|
|
mode: '0644'
|
|
content: |
|
|
[DEFAULT]
|
|
bantime = {{ f2b_bantime }}
|
|
findtime = {{ f2b_findtime }}
|
|
maxretry = {{ f2b_maxretry }}
|
|
bantime.increment = {{ f2b_bantime_increment }}
|
|
bantime.factor = {{ f2b_bantime_factor }}
|
|
bantime.max = {{ f2b_bantime_max }}
|
|
backend = systemd
|
|
banaction = proxmox-fw
|
|
ignoreip = 127.0.0.1/8{% if pve_clustered %} {{ corosync_ip.stdout }}{% endif %} 192.168.2.0/24
|
|
|
|
#################################################
|
|
# SSH
|
|
#################################################
|
|
[sshd]
|
|
enabled = true
|
|
journalmatch = _SYSTEMD_UNIT=sshd.service
|
|
|
|
#################################################
|
|
# Proxmox GUI + AD authentication
|
|
#################################################
|
|
[proxmox-auth]
|
|
enabled = true
|
|
port = https,8006
|
|
filter = proxmox-auth
|
|
logpath = /var/log/pveproxy/access.log
|
|
|
|
#################################################
|
|
# Progressive escalation (recidive)
|
|
#################################################
|
|
[recidive]
|
|
enabled = true
|
|
filter = recidive
|
|
logpath = /var/log/fail2ban.log
|
|
bantime = {{ f2b_recidive_bantime }}
|
|
findtime = {{ f2b_recidive_findtime }}
|
|
maxretry = {{ f2b_recidive_maxretry }}
|
|
banaction = proxmox-fw
|
|
notify:
|
|
- Restart fail2ban
|
|
|
|
#################################################
|
|
# AD / Winbind filter
|
|
#################################################
|
|
|
|
- name: fail2ban | Create AD-aware filter
|
|
ansible.builtin.copy:
|
|
dest: /etc/fail2ban/filter.d/proxmox-auth.conf
|
|
mode: '0644'
|
|
content: |
|
|
[Definition]
|
|
failregex = authentication failure; rhost=<HOST>
|
|
pam_unix\(sshd:auth\): authentication failure;.*rhost=<HOST>
|
|
winbind.*authentication for user.*from <HOST> failed
|
|
ignoreregex =
|
|
notify:
|
|
- Restart fail2ban
|
|
|
|
#################################################
|
|
# Enable services
|
|
#################################################
|
|
|
|
- name: fail2ban | Enable fail2ban
|
|
ansible.builtin.systemd:
|
|
name: fail2ban
|
|
enabled: true
|
|
state: started
|
|
|
|
#################################################
|
|
# List banned IPs cluster-wide
|
|
#################################################
|
|
|
|
- name: fail2ban | Get banned IPs from Proxmox IPSet
|
|
ansible.builtin.command: pve-firewall ipset list {{ f2b_ipset_name }}
|
|
register: banned_ips
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: fail2ban | Show banned IPs
|
|
ansible.builtin.debug:
|
|
msg: >
|
|
Current banned IPs (cluster-wide):
|
|
{{ banned_ips.stdout_lines | default([]) }}
|
|
|
|
#################################################
|
|
# Manual unban
|
|
#################################################
|
|
|
|
- name: fail2ban | Unban specific IP
|
|
ansible.builtin.command: >
|
|
pve-firewall ipset del {{ f2b_ipset_name }} {{ f2b_unban_ip }}
|
|
when: f2b_unban_ip | length > 0
|
|
register: unban_result
|
|
changed_when: "'removed' in unban_result.stdout or unban_result.rc == 0"
|
|
failed_when: false
|
|
|
|
- name: fail2ban | Report unban result
|
|
ansible.builtin.debug:
|
|
msg: "Unbanned IP {{ f2b_unban_ip }}"
|
|
when: f2b_unban_ip | length > 0
|