Compare commits
9 Commits
94bcbbac5b
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| ded4fb8270 | |||
| 38831f981a | |||
| d2761bd840 | |||
| c8fb6e4c80 | |||
| 54f3f761c8 | |||
| 3054a97d15 | |||
| 752db2b57f | |||
| 0004d2bd2d | |||
| e44f757b9e |
@@ -1,6 +1,8 @@
|
|||||||
# Fail2Ban Integration with Proxmox Firewall
|
# 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
|
- SSH protection
|
||||||
- Proxmox GUI / AD login protection
|
- Proxmox GUI / AD login protection
|
||||||
- Progressive ban escalation (recidive jail)
|
- 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).
|
- Ensures safe firewall updates without affecting Corosync ports (5404/5405).
|
||||||
- Supports single-node Fail2Ban using `iptables-multiport`.
|
- Supports single-node Fail2Ban using `iptables-multiport`.
|
||||||
- Enables and starts the Fail2Ban service.
|
- Enables and starts the Fail2Ban service.
|
||||||
@@ -32,23 +35,25 @@ This Ansible playbook deploys and configures **Fail2Ban** on a Proxmox VE enviro
|
|||||||
|
|
||||||
## Variables
|
## 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 |
|
| Variable | Description | Default |
|
||||||
|----------|-------------|----------------|
|
|-------------------------|---------------------------------|-----------------|
|
||||||
| `f2b_bantime` | Default ban time for repeated failures | e.g., `600s` |
|
| `f2b_bantime` | Ban per tentativi falliti | `600s` |
|
||||||
| `f2b_findtime` | Time window to check failures | e.g., `1200s`|
|
| `f2b_findtime` | Finestra per contare fallimenti | `1200s` |
|
||||||
| `f2b_maxretry` | Maximum retries before ban | e.g., `5` |
|
| `f2b_maxretry` | Tentativi prima del ban | `5` |
|
||||||
| `f2b_bantime_increment` | Incremental ban time (recidive) | e.g., `true` |
|
| `f2b_bantime_increment` | Abilita ban incrementale | `true` |
|
||||||
| `f2b_bantime_factor` | Factor for incremental ban | e.g., `2` |
|
| `f2b_bantime_factor` | Fattore aumento ban | `2` |
|
||||||
| `f2b_bantime_max` | Maximum ban time | e.g., `7d` |
|
| `f2b_bantime_max` | Durata massima del ban | `7d` |
|
||||||
| `f2b_recidive_bantime` | Ban time for recidive jail | e.g., `3600` |
|
| `f2b_recidive_bantime` | Ban per recidiva | `3600` |
|
||||||
| `f2b_recidive_findtime` | Findtime for recidive jail | e.g., `86400` |
|
| `f2b_recidive_findtime` | Finestra recidiva | `86400` |
|
||||||
| `f2b_recidive_maxretry` | Max retry for recidive jail | e.g., `3` |
|
| `f2b_recidive_maxretry` | Tentativi recidiva | `3` |
|
||||||
| `f2b_ipset_name` | Name of Proxmox IPSet used for banned IPs | e.g., `f2b-blacklist` |
|
| `f2b_ipset_name` | Nome IPSet per IP bannati | `f2b-blacklist` |
|
||||||
| `f2b_unban_ip` | Optional IP to unban manually | Leave undefined if not needed |
|
| `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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -63,7 +68,11 @@ ansible-playbook -i inventory fail2ban-proxmox.yml
|
|||||||
### 2. List current banned IPs
|
### 2. List current banned IPs
|
||||||
|
|
||||||
```bash
|
```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
|
### 3. Unban a specific IP
|
||||||
@@ -75,20 +84,27 @@ ansible-playbook -i inventory fail2ban-proxmox.yml -e "f2b_unban_ip=1.2.3.4"
|
|||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
- Detects Proxmox – ensures the playbook runs only on Proxmox VE hosts.
|
- Detects Proxmox – ensures the playbook runs only on Proxmox VE hosts.
|
||||||
- Cluster safety checks – verifies /etc/pve/.members and corosync.conf for quorum.
|
- Cluster safety checks – verifies /etc/pve/.members and corosync.conf
|
||||||
- Installs Fail2Ban – ensures /etc/fail2ban/jail.local exists and applies configuration.
|
for quorum.
|
||||||
- Cluster-aware action – for clustered nodes, Fail2Ban bans are added to Proxmox firewall and compiled immediately (pve-firewall compile).
|
- Installs Fail2Ban – ensures /etc/fail2ban/jail.local exists and applies
|
||||||
- Single-node fallback – uses iptables-multiport for nodes not in a cluster.
|
configuration.
|
||||||
- Corosync protection – prevents firewall rules from dropping cluster communication ports (5404/5405).
|
- 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
|
## Notes & Safety
|
||||||
|
|
||||||
- The playbook does not copy jail.conf, only manages jail.local.
|
- The playbook does not copy jail.conf, only manages jail.local.
|
||||||
- Firewall rules for clustered nodes are only modified if quorum exists.
|
- 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.
|
- 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
|
## License
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#################################################
|
#################################################
|
||||||
|
|
||||||
- name: fail2ban | Detect Proxmox
|
- name: fail2ban | Detect Proxmox
|
||||||
stat:
|
ansible.builtin.stat:
|
||||||
path: /usr/bin/pveversion
|
path: /usr/bin/pveversion
|
||||||
register: pve_installed
|
register: pve_installed
|
||||||
|
|
||||||
@@ -80,7 +80,6 @@
|
|||||||
- name: fail2ban | Configure Fail2Ban jails
|
- name: fail2ban | Configure Fail2Ban jails
|
||||||
ansible.builtin.blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
dest: /etc/fail2ban/jail.local
|
dest: /etc/fail2ban/jail.local
|
||||||
create: true
|
|
||||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - PROXMOX"
|
marker: "# {mark} ANSIBLE MANAGED BLOCK - PROXMOX"
|
||||||
block: |
|
block: |
|
||||||
# jail.conf (default)
|
# jail.conf (default)
|
||||||
@@ -95,7 +94,6 @@
|
|||||||
backend = systemd
|
backend = systemd
|
||||||
banaction = {% if (clustered.stat.exists | default(false)) %} proxmox-fw{% else %} iptables-multiport{% endif %}
|
banaction = {% if (clustered.stat.exists | default(false)) %} proxmox-fw{% else %} iptables-multiport{% endif %}
|
||||||
ignoreip = 127.0.0.1/8 192.168.2.0/24
|
ignoreip = 127.0.0.1/8 192.168.2.0/24
|
||||||
# {% if pmxcfs_running.stat.exists %} {{ corosync_networks | join(' ') }}{% endif %}
|
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
# SSH
|
# SSH
|
||||||
@@ -154,10 +152,16 @@
|
|||||||
{{
|
{{
|
||||||
'/etc/pve/firewall/cluster.fw'
|
'/etc/pve/firewall/cluster.fw'
|
||||||
if clustered.stat.exists
|
if clustered.stat.exists
|
||||||
else '/etc/pve/nodes/' + pve_node + '.fw'
|
else '/etc/pve/nodes/' + pve_node + '/host.fw'
|
||||||
}}
|
}}
|
||||||
when: pve_installed.stat.exists | default(false)
|
when: pve_installed.stat.exists | default(false)
|
||||||
|
|
||||||
|
- name: fail2ban | Show firewall config path
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >
|
||||||
|
WARNING: Proxmox firewall config path is: {{ pve_firewall_config}}
|
||||||
|
when: pve_firewall_config is defined
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
# Detect firewall configuration
|
# Detect firewall configuration
|
||||||
#################################################
|
#################################################
|
||||||
@@ -174,15 +178,19 @@
|
|||||||
register: fw_content
|
register: fw_content
|
||||||
when: fw_stat.stat.exists | default(false)
|
when: fw_stat.stat.exists | default(false)
|
||||||
|
|
||||||
|
- name: fail2ban | Debug config contents
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >
|
||||||
|
{{ fw_content }}
|
||||||
|
when: fw_stat.stat.exists | default(false)
|
||||||
|
|
||||||
- name: fail2ban | Determine if firewall enabled
|
- name: fail2ban | Determine if firewall enabled
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
pve_firewall_enabled: >-
|
pve_firewall_enabled: >-
|
||||||
{{
|
{{
|
||||||
(fw_stat.stat.exists | default(false)) and
|
fw_stat.stat.exists and
|
||||||
(
|
(fw_content.content | b64decode)
|
||||||
(fw_content.content | default('') | b64decode)
|
is search('^enable:\s*1$', multiline=True)
|
||||||
is search('enable:\s*1')
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
- name: fail2ban | Warn if firewall not enabled
|
- name: fail2ban | Warn if firewall not enabled
|
||||||
@@ -238,7 +246,7 @@
|
|||||||
# Deploy cluster-aware Fail2Ban action
|
# Deploy cluster-aware Fail2Ban action
|
||||||
#################################################
|
#################################################
|
||||||
|
|
||||||
- name: fail2ban-fw | Deploy proxmox-fw action
|
- name: fail2ban | Deploy proxmox-fw action
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: /etc/fail2ban/action.d/proxmox-fw.conf
|
dest: /etc/fail2ban/action.d/proxmox-fw.conf
|
||||||
owner: root
|
owner: root
|
||||||
@@ -266,7 +274,7 @@
|
|||||||
actionstart =
|
actionstart =
|
||||||
actionstop =
|
actionstop =
|
||||||
when:
|
when:
|
||||||
- clustered.stat.exists | default(false)
|
- clustered.stat.exists | default(false)
|
||||||
notify:
|
notify:
|
||||||
- Restart fail2ban
|
- Restart fail2ban
|
||||||
|
|
||||||
@@ -280,35 +288,35 @@
|
|||||||
enabled: true
|
enabled: true
|
||||||
state: started
|
state: started
|
||||||
|
|
||||||
#################################################
|
# #################################################
|
||||||
# List banned IPs cluster-wide
|
# # List banned IPs cluster-wide
|
||||||
#################################################
|
# #################################################
|
||||||
|
|
||||||
- name: fail2ban | Get banned IPs from Proxmox IPSet
|
# - name: fail2ban | Get banned IPs from Proxmox IPSet
|
||||||
ansible.builtin.command: pve-firewall ipset list {{ f2b_ipset_name }}
|
# ansible.builtin.command: pve-firewall ipset list {{ f2b_ipset_name }}
|
||||||
register: banned_ips
|
# register: banned_ips
|
||||||
changed_when: false
|
# changed_when: false
|
||||||
failed_when: false
|
# failed_when: false
|
||||||
|
|
||||||
- name: fail2ban | Show banned IPs
|
# - name: fail2ban | Show banned IPs
|
||||||
ansible.builtin.debug:
|
# ansible.builtin.debug:
|
||||||
msg: >
|
# msg: >
|
||||||
Current banned IPs (cluster-wide):
|
# Current banned IPs (cluster-wide):
|
||||||
{{ banned_ips.stdout_lines | default([]) }}
|
# {{ banned_ips.stdout_lines | default([]) }}
|
||||||
|
|
||||||
#################################################
|
# #################################################
|
||||||
# Manual unban
|
# # Manual unban
|
||||||
#################################################
|
# #################################################
|
||||||
|
|
||||||
- name: fail2ban | Unban specific IP
|
# - name: fail2ban | Unban specific IP
|
||||||
ansible.builtin.command: >
|
# ansible.builtin.command: >
|
||||||
pve-firewall ipset del {{ f2b_ipset_name }} {{ f2b_unban_ip }}
|
# pve-firewall ipset del {{ f2b_ipset_name }} {{ f2b_unban_ip }}
|
||||||
when: f2b_unban_ip is defined and f2b_unban_ip | length > 0
|
# when: f2b_unban_ip is defined and f2b_unban_ip | length > 0
|
||||||
register: unban_result
|
# register: unban_result
|
||||||
changed_when: "'removed' in unban_result.stdout or unban_result.rc == 0"
|
# changed_when: "'removed' in unban_result.stdout or unban_result.rc == 0"
|
||||||
failed_when: false
|
# failed_when: false
|
||||||
|
|
||||||
- name: fail2ban | Report unban result
|
# - name: fail2ban | Report unban result
|
||||||
ansible.builtin.debug:
|
# ansible.builtin.debug:
|
||||||
msg: "Unbanned IP {{ f2b_unban_ip }}"
|
# msg: "Unbanned IP {{ f2b_unban_ip }}"
|
||||||
when: f2b_unban_ip | length > 0
|
# when: f2b_unban_ip | length > 0
|
||||||
|
|||||||
Reference in New Issue
Block a user