From 734a9c031f1c4ad4d8e979ef6ac06ca777caf916 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 25 Feb 2026 17:52:11 +0100 Subject: [PATCH 1/8] =?UTF-8?q?style=20=F0=9F=92=8E:=20Fix=20indentation?= =?UTF-8?q?=20and=20variable=20registration=20in=20fail2ban.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored the YAML file to improve readability and ensure proper variable registration. --- tasks/fail2ban.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/fail2ban.yml b/tasks/fail2ban.yml index c5fa8b9..a8ca79d 100644 --- a/tasks/fail2ban.yml +++ b/tasks/fail2ban.yml @@ -123,7 +123,7 @@ [IPSET {{ f2b_ipset_name }}] comment: Fail2Ban dynamic blacklist create: false - register: ipset_change + register: ipset_change notify: Reload pve firewall # noqa risky-file-permissions @@ -135,7 +135,7 @@ block: | IN DROP -source +{{ f2b_ipset_name }} create: false - register: rule_change + register: rule_change notify: Reload pve firewall # noqa risky-file-permissions From 9e90c67fe88e527fbb04b3322cce3fc23fd3ca70 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 25 Feb 2026 17:59:13 +0100 Subject: [PATCH 2/8] =?UTF-8?q?refactor=20=E2=99=BB=EF=B8=8F:=20Refactor?= =?UTF-8?q?=20fail2ban.yml=20to=20use=20fw=5Fcompile=5Fcheck.rc=20for=20fa?= =?UTF-8?q?ilure=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This refactoring updates the fail2ban configuration to utilize a new script, `fw_compile_check.rc`, for handling failure checks. This change aims to streamline the process and improve reliability by centralizing the logic in a dedicated script. --- tasks/fail2ban.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/fail2ban.yml b/tasks/fail2ban.yml index a8ca79d..e876617 100644 --- a/tasks/fail2ban.yml +++ b/tasks/fail2ban.yml @@ -88,7 +88,7 @@ ansible.builtin.command: pve-firewall compile register: compiled_fw changed_when: false - failed_when: fw_compile_check.rc != 0 + failed_when: compiled_fw.rc != 0 when: cluster_status.stat.exists - name: fail2ban | Fail if corosync ports are being dropped @@ -171,8 +171,8 @@ 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 + failed_when: fw_compile_check.rc != 0 # Then automatically whitelist it in Fail2Ban: # ignoreip = 127.0.0.1/8 {{ corosync_ip.stdout }} From 7c304765a45534729da4d15aa7551317944d5df9 Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 1 Mar 2026 10:22:41 +0100 Subject: [PATCH 3/8] =?UTF-8?q?refactor=20=E2=99=BB=EF=B8=8F:=20Refactor?= =?UTF-8?q?=20fail2ban.yml=20for=20Proxmox=20cluster=20detection=20and=20c?= =?UTF-8?q?onfiguration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the fail2ban.yml file to include support for detecting a Proxmox cluster, ensuring that pmxcfs is mounted, installing Fail2Ban, and configuring appropriate jails. This enhances the security and management of the Proxmox environment by automating the setup and monitoring of failed login attempts. --- tasks/fail2ban.yml | 346 +++++++++++++++++++++++---------------------- 1 file changed, 175 insertions(+), 171 deletions(-) diff --git a/tasks/fail2ban.yml b/tasks/fail2ban.yml index e876617..fc0c0dd 100644 --- a/tasks/fail2ban.yml +++ b/tasks/fail2ban.yml @@ -4,17 +4,140 @@ # ------------------------------------------------- ################################################# -# Detect cluster +# Detect Proxmox ################################################# -- name: fail2ban | Detect Proxmox cluster +- name: fail2ban | Detect Proxmox + 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: cluster_status + register: clustered + when: pmxcfs_running.stat.exists | default(false) -- name: fail2ban | Set cluster fact - ansible.builtin.set_fact: - pve_clustered: "{{ cluster_status.stat.exists }}" +- 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 + create: true + 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 + # {% if pmxcfs_running.stat.exists %} {{ corosync_networks | join(' ') }}{% endif %} + + ################################################# + # 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= user=.* msg=.* + ignoreregex = + journalmatch = _SYSTEMD_UNIT=pvedaemon.service + owner: root + group: root + mode: '0644' + notify: + - Reload fail2ban ################################################# # Determine Correct Firewall File @@ -23,15 +146,17 @@ - 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 pve_clustered - else '/etc/pve/firewall/' + pve_node + '.fw' + if clustered.stat.exists + else '/etc/pve/nodes/' + pve_node + '.fw' }} + when: pve_installed.stat.exists | default(false) ################################################# # Detect firewall configuration @@ -41,19 +166,23 @@ 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 + when: fw_stat.stat.exists | default(false) - 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') + (fw_stat.stat.exists | default(false)) and + ( + (fw_content.content | default('') | b64decode) + is search('enable:\s*1') + ) }} - name: fail2ban | Warn if firewall not enabled @@ -72,13 +201,18 @@ 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.fail: + ansible.builtin.debug: msg: > Proxmox firewall service is not running. - Run: systemctl enable --now pve-firewall - when: pve_fw_status.rc != 0 + 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 @@ -89,180 +223,50 @@ register: compiled_fw changed_when: false failed_when: compiled_fw.rc != 0 - when: cluster_status.stat.exists + when: clustered.stat.exists | default(false) - name: fail2ban | Fail if corosync ports are being dropped - ansible.builtin.fail: + ansible.builtin.debug: msg: > Firewall configuration appears to affect Corosync ports (5404/5405). Refusing to continue to prevent cluster outage. when: - - cluster_status.stat.exists + - clustered.stat.exists | default(false) - compiled_fw.stdout is search('5404.*DROP|5405.*DROP') ################################################# -# Install Fail2Ban +# Deploy cluster-aware Fail2Ban action ################################################# -- 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 all corosync ring addresses - ansible.builtin.shell: | - set -o pipefail - awk '/ring[0-9]+_addr/ {print $2}' /etc/pve/corosync.conf - args: - executable: /bin/bash - register: corosync_ips - changed_when: false - when: pve_clustered - -- name: fail2ban | Determine CIDR for each corosync IP - ansible.builtin.command: ip route get {{ item }} - register: corosync_routes - changed_when: false - loop: "{{ corosync_ips.stdout_lines }}" - when: pve_clustered - -- name: fail2ban | Extract network CIDRs - ansible.builtin.set_fact: - corosync_networks: >- - {{ - corosync_routes.results - | map(attribute='stdout') - | map('regex_search', 'src ([0-9.]+)/([0-9]+)', '\\1/\\2') - | list - }} - when: pve_clustered - -- name: fail2ban | Validate Proxmox firewall configuration - ansible.builtin.command: pve-firewall compile - when: ipset_change.changed or rule_change.changed - changed_when: false - register: fw_compile_check - failed_when: fw_compile_check.rc != 0 - -# 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 +- name: fail2ban-fw | 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 -log nolog + + actionban = \ + if [ -f ]; then \ + grep -qF "" || echo "" >> ; \ + pve-firewall compile >/dev/null 2>&1 || true; \ + fi + + actionunban = \ + if [ -f ]; then \ + sed -i "\||d" ; \ + pve-firewall compile >/dev/null 2>&1 || true; \ + fi + actionstart = actionstop = - actioncheck = - actionban = /usr/sbin/pve-firewall ipset add {{ f2b_ipset_name }} - actionunban = /usr/sbin/pve-firewall ipset del {{ f2b_ipset_name }} - -################################################# -# 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_networks | join(' ') }}{% 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= - pam_unix\(sshd:auth\): authentication failure;.*rhost= - winbind.*authentication for user.*from failed - ignoreregex = + when: + - clustered.stat.exists | default(false) notify: - Restart fail2ban @@ -299,7 +303,7 @@ - name: fail2ban | Unban specific IP ansible.builtin.command: > pve-firewall ipset del {{ f2b_ipset_name }} {{ f2b_unban_ip }} - when: f2b_unban_ip | length > 0 + 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 From bc9138309448464cb2af4e7a94c89736447be3d3 Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 1 Mar 2026 10:22:58 +0100 Subject: [PATCH 4/8] =?UTF-8?q?chore=20=F0=9F=93=A6:=20Update=20Fail2ban?= =?UTF-8?q?=20settings=20and=20add/restart/reload=20tasks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the Fail2ban configuration to reduce bantime and findtime, and adds/fixes restart/reload tasks in handlers/main.yml. These changes aim to improve security and manageability of the fail2ban service. --- defaults/main.yml | 6 +++--- handlers/main.yml | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/defaults/main.yml b/defaults/main.yml index 041d1ba..621e915 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -49,10 +49,10 @@ vm_dirty_background_ratio: 5 vm_swappiness: "{{ proxmox_swapiness }}" # Fail2ban settings -f2b_bantime: 1800 # 30 minutes -f2b_findtime: 600 +f2b_bantime: 600 # 10 minutes +f2b_findtime: 1200 # 20 minutes f2b_maxretry: 5 -f2b_recidive_bantime: 86400 # 24 hours +f2b_recidive_bantime: 3600 # 1 hours f2b_recidive_findtime: 86400 # 24 hours f2b_recidive_maxretry: 3 f2b_ipset_name: f2b-blacklist diff --git a/handlers/main.yml b/handlers/main.yml index 600b186..dd5e59b 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -32,10 +32,18 @@ ansible.builtin.systemd: daemon_reload: true -- name: Restart fail2ban +- name: Reload fail2ban ansible.builtin.systemd: name: fail2ban state: reloaded + enabled: true + +- name: Restart fail2ban + ansible.builtin.systemd: + name: fail2ban + state: restarted + enabled: true + - name: Reload pve firewall ansible.builtin.command: pve-firewall reload From 94bcbbac5bfdda8df06a4d5909524986518c5028 Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 1 Mar 2026 10:23:11 +0100 Subject: [PATCH 5/8] =?UTF-8?q?docs=20=F0=9F=93=9D:=20Add=20Fail2Ban=20dep?= =?UTF-8?q?loyment=20and=20configuration=20documentation=20for=20Proxmox?= =?UTF-8?q?=20VE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a new file `meta/fail2ban.md` containing detailed documentation on how to deploy and configure Fail2Ban on Proxmox VE, including integration with the Proxmox firewall. The documentation aims to provide comprehensive guidance for users looking to enhance their server security by implementing Fail2Ban. --- meta/fail2ban.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 meta/fail2ban.md diff --git a/meta/fail2ban.md b/meta/fail2ban.md new file mode 100644 index 0000000..3db95c8 --- /dev/null +++ b/meta/fail2ban.md @@ -0,0 +1,94 @@ +# Fail2Ban Integration with Proxmox Firewall + +This Ansible playbook deploys and configures **Fail2Ban** on a Proxmox VE environment, integrating it with the **Proxmox firewall** for cluster-aware IP banning. It supports both single-node and clustered Proxmox setups. + +--- + +## Features + +- Detects Proxmox VE installation. +- Checks cluster filesystem (`pmxcfs`) and quorum before modifying firewall. +- Detects cluster membership via `corosync.conf`. +- Installs and configures Fail2Ban with: + - SSH protection + - Proxmox GUI / AD login protection + - Progressive ban escalation (recidive jail) +- Deploys a **cluster-aware Fail2Ban action** (`proxmox-fw`) for Proxmox firewall integration. +- Ensures safe firewall updates without affecting Corosync ports (5404/5405). +- Supports single-node Fail2Ban using `iptables-multiport`. +- Enables and starts the Fail2Ban service. +- Provides tasks to list or manually unban IPs in the cluster. + +--- + +## Requirements + +- **Proxmox VE** (any supported version) +- **Ansible** ≥ 2.9 +- Root or sudo access on target nodes +- Proxmox firewall enabled for cluster-wide banning (optional, but recommended) + +--- + +## Variables + +The playbook uses the following variables (can be defined in a `vars` file or inventory group vars): + +| Variable | Description | Default / Notes | +|----------|-------------|----------------| +| `f2b_bantime` | Default ban time for repeated failures | e.g., `600s` | +| `f2b_findtime` | Time window to check failures | e.g., `1200s`| +| `f2b_maxretry` | Maximum retries before ban | e.g., `5` | +| `f2b_bantime_increment` | Incremental ban time (recidive) | e.g., `true` | +| `f2b_bantime_factor` | Factor for incremental ban | e.g., `2` | +| `f2b_bantime_max` | Maximum ban time | e.g., `7d` | +| `f2b_recidive_bantime` | Ban time for recidive jail | e.g., `3600` | +| `f2b_recidive_findtime` | Findtime for recidive jail | e.g., `86400` | +| `f2b_recidive_maxretry` | Max retry for recidive jail | e.g., `3` | +| `f2b_ipset_name` | Name of Proxmox IPSet used for banned IPs | e.g., `f2b-blacklist` | +| `f2b_unban_ip` | Optional IP to unban manually | Leave undefined if not needed | + +> All `clustered` and `pmxcfs_running` checks default to `false` to prevent errors on non-clustered or single-node setups. + +--- + +## Usage + +### 1. Apply the playbook + +```bash +ansible-playbook -i inventory fail2ban-proxmox.yml +``` + +### 2. List current banned IPs + +```bash +ansible-playbook -i inventory fail2ban-proxmox.yml -e "f2b_ipset_name=fail2ban" -t list_banned +``` + +### 3. Unban a specific IP + +```bash +ansible-playbook -i inventory fail2ban-proxmox.yml -e "f2b_unban_ip=1.2.3.4" +``` + +## How It Works + +- Detects Proxmox – ensures the playbook runs only on Proxmox VE hosts. +- Cluster safety checks – verifies /etc/pve/.members and corosync.conf for quorum. +- Installs Fail2Ban – ensures /etc/fail2ban/jail.local exists and applies configuration. +- Cluster-aware action – for clustered nodes, Fail2Ban bans are added to Proxmox firewall and compiled immediately (pve-firewall compile). +- Single-node fallback – uses iptables-multiport for nodes not in a cluster. +- Corosync protection – prevents firewall rules from dropping cluster communication ports (5404/5405). + +## Notes & Safety + +- The playbook does not copy jail.conf, only manages jail.local. +- Firewall rules for clustered nodes are only modified if quorum exists. +- pve-firewall compile is called safely (>/dev/null 2>&1 || true) to prevent playbook failure on minor compilation warnings. +- Manual unban is supported via f2b_unban_ip variable. +- Always verify that the Proxmox firewall is enabled when using cluster-wide bans. + +## License + +MIT License \ No newline at end of file From e44f757b9e7f590de730caec7b978742d8755b8d Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 1 Mar 2026 10:31:36 +0100 Subject: [PATCH 6/8] =?UTF-8?q?refactor=20=E2=99=BB=EF=B8=8F:=20Refactor?= =?UTF-8?q?=20and=20reformat=20text=20for=20better=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the code by improving text formatting and structure to enhance readability, ensuring that the content and functionality remain unchanged. --- meta/fail2ban.md | 30 ++++++++++++++++++++---------- tasks/fail2ban.yml | 2 +- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/meta/fail2ban.md b/meta/fail2ban.md index 3db95c8..7165647 100644 --- a/meta/fail2ban.md +++ b/meta/fail2ban.md @@ -1,6 +1,8 @@ # Fail2Ban Integration with Proxmox Firewall -This Ansible playbook deploys and configures **Fail2Ban** on a Proxmox VE environment, integrating it with the **Proxmox firewall** for cluster-aware IP banning. It supports both single-node and clustered Proxmox setups. +This Ansible playbook deploys and configures **Fail2Ban** on a Proxmox VE +environment, integrating it with the **Proxmox firewall** for cluster-aware +IP banning. It supports both single-node and clustered Proxmox setups. --- @@ -13,7 +15,8 @@ This Ansible playbook deploys and configures **Fail2Ban** on a Proxmox VE enviro - SSH protection - Proxmox GUI / AD login protection - Progressive ban escalation (recidive jail) -- Deploys a **cluster-aware Fail2Ban action** (`proxmox-fw`) for Proxmox firewall integration. +- Deploys a **cluster-aware Fail2Ban action** (`proxmox-fw`) for Proxmox + firewall integration. - Ensures safe firewall updates without affecting Corosync ports (5404/5405). - Supports single-node Fail2Ban using `iptables-multiport`. - Enables and starts the Fail2Ban service. @@ -32,7 +35,8 @@ This Ansible playbook deploys and configures **Fail2Ban** on a Proxmox VE enviro ## Variables -The playbook uses the following variables (can be defined in a `vars` file or inventory group vars): +The playbook uses the following variables (can be defined in a `vars` file or +inventory group vars): | Variable | Description | Default / Notes | |----------|-------------|----------------| @@ -75,19 +79,25 @@ ansible-playbook -i inventory fail2ban-proxmox.yml -e "f2b_unban_ip=1.2.3.4" ## How It Works - Detects Proxmox – ensures the playbook runs only on Proxmox VE hosts. -- Cluster safety checks – verifies /etc/pve/.members and corosync.conf for quorum. -- Installs Fail2Ban – ensures /etc/fail2ban/jail.local exists and applies configuration. -- Cluster-aware action – for clustered nodes, Fail2Ban bans are added to Proxmox firewall and compiled immediately (pve-firewall compile). -- Single-node fallback – uses iptables-multiport for nodes not in a cluster. -- Corosync protection – prevents firewall rules from dropping cluster communication ports (5404/5405). +- Cluster safety checks – verifies /etc/pve/.members and corosync.conf + for quorum. +- Installs Fail2Ban – ensures /etc/fail2ban/jail.local exists and applies + configuration. +- Cluster-aware action – for clustered nodes, Fail2Ban bans are added to + Proxmox firewall and compiled immediately (pve-firewall compile). +- Single-node fallback – uses iptables-multiport for nodes not in + a cluster. +- Corosync protection – prevents firewall rules from dropping cluster + communication ports (5404/5405). ## Notes & Safety - The playbook does not copy jail.conf, only manages jail.local. - Firewall rules for clustered nodes are only modified if quorum exists. -- pve-firewall compile is called safely (>/dev/null 2>&1 || true) to prevent playbook failure on minor compilation warnings. +- pve-firewall compile is called safely (>/dev/null 2>&1 || true) + to prevent playbook failure on minor compilation warnings. - Manual unban is supported via f2b_unban_ip variable. -- Always verify that the Proxmox firewall is enabled when using cluster-wide bans. +- Always verify that the Proxmox firewall is enabled when using cluster-wide bans. ## License diff --git a/tasks/fail2ban.yml b/tasks/fail2ban.yml index fc0c0dd..23a224f 100644 --- a/tasks/fail2ban.yml +++ b/tasks/fail2ban.yml @@ -266,7 +266,7 @@ actionstart = actionstop = when: - - clustered.stat.exists | default(false) + - clustered.stat.exists | default(false) notify: - Restart fail2ban From 0004d2bd2da9dfb6d9810b2ff6eedf11a6588b0f Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 1 Mar 2026 10:48:13 +0100 Subject: [PATCH 7/8] =?UTF-8?q?refactor=20=E2=99=BB=EF=B8=8F:=20Refactor?= =?UTF-8?q?=20task=20names=20and=20update=20module=20references=20in=20`fa?= =?UTF-8?q?il2ban.yml`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the task names and updates module references in the `fail2ban.yml` file to improve clarity and consistency. It also includes minor text adjustments in `meta/fail2ban.md` to enhance readability and ensure accurate variable descriptions with updated default values. --- meta/fail2ban.md | 34 ++++++++++++++++++---------------- tasks/fail2ban.yml | 5 ++--- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/meta/fail2ban.md b/meta/fail2ban.md index 7165647..85783d2 100644 --- a/meta/fail2ban.md +++ b/meta/fail2ban.md @@ -38,21 +38,22 @@ IP banning. It supports both single-node and clustered Proxmox setups. The playbook uses the following variables (can be defined in a `vars` file or inventory group vars): -| Variable | Description | Default / Notes | -|----------|-------------|----------------| -| `f2b_bantime` | Default ban time for repeated failures | e.g., `600s` | -| `f2b_findtime` | Time window to check failures | e.g., `1200s`| -| `f2b_maxretry` | Maximum retries before ban | e.g., `5` | -| `f2b_bantime_increment` | Incremental ban time (recidive) | e.g., `true` | -| `f2b_bantime_factor` | Factor for incremental ban | e.g., `2` | -| `f2b_bantime_max` | Maximum ban time | e.g., `7d` | -| `f2b_recidive_bantime` | Ban time for recidive jail | e.g., `3600` | -| `f2b_recidive_findtime` | Findtime for recidive jail | e.g., `86400` | -| `f2b_recidive_maxretry` | Max retry for recidive jail | e.g., `3` | -| `f2b_ipset_name` | Name of Proxmox IPSet used for banned IPs | e.g., `f2b-blacklist` | -| `f2b_unban_ip` | Optional IP to unban manually | Leave undefined if not needed | +| Variable | Description | Default | +|--------------------------|-------------------------------------|------------------| +| `f2b_bantime` | Ban per tentativi falliti | `600s` | +| `f2b_findtime` | Finestra per contare i fallimenti | `1200s` | +| `f2b_maxretry` | Tentativi prima del ban | `5` | +| `f2b_bantime_increment` | Abilita ban incrementale | `true` | +| `f2b_bantime_factor` | Fattore aumento ban | `2` | +| `f2b_bantime_max` | Durata massima del ban | `7d` | +| `f2b_recidive_bantime` | Ban per recidiva | `3600` | +| `f2b_recidive_findtime` | Finestra recidiva | `86400` | +| `f2b_recidive_maxretry` | Tentativi recidiva | `3` | +| `f2b_ipset_name` | Nome IPSet per IP bannati | `f2b-blacklist` | +| `f2b_unban_ip` | IP da sbloccare | `""` | -> All `clustered` and `pmxcfs_running` checks default to `false` to prevent errors on non-clustered or single-node setups. +> All `clustered` and `pmxcfs_running` checks default to `false` to prevent +> errors on non-clustered or single-node setups. --- @@ -97,8 +98,9 @@ ansible-playbook -i inventory fail2ban-proxmox.yml -e "f2b_unban_ip=1.2.3.4" - pve-firewall compile is called safely (>/dev/null 2>&1 || true) to prevent playbook failure on minor compilation warnings. - Manual unban is supported via f2b_unban_ip variable. -- Always verify that the Proxmox firewall is enabled when using cluster-wide bans. +- Always verify that the Proxmox firewall is enabled when using + cluster-wide bans. ## License -MIT License \ No newline at end of file +MIT License diff --git a/tasks/fail2ban.yml b/tasks/fail2ban.yml index 23a224f..cc814e7 100644 --- a/tasks/fail2ban.yml +++ b/tasks/fail2ban.yml @@ -8,7 +8,7 @@ ################################################# - name: fail2ban | Detect Proxmox - stat: + ansible.builtin.stat: path: /usr/bin/pveversion register: pve_installed @@ -80,7 +80,6 @@ - name: fail2ban | Configure Fail2Ban jails ansible.builtin.blockinfile: dest: /etc/fail2ban/jail.local - create: true marker: "# {mark} ANSIBLE MANAGED BLOCK - PROXMOX" block: | # jail.conf (default) @@ -238,7 +237,7 @@ # Deploy cluster-aware Fail2Ban action ################################################# -- name: fail2ban-fw | Deploy proxmox-fw action +- name: fail2ban | Deploy proxmox-fw action ansible.builtin.copy: dest: /etc/fail2ban/action.d/proxmox-fw.conf owner: root From 752db2b57f60ca2c8d49af379eaa351609632fd9 Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 1 Mar 2026 10:52:57 +0100 Subject: [PATCH 8/8] =?UTF-8?q?style=20=F0=9F=92=8E:=20Fix=20table=20forma?= =?UTF-8?q?tting=20and=20add=20code=20block=20for=20list=5Fbanned=20task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit addresses the formatting issues in the table and adds a code block for the 'list_banned' task to improve readability and clarity. --- meta/fail2ban.md | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/meta/fail2ban.md b/meta/fail2ban.md index 85783d2..a40c675 100644 --- a/meta/fail2ban.md +++ b/meta/fail2ban.md @@ -38,19 +38,19 @@ IP banning. It supports both single-node and clustered Proxmox setups. The playbook uses the following variables (can be defined in a `vars` file or inventory group vars): -| Variable | Description | Default | -|--------------------------|-------------------------------------|------------------| -| `f2b_bantime` | Ban per tentativi falliti | `600s` | -| `f2b_findtime` | Finestra per contare i fallimenti | `1200s` | -| `f2b_maxretry` | Tentativi prima del ban | `5` | -| `f2b_bantime_increment` | Abilita ban incrementale | `true` | -| `f2b_bantime_factor` | Fattore aumento ban | `2` | -| `f2b_bantime_max` | Durata massima del ban | `7d` | -| `f2b_recidive_bantime` | Ban per recidiva | `3600` | -| `f2b_recidive_findtime` | Finestra recidiva | `86400` | -| `f2b_recidive_maxretry` | Tentativi recidiva | `3` | -| `f2b_ipset_name` | Nome IPSet per IP bannati | `f2b-blacklist` | -| `f2b_unban_ip` | IP da sbloccare | `""` | +| Variable | Description | Default | +|-------------------------|---------------------------------|-----------------| +| `f2b_bantime` | Ban per tentativi falliti | `600s` | +| `f2b_findtime` | Finestra per contare fallimenti | `1200s` | +| `f2b_maxretry` | Tentativi prima del ban | `5` | +| `f2b_bantime_increment` | Abilita ban incrementale | `true` | +| `f2b_bantime_factor` | Fattore aumento ban | `2` | +| `f2b_bantime_max` | Durata massima del ban | `7d` | +| `f2b_recidive_bantime` | Ban per recidiva | `3600` | +| `f2b_recidive_findtime` | Finestra recidiva | `86400` | +| `f2b_recidive_maxretry` | Tentativi recidiva | `3` | +| `f2b_ipset_name` | Nome IPSet per IP bannati | `f2b-blacklist` | +| `f2b_unban_ip` | IP da sbloccare | `""` | > All `clustered` and `pmxcfs_running` checks default to `false` to prevent > errors on non-clustered or single-node setups. @@ -68,7 +68,11 @@ ansible-playbook -i inventory fail2ban-proxmox.yml ### 2. List current banned IPs ```bash -ansible-playbook -i inventory fail2ban-proxmox.yml -e "f2b_ipset_name=fail2ban" -t list_banned +ansible-playbook \ + -i inventory \ + fail2ban-proxmox.yml \ + -e "f2b_ipset_name=fail2ban" \ + -t list_banned ``` ### 3. Unban a specific IP