Ansible collection: foundata.fail2ban

Source code

Documentation

Licensing

Miscellaneous

The ansible collection foundata.fail2ban provides resources to manage fail2ban, a daemon to ban hosts that cause multiple authentication errors.

The following lists the README.md files of the most important included contents (e.g. roles). You might browse the source code repositories for a better overview.

Ansible role: foundata.fail2ban.run

The foundata.fail2ban.run Ansible role (part of the foundata.fail2ban Ansible collection). It provides automated management of fail2ban configuration and service state.

Features

  • Custom filters and actions:
    • Define custom fail2ban filters and actions as simple YAML dictionaries or raw content.
  • Sane defaults (see __run_fail2ban_jail_settings_defaults in ./vars/main.yml for a complete list)
    • Platform-aware ban actions (nftables, firewalld, ufw) and log backends (systemd journal).
    • Built-in sshd jail enabled out of the box.
  • Preserve distribution defaults by managing only drop-in configuration files in /etc/fail2ban/fail2ban.d/ and /etc/fail2ban/jail.d/, ensuring compatibility across a wide range of distributions.
  • Simple configuration with a layered merge system:
    • Internal defaults, platform-specific overrides, and user settings are merged automatically.
    • User-provided values always take highest priority.

Example playbooks, using this role

Installation with automatic upgrade:

---

- name: "Initialize the foundata.fail2ban.run role"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.fail2ban.run role"
      ansible.builtin.include_role:
        name: "foundata.fail2ban.run"
      vars:
        run_fail2ban_autoupgrade: true

Uninstall:

---

- name: "Initialize the foundata.fail2ban.run role"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.fail2ban.run role"
      ansible.builtin.include_role:
        name: "foundata.fail2ban.run"
      vars:
        run_fail2ban_state: "absent"

Installation with custom jail settings (overriding defaults) and a custom filter:

---

- name: "Initialize the foundata.fail2ban.run role"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.fail2ban.run role"
      ansible.builtin.include_role:
        name: "foundata.fail2ban.run"
      vars:
        run_fail2ban_jail_settings:
          DEFAULT:
            bantime: "1h"
            findtime: "30m"
            maxretry: 5
            destemail: "admin@example.com"
          sshd:
            maxretry: 3
        run_fail2ban_custom_filters:
          my-app:
            Definition:
              failregex: '^Authentication failure for .* from <HOST>$'

Installation with a custom action using raw content:

---

- name: "Initialize the foundata.fail2ban.run role"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.fail2ban.run role"
      ansible.builtin.include_role:
        name: "foundata.fail2ban.run"
      vars:
        run_fail2ban_jail_settings:
          DEFAULT:
            bantime: "15m"
          my-app:
            enabled: true
            filter: "my-app"
            logpath: "/var/log/my-app/access.log"
            backend: "auto"
            maxretry: 5
            action: "notify-slack"
        run_fail2ban_custom_actions:
          notify-slack:
            content: |
              [Definition]
              actionban = curl -X POST -H 'Content-type: application/json' --data '{"text":"Banned <ip>"}' https://hooks.slack.example.com/services/xxx
              actionunban = curl -X POST -H 'Content-type: application/json' --data '{"text":"Unbanned <ip>"}' https://hooks.slack.example.com/services/xxx

Supported tags

It might be useful and faster to only call parts of the role by using tags:

  • run_fail2ban_setup: Manage basic resources, such as packages or service users.
  • run_fail2ban_config: Manage settings, such as adapting or creating configuration files.
  • run_fail2ban_service: Manage services and daemons, such as running states and service boot configurations.

There are also tags usually not meant to be called directly but listed for the sake of completeness** and edge cases:

  • run_fail2ban_always, always: Tasks needed by the role itself for internal role setup and the Ansible environment.

Role variables

The following variables can be configured for this role:

VariableTypeRequiredDefaultDescription (abstract)
run_fail2ban_statestrNo"present"Determines whether the managed resources should be present or absent.

present ensures that required components, such as software packages, are installed and configured.

absent reverts changes as much as possible, such as …
run_fail2ban_autoupgradeboolNofalseIf set to true, all managed packages will be upgraded during each Ansible run (e.g., when the package provider detects a newer version than the currently installed one).
run_fail2ban_service_statestrNo"enabled"Defines the status of the service(s).

enabled: Service is running and will start automatically at boot.

disabled: Service is stopped and will not start automatically at boot.

running Service is running but will not start …
run_fail2ban_service_settingsdictNo{}Fail2ban service configuration values.

These settings are placed in a drop-in configuration file in /fail2ban.d. They take highest priority, overriding internal defaults (see __run_fail2ban_service_settings_defaults in vars/main.yml) and …
run_fail2ban_config_service_dropin_file_namestrNo"99-managed.local"Filename of the drop-in configuration file to be placed in /fail2ban.d. Defaults to 99-managed.local. The 99- prefix in combination with the .local extension ensures later loading and thus higher precedence over files with lower-numbered …
run_fail2ban_jail_settingsdictNo{}Fail2ban jail configuration values.

These settings are placed in a drop-in configuration file in /jail.d. They take highest priority, overriding internal defaults (see __run_fail2ban_jail_settings_defaults in vars/main.yml) and …
run_fail2ban_config_jail_dropin_file_namestrNo"99-managed.local"Filename of the drop-in configuration file to be placed in /jail.d. Defaults to 99-managed.local. The 99- prefix in combination with the .local extension ensures later loading and thus higher precedence over files with lower-numbered prefixes …
run_fail2ban_custom_filtersdictNo{}Custom filter definitions to be placed in /filter.d/. Filters define the regex patterns used to detect intrusion attempts in log files.

Use filter names as top-level YAML keys (will create filter.d/.conf) Use section names as 2nd-level …
run_fail2ban_custom_actionsdictNo{}Custom action definitions to be placed in /action.d/. Actions define what happens when an IP is banned or unbanned (e.g., firewall rules, notifications).

Use action names as top-level YAML keys (will create action.d/.conf) Use section …

run_fail2ban_state

⇑ Back to ToC ⇑

Determines whether the managed resources should be present or absent.

present ensures that required components, such as software packages, are installed and configured.

absent reverts changes as much as possible, such as removing packages, deleting created users, stopping services, restoring modified settings, …

  • Type: str
  • Required: No
  • Default: "present"
  • Choices: present, absent

run_fail2ban_autoupgrade

⇑ Back to ToC ⇑

If set to true, all managed packages will be upgraded during each Ansible run (e.g., when the package provider detects a newer version than the currently installed one).

  • Type: bool
  • Required: No
  • Default: false

run_fail2ban_service_state

⇑ Back to ToC ⇑

Defines the status of the service(s).

enabled: Service is running and will start automatically at boot.

disabled: Service is stopped and will not start automatically at boot.

running Service is running but will not start automatically at boot. This can be used to start a service on the first Ansible run without enabling it for boot.

unmanaged: Service will not start at boot, and Ansible will not manage its running state. This is primarily useful when services are monitored and managed by systems other than Ansible.

The singular form (service) is used for simplicity. However, the defined status applies to all services if multiple are being managed by this role.

  • Type: str
  • Required: No
  • Default: "enabled"
  • Choices: enabled, disabled, running, unmanaged

run_fail2ban_service_settings

⇑ Back to ToC ⇑

Fail2ban service configuration values.

These settings are placed in a drop-in configuration file in <config dir>/fail2ban.d. They take highest priority, overriding internal defaults (see __run_fail2ban_service_settings_defaults in vars/main.yml) and platform-specific overrides.

Use standard Fail2ban section names as top-level YAML keys, and place their corresponding option names and values inside each section. This YAML structure is directly translated into Fail2ban’s INI format, preserving section names and option names exactly as written:

  • Each INI section ([SECTION]) becomes a YAML mapping key.
  • Each INI option inside that section becomes a YAML key-value pair.

Special cases:

  • For boolean values, use true/false (these will be converted to strings by the role as needed).
  • For multiline values, use normal YAML block scalars (|). The role will automatically handle indentation:
    logpath: |
      /var/log/auth.log
      /var/log/secure
    
  • Settings in Fail2ban’s DEFAULT section are getting applied to all parts of the configuration unless overridden in individual section definitions.

Example:

DEFAULT:
  loglevel: "INFO"
  logtarget: "/var/log/fail2ban.log"
Thread:
  stacksize: 0

will result in

[DEFAULT]
loglevel = INFO
logtarget = /var/log/fail2ban.log

[Thread]
stacksize = 0

The official documentation provides general configuration guidance:

The unmodified default configuration is already a good starting point and usually doesn’t require changes when using common services like SSH or NGINX. If empty dict (default), only the internal defaults and any platform-specific overrides apply.

  • Type: dict
  • Required: No
  • Default: {}

run_fail2ban_config_service_dropin_file_name

⇑ Back to ToC ⇑

Filename of the drop-in configuration file to be placed in <config dir>/fail2ban.d. Defaults to 99-managed.local. The 99- prefix in combination with the .local extension ensures later loading and thus higher precedence over files with lower-numbered prefixes or .conf extension (cf. man jail.conf).

If a non-default filename is used, any existing <config dir>/fail2ban.d/99-managed.local from previous Ansible runs will be removed automatically to prevent conflicts.

  • Type: str
  • Required: No
  • Default: "99-managed.local"

run_fail2ban_jail_settings

⇑ Back to ToC ⇑

Fail2ban jail configuration values.

These settings are placed in a drop-in configuration file in <config dir>/jail.d. They take highest priority, overriding internal defaults (see __run_fail2ban_jail_settings_defaults in vars/main.yml) and platform-specific overrides.

Use standard Fail2ban section names as top-level YAML keys, and place their corresponding option names and values inside each section. This YAML structure is directly translated into Fail2ban’s INI format, preserving section names and option names exactly as written:

  • Each INI section ([SECTION]) becomes a YAML mapping key.
  • Each INI option inside that section becomes a YAML key-value pair.

Special cases:

  • For boolean values, use true/false (these will be converted to strings by the role as needed).
  • For multiline values, use normal YAML block scalars (|). The role will automatically handle indentation:
    logpath: |
      /var/log/auth.log
      /var/log/secure
    
  • Settings in Fail2ban’s DEFAULT section are getting applied to all jails unless overridden in individual jail definitions.

Example:

DEFAULT:
  enabled: false
  maxretry: 3
  ignoreself: true
  ignoreip: "127.0.0.1/8 ::1 192.168.0.0/16 10.0.0.0/8 my-trusted-host.example.com"
sshd:
  enabled: true
nginx-http-auth:
  enabled: true
  port: "http,https"
  logpath: "/var/log/nginx/error.log"

will result in

[DEFAULT]
enabled = false
maxretry = 3
ignoreself = true
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16 10.0.0.0/8 my-trusted-host.example.com

[sshd]
enabled = true

[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log

The official documentation provides general configuration guidance:

The unmodified default configuration is already a good starting point and usually doesn’t require changes when using common services like SSH or NGINX (beside enabling the jail). If empty dict (default), only the internal defaults, and platform overrides apply.

  • Type: dict
  • Required: No
  • Default: {}

run_fail2ban_config_jail_dropin_file_name

⇑ Back to ToC ⇑

Filename of the drop-in configuration file to be placed in <config dir>/jail.d. Defaults to 99-managed.local. The 99- prefix in combination with the .local extension ensures later loading and thus higher precedence over files with lower-numbered prefixes or .conf extension (cf. man jail.conf).

If a non-default filename is used, any existing <config dir>/jail.d/99-managed.local from previous Ansible runs will be removed automatically to prevent conflicts.

  • Type: str
  • Required: No
  • Default: "99-managed.local"

run_fail2ban_custom_filters

⇑ Back to ToC ⇑

Custom filter definitions to be placed in <config dir>/filter.d/. Filters define the regex patterns used to detect intrusion attempts in log files.

Use filter names as top-level YAML keys (will create filter.d/<name>.conf) Use section names as 2nd-level YAML keys, and place their corresponding option names and values inside each section. This YAML structure is directly translated into Fail2ban’s INI format, preserving section names and option names exactly as written:

  • Each INI section ([SECTION]) becomes a YAML mapping key.
  • Each INI option inside that section becomes a YAML key-value pair.

Special cases:

  • For boolean values, use true/false (these will be converted to strings by the role as needed).
  • There is a content key, allowing you to put raw lines for the filter as the value without any special treatment. This is useful if you already have existing filters and do not want to convert their definitions into YAML:
    my-complex-filter:
      content: |
        [Definition]
        failregex = ^<HOST> - .*$
        [...]
    

Example:

dropbear-complex:
  INCLUDES:
    before: "common.conf"
  Definition:
    _daemon: "dropbear"
    failregex: |-
      ^[Ll]ogin attempt for nonexistent user ('.*' )?from <HOST>:\d+$
      ^[Bb]ad (PAM )?password attempt for .+ from <HOST>(:\d+)?$

will create filter.d/dropbear-complex.local with the content:

[INCLUDES]
before = common.conf

[Definition]
_daemon = dropbear
failregex = ^[Ll]ogin attempt for nonexistent user ('.*' )?from <HOST>:\d+$
            ^[Bb]ad (PAM )?password attempt for .+ from <HOST>(:\d+)?$

The official documentation provides general configuration guidance:

Most users do not need custom filter as fail2ban ships with filters for common services like SSH or NGINX. If empty dict (default), no custom filters are managed.

  • Type: dict
  • Required: No
  • Default: {}

run_fail2ban_custom_actions

⇑ Back to ToC ⇑

Custom action definitions to be placed in <config dir>/action.d/. Actions define what happens when an IP is banned or unbanned (e.g., firewall rules, notifications).

Use action names as top-level YAML keys (will create action.d/<name>.conf) Use section names as 2nd-level YAML keys, and place their corresponding option names and values inside each section. This YAML structure is directly translated into Fail2ban’s INI format, preserving section names and option names exactly as written:

  • Each INI section ([SECTION]) becomes a YAML mapping key.
  • Each INI option inside that section becomes a YAML key-value pair.

Special cases:

  • For boolean values, use true/false (these will be converted to strings by the role as needed).
  • There is a content key, allowing you to put raw lines for the action as the value without any special treatment. This is useful if you already have existing action and do not want to convert their definitions into YAML:
    my-complex-notify-slack:
      content: |
        [Definition]
        actionban = curl -X POST -H 'Content-type: application/json' --data '{"text":"Banned <ip>"}' ...
        actionunban = curl -X POST -H 'Content-type: application/json' --data '{"text":"Unbanned <ip>"}'
    

Example:

my-complex-notify-slack:
  INCLUDES:
    before: "foobar.conf"
  Definition:
    actionban: >-
      curl -X POST -H 'Content-type: application/json' --data '{"text":"Banned <ip>"}' ...
    actionunban: >-
      curl -X POST -H 'Content-type: application/json' --data '{"text":"Unbanned <ip>"}' ...

will create action.d/my-complex-notify-slack.local with the content:

[INCLUDES]
before = foobar.conf

[Definition]
actionban = curl -X POST -H 'Content-type: application/json' --data '{"text":"Banned <ip>"}' ...
actionunban = curl -X POST -H 'Content-type: application/json' --data '{"text":"Unbanned <ip>"}' ...

The official documentation provides general configuration guidance:

Most users do not need custom actions as fail2ban ships with actions for common firewall backends. If empty dict (default), no custom actions are managed.

  • Type: dict
  • Required: No
  • Default: {}

Dependencies

See dependencies in meta/main.yml.

Compatibility

See min_ansible_version in meta/main.yml and __run_fail2ban_supported_platforms in vars/main.yml.

External requirements

There are no special requirements not covered by Ansible itself.