Files
ansible_role_proxmox_provision/tasks/fail2ban.yml
Jose 3afa853d09
Some checks failed
ansible-lint / Ansible Lint (push) Failing after 6s
Gitleaks Scan / gitleaks (push) Successful in 5s
Markdown Lint / markdown-lint (push) Successful in 5s
feat : Add new variable f2b_unban_ip for specifying an IP to unban during playbook execution.
Introduce a new variable `f2b_unban_ip` in the Ansible playbook to allow users to specify an IP address that should be unbanned using Fail2Ban. This feature enhances the flexibility of the playbook by enabling targeted IP management.
2026-02-23 18:35:10 +01:00

269 lines
8.1 KiB
YAML

---
# -------------------------------------------------
# Deploy Fail2Ban integrated with Proxmox Firewall
# -------------------------------------------------
#################################################
# Detect firewall configuration
#################################################
- name: fail2ban | Check if cluster firewall config exists
ansible.builtin.stat:
path: /etc/pve/firewall/cluster.fw
register: cluster_fw
- name: fail2ban | Read cluster firewall config
slurp:
src: /etc/pve/firewall/cluster.fw
register: cluster_fw_content
when: cluster_fw.stat.exists
- name: fail2ban | Determine if firewall enabled
ansible.builtin.set_fact:
pve_firewall_enabled: >-
{{
cluster_fw.stat.exists and
(cluster_fw_content.content | b64decode)
is search('enable:\s*1')
}}
- name: fail2ban | Abort if firewall not enabled
ansible.builtin.fail:
msg: >
Proxmox firewall is not enabled at Datacenter level.
Enable it before deploying Fail2Ban integration.
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: "'Status: enabled' not in pve_fw_status.stdout"
#################################################
# 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 }}"
#################################################
# 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')
- compiled_fw.stdout is search('DROP')
#################################################
# Install Fail2Ban
#################################################
- name: fail2ban | Install fail2ban
ansible.builtin.apt:
name: fail2ban
state: present
update_cache: true
#################################################
# Create Proxmox firewall IPSet (cluster-wide)
#################################################
- name: fail2ban | Ensure firewall cluster config exists
ansible.builtin.file:
path: /etc/pve/firewall
state: directory
when: pve_clustered
- name: fail2ban | Add Fail2Ban IPSet to cluster firewall
ansible.builtin.blockinfile:
path: /etc/pve/firewall/cluster.fw
marker: "# {mark} ANSIBLE FAIL2BAN IPSET"
block: |
[IPSET {{ f2b_ipset_name }}]
create: true
when: pve_clustered
- name: fail2ban | Add drop rule for Fail2Ban IPSet
ansible.builtin.blockinfile:
path: /etc/pve/firewall/cluster.fw
marker: "# {mark} ANSIBLE FAIL2BAN RULE"
block: |
[RULES]
IN DROP -source +{{ f2b_ipset_name }}
when: pve_clustered
- name: fail2ban | Extract corosync ring0 address
ansible.builtin.shell: grep ring0_addr /etc/pve/corosync.conf | awk '{print $2}'
register: corosync_ip
changed_when: false
when: cluster_status.stat.exists
# 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 {{ corosync_ip.stdout | default('') }} 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
- name: fail2ban | Reload Proxmox firewall
ansible.builtin.command: pve-firewall reload
changed_when: false
#################################################
# 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