# Ansible Role: fail2ban An Ansible role for installing and configuring fail2ban on Linux systems. This role provides flexible configuration options for fail2ban's main settings, jail configurations, and custom filters/actions. ## Requirements - Ansible 2.9 or higher - Target systems: Debian/Ubuntu or RHEL-based distributions - Python 3 on target hosts ## Role Variables ### Default Variables Available variables are listed below, along with default values (see `defaults/main.yaml`): ```yaml # Packages to install fail2ban_dependencies: - fail2ban - python3-systemd # Service configuration fail2ban_service: fail2ban fail2ban_loglevel: INFO fail2ban_logtarget: /var/log/fail2ban.log # Default jail settings fail2ban_ignoreself: "true" fail2ban_ignoreips: - 127.0.0.1/8 - ::1 fail2ban_bantime: 10m fail2ban_findtime: 10m fail2ban_maxretry: 5 fail2ban_backend: auto ``` ### Important Default Settings to Configure Before deploying to production, consider adjusting these critical settings: 1. **IP Whitelist** (`fail2ban_ignoreips`): Add your management IPs to prevent lockouts 2. **Ban Duration** (`fail2ban_bantime`): Default is 10 minutes; consider longer for production (1h, 24h, 1w) 3. **Max Retry** (`fail2ban_maxretry`): Default is 5 attempts; lower for stricter security 4. **Find Time Window** (`fail2ban_findtime`): Default is 10 minutes; adjust based on your threat model 5. **Backend** (`fail2ban_backend`): Set to `systemd` for systemd-based distros for better performance ### Custom Configuration Variables #### Using the INI Module Format You can add custom configuration to `fail2ban.local` and `jail.local` by defining options in the INI module format: ```yaml # Additional fail2ban.local configuration fail2ban_configuration: [] - option: loglevel # The INI option name value: "DEBUG" # The value to set section: Definition # The INI section # Additional jail.local configuration fail2ban_jail_configuration: - option: destemail value: "admin@example.com" section: DEFAULT - option: sender value: "fail2ban@example.com" section: DEFAULT - option: action value: "%(action_mwl)s" section: DEFAULT ``` **INI Format Explanation:** - `section`: The configuration section in the INI file (e.g., `DEFAULT`, `Definition`, `sshd`) - `option`: The configuration parameter name - `value`: The value to assign (must be a string, number, or boolean) This format allows you to set any fail2ban configuration option without modifying the role itself. ### Creating Custom Jails #### Method 1: Define Jails as Dictionaries (Recommended for Simple Jails) Create custom jails by defining them as dictionaries. This method uses a Jinja2 template to generate jail files: ```yaml fail2ban_jails: - name: sshd # Jail name (required) enabled: true # Enable/disable jail port: ssh # Service port(s) logpath: /var/log/auth.log # Log file to monitor maxretry: 3 # Override default maxretry bantime: 1h # Override default bantime findtime: 10m # Override default findtime banaction: iptables-multiport # Ban action to use filter: sshd # Filter to use ``` **Common jail parameters:** - `name`: Jail identifier (required) - `enabled`: true/false - `port`: Service port (ssh, http, https, etc.) - `filter`: Filter name (must exist in filter.d/) - `logpath`: Path to log file(s) to monitor - `maxretry`: Number of failures before ban - `findtime`: Time window for counting failures - `bantime`: Duration of ban - `banaction`: Action to execute on ban - `action`: Full action with parameters #### Method 2: Copy Complete Jail Files (For Complex Configurations) For more complex jail configurations that require specific options not supported by the template, copy complete jail files: ```yaml # Path to directory containing custom jail files fail2ban_jails_path: "{{ playbook_dir }}/files/fail2ban/jails" ``` Place your `.conf` or `.local` files in the specified directory. They will be copied to `/etc/fail2ban/jail.d/`. ### Custom Filters and Actions #### Copying Custom Filters Define custom filters for application-specific log patterns: ```yaml # Path to directory containing custom filter files fail2ban_filters_path: "{{ playbook_dir }}/files/fail2ban/filters" ``` Place your filter `.conf` files in the specified directory. They will be copied to `/etc/fail2ban/filter.d/`. **Example custom filter** (`files/fail2ban/filters/myapp.conf`): ```ini [Definition] failregex = ^ .* "POST /login HTTP/.*" 401 ^Authentication failure for .* from ignoreregex = ``` #### Copying Custom Actions Define custom actions for specific ban/unban behaviors: ```yaml # Path to directory containing custom action files fail2ban_actions_path: "files/fail2ban/actions" ``` Place your action `.conf` files in the specified directory. They will be copied to `/etc/fail2ban/action.d/`. To see an example of a custom action, I've included a [ufw-docker action](files/actions/ufw-docker.conf) with the role as I use it frequently. It is an enhanced UFW action that provides additional protection for Docker environments. To use it, just set `fail2ban_actions_path` to `actions`. > [!WARNING] > For this action to work properly, you'll need to configure UFW to work properly with Docker, either manually or using the [ufw-docker](https://github.com/chaifeng/ufw-docker) script. ## Dependencies None. ## Example Playbook ### Basic Usage with Recommended Defaults ```yaml - hosts: servers become: yes roles: - role: fail2ban vars: # Add your management IPs to prevent lockouts fail2ban_ignoreips: - 127.0.0.1/8 - ::1 - 10.0.0.0/8 # Your private network # Stricter default settings fail2ban_bantime: 1h fail2ban_maxretry: 3 fail2ban_backend: systemd ``` ### Custom Configuration with INI Format The role uses the `ini_file` module to edit the default configuration files. You may override or add to this configuration: ```yaml - hosts: servers become: yes roles: - role: fail2ban vars: # Override fail2ban.conf base settings using INI format fail2ban_configuration: - option: loglevel value: "NOTICE" section: Definition - option: logtarget value: "SYSLOG" section: Definition # Configure jail.conf email notifications fail2ban_jail_configuration: - option: destemail value: "security@example.com" section: DEFAULT - option: sender value: "fail2ban@example.com" section: DEFAULT - option: action value: "%(action_mwl)s" # Mail with logs section: DEFAULT ``` ### Creating Custom Jails with Dictionaries ```yaml - hosts: servers become: yes roles: - role: fail2ban vars: fail2ban_bantime: 1h fail2ban_maxretry: 3 fail2ban_jails: # SSH protection - name: sshd enabled: true port: ssh logpath: /var/log/auth.log maxretry: 3 bantime: 24h # Nginx authentication failures - name: nginx-http-auth enabled: true port: http,https logpath: /var/log/nginx/error.log # Recidive jail for repeat offenders - name: recidive enabled: true maxretry: 3 findtime: 1w bantime: 30d logpath: /var/log/fail2ban.log ``` ### Using Complete Jail Files for Complex Configurations ```yaml - hosts: servers become: yes roles: - role: fail2ban vars: # Use dictionaries for simple jails fail2ban_jails: - name: sshd enabled: true maxretry: 3 # For complex jails, copy complete jail files fail2ban_jails_path: "{{ playbook_dir }}/files/fail2ban/jails" ``` ## Debian-Specific Configuration ### SSH Service Name Issue On Debian systems, there's a common issue where fail2ban's default SSH jail doesn't work out of the box. The problem: - fail2ban's default configuration expects the service to be named `sshd` - On many Debian systems, the actual systemd service is named `ssh.service` (not `sshd.service`) - When using the `systemd` backend, fail2ban looks for logs from the `sshd` service and finds nothing - Result: **SSH protection silently fails** - no errors, but no banning occurs ### The Solution This role provides the `fail2ban_default_debian_jail_configuration` variable to override Debian's default jail settings: ```yaml fail2ban_default_debian_jail_configuration: - option: backend value: systemd section: sshd - option: journalmatch value: "_SYSTEMD_UNIT=ssh.service" # Use ssh.service instead of sshd.service section: sshd ``` This configuration: - Explicitly sets the backend to `systemd` for better performance - Corrects the journal match to look for `ssh.service` instead of the default `sshd.service` - Is applied only on Debian systems (via `when: ansible_facts['distribution'] == 'Debian'`) ### Usage Example ```yaml - hosts: debian_servers become: yes roles: - role: fail2ban vars: fail2ban_backend: systemd # Fix SSH jail on Debian fail2ban_default_debian_jail_configuration: - option: backend value: systemd section: sshd - option: journalmatch value: "_SYSTEMD_UNIT=ssh.service" section: sshd fail2ban_jails: - name: sshd enabled: true maxretry: 3 bantime: 1h ``` > [!NOTE] > This only affects the default SSH jail in `/etc/fail2ban/jail.d/defaults-debian.conf`. If you're defining custom SSH jails using `fail2ban_jails`, make sure to specify the correct `journalmatch` or `logpath` for your system. ### Verifying SSH Protection is Working After applying the role, verify fail2ban is monitoring SSH: ```bash # Check if the sshd jail is active sudo fail2ban-client status sshd # Check what logs fail2ban is monitoring sudo fail2ban-client get sshd logpath # Test with a failed login and check sudo fail2ban-client status sshd ``` If you see "Currently banned: 0" and "Total banned: 0" after several failed login attempts, the jail may not be configured correctly for your system's SSH service name. ## Role Tasks This role performs the following tasks: 1. Installs fail2ban and required dependencies 2. Configures `fail2ban.local` with base and custom settings 3. Configures `jail.local` with default jail parameters 4. Configures Debian-specific jail settings (when applicable) 5. Copies custom filter configurations (if provided) 6. Copies custom action configurations (if provided) 7. Copies or generates jail configurations 8. Starts and enables the fail2ban service ## Handlers - `Restart fail2ban`: Restarts the fail2ban service when configuration changes are made ## File Organization Organize your custom files in the following structure: ``` playbook_directory/ ├── files/ │ └── fail2ban/ │ ├── filters/ │ │ ├── myapp.conf │ │ └── custom-filter.conf │ ├── actions/ │ │ ├── slack-notify.conf │ │ └── custom-action.conf │ └── jails/ │ └── complex-jail.local ├── roles/ │ └── fail2ban/ └── playbook.yml ``` ## Tips and Best Practices 1. **Always whitelist management IPs** in `fail2ban_ignoreips` to prevent lockouts 2. **Start with lenient settings** (higher maxretry, shorter bantime) and adjust based on logs 3. **Use systemd backend** when available for better performance: `fail2ban_backend: systemd` 4. **Monitor fail2ban logs** at `/var/log/fail2ban.log` to tune your rules 5. **Test custom filters** before deploying to production using `fail2ban-regex` 6. **Use dictionaries for simple jails**, copy files for complex ones requiring specific options 7. **Keep custom filters and actions in version control** alongside your playbooks ## Testing Custom Filters Test your custom filters before deployment: ```bash fail2ban-regex /var/log/myapp.log /etc/fail2ban/filter.d/myapp.conf ``` ## License MIT