lelutin / puppet-fail2ban

Manage fail2ban and its jails with puppet
GNU General Public License v3.0
8 stars 30 forks source link

Puppet module for fail2ban

Table of contents:

  1. Overview
  2. Module description
  3. Usage
  4. Requirements
  5. Compatibility
  6. Upgrade notices
  7. Documentation
  8. Testing

Overview

Install and manage fail2ban with puppet to block bruteforce attempts.

Module description

With this module, you can install fail2ban and define any configuration for the service in order to slow down bruteforce attempts on services that need to be exposed to the internet.

This module lets you create:

Usage

To use this module just include the fail2ban class.

To change default configurations in jail.conf or fail2ban.conf, you can pass values to parameters to the fail2ban class. See technical reference documentation (REFERENCE.md) for full list of parameters.

Here's an example that sets default ignored IP address for all jails to localhost plus another rfc1819 IP:

class { 'fail2ban':
  ignoreip => ['127.0.0.1', '10.0.0.1'],
}

Defining jails

The fail2ban::jail defined type lets you configure jails. This is the resource you'll mostly likely be using the most.

You can use one of the jail parameter presets (see details and list of presets in the section below. for more details the presets are defined in hiera files in data/) to speed up defining some common jails.

The following example defines a jail for the jenkins service:

fail2ban::jail { 'jenkins':
  port    => 'all',
  filter  => 'jenkins',
  logpath => ['/var/log/jenkins.log'],
}

Predefined jails

The list at the end of this section contains all of the presets that can be used to configure jails more easily.

Each of them is a data point -- a hash of parameter and values -- in hiera that needs to be gathered with the lookup() function.

Each hash represents parameters and values that should be passed in to the fail2ban::jail defined type (so they are really just presets for the type's parameters) documented above and has a lookup key of fail2ban::jail::$jailname.

For example, to quickly configure a jail for the ssh service with the preset parameters:

$ssh_params = lookup('fail2ban::jail::sshd')
fail2ban::jail { 'sshd':
  * => $ssh_params,
}

You can also override values from the preset or define new parameters by concatenating your own hash to it. In the following example we define new parameters bantime and findtime and we override the preset for maxretry:

$ssh_extra_params  = {
  'bantime'  => 300,
  'findtime' => 200,
  'maxretry' => 3,
}
$ssh_params = lookup('fail2ban::jail::sshd') + $ssh_extra_params
fail2ban::jail { 'sshd':
  * => $ssh_params,
}

This way you can set any parameter to the fail2ban::jail defined type and override preset values.

Watch out: jails by default use the same filter name as the jail name, so make sure to either use the same string as the lookup key for the jail resource name, or override the filter parameter.

Here's the full list of currently available presets. To know each preset's default values you can inspect files in data/:

Defining filters

You might want to define new filters for your new jails. To do that, you can use the fail2ban::filter defined type:

fail2ban::filter { 'jenkins':
  failregexes => [
    # Those regexes are really arbitrary examples.
    'Invalid login to Jenkins by user mooh by IP \'<HOST>\'',
    'Forced entry trial by <HOST>',
  ],
}

Defining actions

Fail2ban can do pretty much what you want it to do (e.g. run an action) when an IP matches a filter enough times during the rate limit set by the jail.

To define a new action, you can use the fail2ban::action defined type. Here's an example that would call out to a fictitious REST API whenever an IP address is banned and unbanned:

fail2ban::action { 'rest_api':
  ensure      => present,
  actionban   => ['curl -s -X PUT http://yourapi:8080/theapi/v4/firewall/rules -H "Content-Type:application/json" -H "Authorization: ..." -d "{\"ban\": \"<ip>\"}"'],
  actionunban => ['curl -s -X DELETE http://yourapi:8080/theapi/v4/firewall/rules/1 -H "Authorization: ..."'],
}

Python action scripts

Fail2ban lets users define actions as python scripts. These actions should exist as a file within /etc/fail2ban/action/$action.py where $action is the name of the action.

The contents of those files can differ wildly. Other than ensuring the location of the file and its permissions, this module wouldn't actually add much more on top of simply managing the python scripts as file resources, so no defined resource type was created for them.

If you manage such an action script, it is recommended to make it signal Class['fail2ban::service'] (e.g. with ~>) in order to automatically restart the service upon changes.

nftables support

Fail2ban supports nftables with the builtin actions:

These actions use nftables' set functionality to contain banned IPs instead of adding a firewall rule for each new banned IP. This should make your firewall more efficient if you have lots of banned IPs.

Since nftables is now used by default on Debian since the buster release but iptables is still used by fail2ban's default action, here's how to quickly enable usage of nftables for fail2ban:

Only two global parameters need to be changed:

Here's an example minimal configuration for using nftables with one sshd jail defined as usual:

class { 'fail2ban':
  banaction      => 'nftables',
  chain          => 'input',
}
$ssh_params = lookup('fail2ban::jail::sshd')
fail2ban::jail { 'sshd':
  * => $ssh_params,
}

Do note that upon service restart, fail2ban will not create the ip set and the corresponding rule right away so it will appear as though "it's not working". They will only be added whenever the first "action" is taken (so when banning the first IP for a jail). After that you should see both the set and the rule for that jail when running nft list ruleset.

To list which IPs are currently banned, you can either use fail2ban-client status sshd or list elements of the corresponding set. For the example above: nft list set filter f2b-sshd

Requirements

This module depends on the following modules to function:

Compatibility

This module supports

Puppet versions 6 and 7 are supported.

If you still need to use this module with puppet 5 or 4.10+ you can either try your luck with version 4.x of this module even though support is not official, or you can use the 3.x releases of the module.

Upgrade notices

Documentation

This module uses puppet-strings comments. The most stable way of using puppet-strings is to reuse the same version as what's specified in the Gemfile, so start by running gem install (you might need to setup local path for non-root install first).

Then you can generate HTML documentation in the docs directory with the following command:

bundle exec rake strings:generate

The REFERENCE.md file should be updated along with the code if any API and accompanying puppet-strings documentation change. You can do this with:

bundle exec rake strings:generate:reference

Testing

This module has some tests that you can run to ensure that everything is working as expected.

Before you can use the tests, make sure that you setup your local environment with bundle install.

Smoke tests

You can run sanity check with the validate task from puppet-syntax:

bundle exec rake validate

This will check manifest syntax, template syntax, yaml syntax for hiera files and ensure that the REFERENCE.md file is up to date.

Additionally to this, you can also use rubocop to run sanity checks on ruby files:

bundle exec rake rubocop

Unit tests

The unit tests are built with rspec-puppet.

The usual rspec-puppet_helper rake tasks are available. So, to run spec tests:

bundle exec rake spec

Funtionality tests

Unit tests are great, but sometimes it's nice to actually run the code in order to see if everything is setup properly and that the software is working as expected.

This repository does not have automated functionality tests, but it has a Vagrantfile that you can use to bring up a VM and run this module inside it.

The Vagrantfile expects you to have the vagrant plugin vagrant-librarian-puppet installed. If you don't have it you can also download this module's requirements (see metadata.json) and place them inside tests/modules/.

A couple of manifest files inside tests/ prepare sets of use cases. You can modify the Vagrantfile to use any of them for provisioning the VM.