237 lines
7.1 KiB
YAML
237 lines
7.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
|