From deaeb1f79e4eb51e9c159b1d9ceeca938e5cc7bc Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Mon, 8 Jun 2026 20:05:29 +0400 Subject: [PATCH] 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(' ') }}