Files
ansible_role_proxmox_provision/tasks/fail2ban.yml
Jose 54f3f761c8
All checks were successful
ansible-lint / Ansible Lint (push) Successful in 14s
Gitleaks Scan / gitleaks (push) Successful in 4s
Markdown Lint / markdown-lint (push) Successful in 5s
ai-reviews / Review PR (pull_request) Successful in 12s
PR check / Gitleaks (pull_request) Successful in 5s
PR check / lint tests (pull_request) Successful in 16s
PR check / labeler (pull_request) Successful in 2s
PR check / handle_failures (pull_request) Has been skipped
PR check / handle_success (pull_request) Successful in 2s
chore 📦: Remove commented-out code in fail2ban.yml
This commit removes unnecessary commented-out code from the fail2ban.yml file to clean up and simplify the configuration.
2026-03-01 12:05:48 +01:00

313 lines
9.4 KiB
YAML

---
# -------------------------------------------------
# Deploy Fail2Ban integrated with Proxmox Firewall
# -------------------------------------------------
#################################################
# Detect Proxmox
#################################################
- name: fail2ban | Detect Proxmox
ansible.builtin.stat:
path: /usr/bin/pveversion
register: pve_installed
#################################################
# Ensure pmxcfs is mounted (cluster filesystem)
#################################################
- name: fail2ban | Check pmxcfs cluster filesystem
ansible.builtin.stat:
path: /etc/pve/.members
register: pmxcfs_running
when: pve_installed.stat.exists | default(false)
- name: fail2ban | Warn if pmxcfs not mounted (no quorum)
ansible.builtin.debug:
msg: >
/etc/pve is not mounted or node has no quorum.
Refusing to modify cluster firewall.
when:
- pve_installed.stat.exists | default(false)
- not pmxcfs_running.stat.exists
#################################################
# Detect cluster membership
#################################################
- name: fail2ban | Detect Proxmox cluster membership
ansible.builtin.stat:
path: /etc/pve/corosync.conf
register: clustered
when: pmxcfs_running.stat.exists | default(false)
- name: fail2ban | Warn if corosync.conf is missing
ansible.builtin.debug:
msg: >
node has no quorum.
Refusing to modify cluster firewall.
when:
- pve_installed.stat.exists | default(false)
- pmxcfs_running.stat.exists | default(false)
- not clustered.stat.exists
#################################################
# Install Fail2Ban
#################################################
- name: fail2ban | Install fail2ban
ansible.builtin.apt:
name: fail2ban
state: present
update_cache: true
#################################################
# Ensure jail.local exists (do NOT copy jail.conf)
#################################################
- name: fail2ban | Ensure jail.local exists
ansible.builtin.file:
path: /etc/fail2ban/jail.local
state: touch
owner: root
group: root
mode: '0644'
#################################################
# Configure Fail2Ban jails
#################################################
- name: fail2ban | Configure Fail2Ban jails
ansible.builtin.blockinfile:
dest: /etc/fail2ban/jail.local
marker: "# {mark} ANSIBLE MANAGED BLOCK - PROXMOX"
block: |
# jail.conf (default)
# jail.local (override defaults)
[DEFAULT]
bantime = {{ f2b_bantime }}
findtime = {{ f2b_findtime }}
maxretry = {{ f2b_maxretry }}
bantime.increment = {{ f2b_bantime_increment }}
bantime.factor = {{ f2b_bantime_factor }}
bantime.maxtime = {{ f2b_bantime_max }}
backend = systemd
banaction = {% if (clustered.stat.exists | default(false)) %} proxmox-fw{% else %} iptables-multiport{% endif %}
ignoreip = 127.0.0.1/8 192.168.2.0/24
#################################################
# SSH
#################################################
[sshd]
enabled = true
#################################################
# Proxmox GUI + AD authentication
#################################################
[proxmox]
enabled = true
port = https,http,8006
filter = proxmox
#################################################
# 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 = {% if (clustered.stat.exists | default(false)) %} proxmox-fw{% else %} iptables-multiport{% endif %}
notify:
- Reload fail2ban
- name: fail2ban | Place Proxmox filter definition
ansible.builtin.copy:
dest: /etc/fail2ban/filter.d/proxmox.conf
content: |
[Definition]
failregex = pvedaemon\[.*authentication failure; rhost=<HOST> user=.* msg=.*
ignoreregex =
journalmatch = _SYSTEMD_UNIT=pvedaemon.service
owner: root
group: root
mode: '0644'
notify:
- Reload fail2ban
#################################################
# Determine Correct Firewall File
#################################################
- name: fail2ban | Get Proxmox node name
ansible.builtin.set_fact:
pve_node: "{{ ansible_hostname }}"
when: not clustered.stat.exists
- name: fail2ban | Set firewall config path
ansible.builtin.set_fact:
pve_firewall_config: >-
{{
'/etc/pve/firewall/cluster.fw'
if clustered.stat.exists
else '/etc/pve/nodes/' + pve_node + '.fw'
}}
when: pve_installed.stat.exists | default(false)
#################################################
# Detect firewall configuration
#################################################
- name: fail2ban | Check firewall config exists
ansible.builtin.stat:
path: "{{ pve_firewall_config }}"
register: fw_stat
when: pve_firewall_config is defined
- name: fail2ban | Read firewall config
ansible.builtin.slurp:
src: "{{ pve_firewall_config }}"
register: fw_content
when: fw_stat.stat.exists | default(false)
- name: fail2ban | Determine if firewall enabled
ansible.builtin.set_fact:
pve_firewall_enabled: >-
{{
(fw_stat.stat.exists | default(false)) and
(
(fw_content.content | default('') | 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
when: pmxcfs_running.stat.exists | default(false)
- name: fail2ban | Abort if firewall daemon not running
ansible.builtin.debug:
msg: >
Proxmox firewall service is not running.
You can run: systemctl enable --now pve-firewall
when:
- pve_fw_status is defined
- pve_fw_status.rc != 0
- fw_stat.stat.exists | default(false)
- pmxcfs_running.stat.exists | default(false)
#################################################
# Corosync safety validation
#################################################
- name: fail2ban | Validate corosync firewall rules
ansible.builtin.command: pve-firewall compile
register: compiled_fw
changed_when: false
failed_when: compiled_fw.rc != 0
when: clustered.stat.exists | default(false)
- name: fail2ban | Fail if corosync ports are being dropped
ansible.builtin.debug:
msg: >
Firewall configuration appears to affect Corosync ports (5404/5405).
Refusing to continue to prevent cluster outage.
when:
- clustered.stat.exists | default(false)
- compiled_fw.stdout is search('5404.*DROP|5405.*DROP')
#################################################
# Deploy cluster-aware Fail2Ban action
#################################################
- name: fail2ban | Deploy proxmox-fw action
ansible.builtin.copy:
dest: /etc/fail2ban/action.d/proxmox-fw.conf
owner: root
group: root
mode: '0644'
content: |
[Definition]
fwfile = {{ pve_firewall_config }}
rule = DROP -source <ip> -log nolog
actionban = \
if [ -f <fwfile> ]; then \
grep -qF "<rule>" <fwfile> || echo "<rule>" >> <fwfile>; \
pve-firewall compile >/dev/null 2>&1 || true; \
fi
actionunban = \
if [ -f <fwfile> ]; then \
sed -i "\|<rule>|d" <fwfile>; \
pve-firewall compile >/dev/null 2>&1 || true; \
fi
actionstart =
actionstop =
when:
- clustered.stat.exists | default(false)
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 is defined and 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