From deaeb1f79e4eb51e9c159b1d9ceeca938e5cc7bc Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Mon, 8 Jun 2026 20:05:29 +0400 Subject: [PATCH 1/3] Prototype a role for installing `fail2ban` on RHEL 9+ --- roles/fail2ban_rhel/README.md | 49 +++++++++++++++++++ roles/fail2ban_rhel/defaults/main.yml | 13 +++++ roles/fail2ban_rhel/handlers/main.yml | 7 +++ roles/fail2ban_rhel/meta/main.yml | 19 +++++++ .../molecule/default/converge.yml | 5 ++ .../molecule/default/molecule.yml | 27 ++++++++++ .../fail2ban_rhel/molecule/default/verify.yml | 17 +++++++ roles/fail2ban_rhel/tasks/main.yml | 27 ++++++++++ roles/fail2ban_rhel/templates/sshd.conf.j2 | 14 ++++++ 9 files changed, 178 insertions(+) create mode 100644 roles/fail2ban_rhel/README.md create mode 100644 roles/fail2ban_rhel/defaults/main.yml create mode 100644 roles/fail2ban_rhel/handlers/main.yml create mode 100644 roles/fail2ban_rhel/meta/main.yml create mode 100644 roles/fail2ban_rhel/molecule/default/converge.yml create mode 100644 roles/fail2ban_rhel/molecule/default/molecule.yml create mode 100644 roles/fail2ban_rhel/molecule/default/verify.yml create mode 100644 roles/fail2ban_rhel/tasks/main.yml create mode 100644 roles/fail2ban_rhel/templates/sshd.conf.j2 diff --git a/roles/fail2ban_rhel/README.md b/roles/fail2ban_rhel/README.md new file mode 100644 index 0000000..6cc6367 --- /dev/null +++ b/roles/fail2ban_rhel/README.md @@ -0,0 +1,49 @@ +fail2ban_rhel +============= + +A small role to deploy `fail2ban` on RHEL/Rocky 9+ hosts. Currently supports only a templated `sshd` jail. + +Requirements +------------ + +- Target hosts must be RHEL-family or Rocky Linux 9+ + +Supported Platforms +------------------- + +- RHEL 9+ +- Rocky Linux 9+ + +Role Variables +-------------- + +sshd jail configuration + +- `fail2ban_rhel_jail_sshd`: + `enabled`: enable the sshd jail. Default: `true` + `port`: fail2ban port for sshd. Default: `ssh` + `logpath`: path to the SSH log file. Default: `/var/log/secure` + `findtime`: time window for fail2ban. Default: `600` + `maxretry`: maximum retry count. Default: `5` + `bantime`: ban duration in seconds. Default: `600` + `ignoreips`: list of IPs and CIDRs that fail2ban should ignore. Default: `127.0.0.1/8`, `::1` + +Behavior +-------- + +By default this role: + +- installs `epel-release` on RedHat-family systems when enabled +- installs `fail2ban` +- creates `/etc/fail2ban/jail.d/sshd.conf` from a template +- enables and starts the `fail2ban` systemd service + +Example Playbook +---------------- + +See: `molecule/default/converge.yml` + +License +------- + +MIT diff --git a/roles/fail2ban_rhel/defaults/main.yml b/roles/fail2ban_rhel/defaults/main.yml new file mode 100644 index 0000000..c24adcf --- /dev/null +++ b/roles/fail2ban_rhel/defaults/main.yml @@ -0,0 +1,13 @@ +--- +fail2ban_rhel_jail_sshd: + enabled: true + port: ssh + findtime: 600 + maxretry: 5 + bantime: 3600 + logpath: "%(sshd_log)s" + backend: "%(sshd_backend)s" + banaction: firewallcmd-multiport + ignoreips: + - 127.0.0.1/8 + - ::1 diff --git a/roles/fail2ban_rhel/handlers/main.yml b/roles/fail2ban_rhel/handlers/main.yml new file mode 100644 index 0000000..3c98952 --- /dev/null +++ b/roles/fail2ban_rhel/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: Restart fail2ban + ansible.builtin.systemd: + name: fail2ban + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/fail2ban_rhel/meta/main.yml b/roles/fail2ban_rhel/meta/main.yml new file mode 100644 index 0000000..a5c9466 --- /dev/null +++ b/roles/fail2ban_rhel/meta/main.yml @@ -0,0 +1,19 @@ +--- +galaxy_info: + role_name: fail2ban_rhel + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Deploy a lean Fail2Ban configuration for RHEL/Rocky 9+ hosts" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "EL" + versions: ["9"] + - name: "Rocky" + versions: ["9.2"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/fail2ban_rhel/molecule/default/converge.yml b/roles/fail2ban_rhel/molecule/default/converge.yml new file mode 100644 index 0000000..74a7f78 --- /dev/null +++ b/roles/fail2ban_rhel/molecule/default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.fail2ban_rhel diff --git a/roles/fail2ban_rhel/molecule/default/molecule.yml b/roles/fail2ban_rhel/molecule/default/molecule.yml new file mode 100644 index 0000000..4715644 --- /dev/null +++ b/roles/fail2ban_rhel/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: rocky9 + image: geerlingguy/docker-${MOLECULE_DISTRO:-rockylinux9}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/fail2ban_rhel/molecule/default/verify.yml b/roles/fail2ban_rhel/molecule/default/verify.yml new file mode 100644 index 0000000..b211c11 --- /dev/null +++ b/roles/fail2ban_rhel/molecule/default/verify.yml @@ -0,0 +1,17 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: "Check that the fail2ban service is active" + register: sshd_rhel_fail2ban_service + ansible.builtin.systemd: + name: fail2ban + + - name: "Assert fail2ban is running" + ansible.builtin.assert: + that: sshd_rhel_fail2ban_service.status.ActiveState == "active" + success_msg: "fail2ban service is running" + fail_msg: "fail2ban service is not active" diff --git a/roles/fail2ban_rhel/tasks/main.yml b/roles/fail2ban_rhel/tasks/main.yml new file mode 100644 index 0000000..b27a0ff --- /dev/null +++ b/roles/fail2ban_rhel/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- name: "Install EPEL release on RedHat-family systems" + ansible.builtin.dnf: + name: "epel-release" + state: present + +- name: "Install fail2ban" + ansible.builtin.package: + name: "fail2ban" + state: present + +- name: "Ensure Fail2Ban jail configuration directory exists" + ansible.builtin.file: + path: /etc/fail2ban/jail.d + state: directory + owner: root + group: root + mode: "0755" + +- name: "Render sshd jail configuration" + notify: Restart fail2ban + ansible.builtin.template: + src: sshd.conf.j2 + dest: /etc/fail2ban/jail.d/sshd.local + owner: root + group: root + mode: "0644" diff --git a/roles/fail2ban_rhel/templates/sshd.conf.j2 b/roles/fail2ban_rhel/templates/sshd.conf.j2 new file mode 100644 index 0000000..db8df22 --- /dev/null +++ b/roles/fail2ban_rhel/templates/sshd.conf.j2 @@ -0,0 +1,14 @@ +[sshd] +enabled = {{ fail2ban_rhel_jail_sshd.enabled | ternary('true', 'false') }} +port = {{ fail2ban_rhel_jail_sshd.port }} + +logpath = {{ fail2ban_rhel_jail_sshd.logpath }} + +backend = {{ fail2ban_rhel_jail_sshd.backend }} +banaction = {{ fail2ban_rhel_jail_sshd.banaction }} + +findtime = {{ fail2ban_rhel_jail_sshd.findtime }} +maxretry = {{ fail2ban_rhel_jail_sshd.maxretry }} +bantime = {{ fail2ban_rhel_jail_sshd.bantime }} + +ignoreip = {{ fail2ban_rhel.ignoreips | default([]) | join(' ') }} From 870dbac530ca14d74887973d3cd6c4cfcaa109e0 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Mon, 8 Jun 2026 20:10:35 +0400 Subject: [PATCH 2/3] Fix an incorrect variable name --- roles/fail2ban_rhel/molecule/default/verify.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/fail2ban_rhel/molecule/default/verify.yml b/roles/fail2ban_rhel/molecule/default/verify.yml index b211c11..05ea64b 100644 --- a/roles/fail2ban_rhel/molecule/default/verify.yml +++ b/roles/fail2ban_rhel/molecule/default/verify.yml @@ -6,12 +6,12 @@ tasks: - name: "Check that the fail2ban service is active" - register: sshd_rhel_fail2ban_service + register: fail2ban_rhel_fail2ban_service ansible.builtin.systemd: name: fail2ban - name: "Assert fail2ban is running" ansible.builtin.assert: - that: sshd_rhel_fail2ban_service.status.ActiveState == "active" + that: fail2ban_rhel_fail2ban_service.status.ActiveState == "active" success_msg: "fail2ban service is running" fail_msg: "fail2ban service is not active" From bb291e438ac7394953066217e43199ee87162290 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Tue, 9 Jun 2026 20:19:22 +0400 Subject: [PATCH 3/3] Fix jail options not being configurable with default values individually --- roles/fail2ban_rhel/defaults/main.yml | 23 +++++++++++------------ roles/fail2ban_rhel/vars/main.yml | 10 ++++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 roles/fail2ban_rhel/vars/main.yml diff --git a/roles/fail2ban_rhel/defaults/main.yml b/roles/fail2ban_rhel/defaults/main.yml index c24adcf..0f673c5 100644 --- a/roles/fail2ban_rhel/defaults/main.yml +++ b/roles/fail2ban_rhel/defaults/main.yml @@ -1,13 +1,12 @@ --- -fail2ban_rhel_jail_sshd: - enabled: true - port: ssh - findtime: 600 - maxretry: 5 - bantime: 3600 - logpath: "%(sshd_log)s" - backend: "%(sshd_backend)s" - banaction: firewallcmd-multiport - ignoreips: - - 127.0.0.1/8 - - ::1 +fail2ban_rhel_jail_sshd_enabled: true +fail2ban_rhel_jail_sshd_port: ssh +fail2ban_rhel_jail_sshd_findtime: 600 +fail2ban_rhel_jail_sshd_maxretry: 5 +fail2ban_rhel_jail_sshd_bantime: 3600 +fail2ban_rhel_jail_sshd_logpath: "%(sshd_log)s" +fail2ban_rhel_jail_sshd_backend: "%(sshd_backend)s" +fail2ban_rhel_jail_sshd_banaction: firewallcmd-multiport +fail2ban_rhel_jail_sshd_ignoreips: + - 127.0.0.1/8 + - ::1 diff --git a/roles/fail2ban_rhel/vars/main.yml b/roles/fail2ban_rhel/vars/main.yml new file mode 100644 index 0000000..ad0c9d9 --- /dev/null +++ b/roles/fail2ban_rhel/vars/main.yml @@ -0,0 +1,10 @@ +fail2ban_rhel_jail_sshd: + enabled: "{{ fail2ban_rhel_jail_sshd_enabled }}" + port: "{{ fail2ban_rhel_jail_sshd_port }}" + findtime: "{{ fail2ban_rhel_jail_sshd_findtime }}" + maxretry: "{{ fail2ban_rhel_jail_sshd_maxretry }}" + bantime: "{{ fail2ban_rhel_jail_sshd_bantime }}" + logpath: "{{ fail2ban_rhel_jail_sshd_logpath }}" + backend: "{{ fail2ban_rhel_jail_sshd_backend }}" + banaction: "{{ fail2ban_rhel_jail_sshd_banaction }}" + ignoreips: "{{ fail2ban_rhel_jail_sshd_ignoreips }}"