2025-12-02 22:47:52 -05:00
2025-12-02 22:35:27 -05:00
2025-12-02 22:35:27 -05:00
2025-12-02 22:35:27 -05:00
2025-12-02 22:47:52 -05:00
2025-12-02 22:35:27 -05:00
2025-12-02 22:35:27 -05:00
2025-12-02 22:35:27 -05:00
2025-12-02 22:35:27 -05:00
2025-12-02 19:32:11 -08:00
2025-12-02 19:44:28 -08:00

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):

# 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:

# 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

Create custom jails by defining them as dictionaries. This method uses a Jinja2 template to generate jail files:

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:

# 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:

# 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):

[Definition]
failregex = ^<HOST> .* "POST /login HTTP/.*" 401
            ^Authentication failure for .* from <HOST>
ignoreregex =

Copying Custom Actions

Define custom actions for specific ban/unban behaviors:

# 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 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 script.

Dependencies

None.

Example Playbook

- 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:

- 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

- 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

- 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:

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

- 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:

# 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:

fail2ban-regex /var/log/myapp.log /etc/fail2ban/filter.d/myapp.conf

License

MIT

Description
An Ansible role for installing and configuring fail2ban.
Readme MIT 38 KiB
Languages
Jinja 100%