commit 91d96ca835150695df8a7f31790aad19278cd4a7 Author: Javier Bértoli Date: Wed Apr 17 14:38:51 2013 -0300 Initial commit diff --git a/Modulefile b/Modulefile new file mode 100644 index 0000000..805894e --- /dev/null +++ b/Modulefile @@ -0,0 +1,11 @@ +name 'nm-fail2ban' +version '1.0.0' +author 'Javier Bertoli' +license 'Apache2' +project_page 'http://www.netmanagers.com.ar' +source 'https://github.com/netmanagers/puppet-fail2ban' +summary 'Puppet module for fail2ban' +description 'This module installs and manages fail2ban. Check README for details.' +dependency 'example42/puppi', '>= 2.0.0' +# dependency 'example42/monitor', '>= 2.0.0' +# dependency 'example42/firewall', '>= 2.0.0' diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d3a875 --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ += Puppet module: fail2ban + +This is a Puppet module for fail2ban based on the second generation layout ("NextGen") of Example42 Puppet Modules. + +Made by Javier Bértoli / Netmanagers + +Official site: http://www.netmanagers.com.ar + +Official git repository: http://github.com/netmanagers/puppet-fail2ban + +Released under the terms of Apache 2 License. + +This module requires functions provided by the Example42 Puppi module (you need it even if you don't use and install Puppi) + +For detailed info about the logic and usage patterns of Example42 modules check the DOCS directory on Example42 main modules set. + +## USAGE - Basic management + +* All parameters can be set using Hiera. See the manifests to see what can be set. + +* Install fail2ban with default settings + + class { 'fail2ban': } + +* You can configure and set a jail using fail2ban::jail + + fail2ban::jail { 'sshd': + port => '22', + logpath => '/var/log/secure', + maxretry => '2', + } + +* Install a specific version of fail2ban package + + class { 'fail2ban': + version => '1.0.1', + } + +* Disable fail2ban service. + + class { 'fail2ban': + disable => true + } + +* Remove fail2ban package + + class { 'fail2ban': + absent => true + } + +* Enable auditing without without making changes on existing fail2ban configuration *files* + + class { 'fail2ban': + audit_only => true + } + +* Module dry-run: Do not make any change on *all* the resources provided by the module + + class { 'fail2ban': + noops => true + } + + +== USAGE - Overrides and Customizations +* Use custom sources for main config file + + class { 'fail2ban': + source => [ "puppet:///modules/example42/fail2ban/fail2ban.conf-${hostname}" , "puppet:///modules/example42/fail2ban/fail2ban.conf" ], + } + + +* Use custom source directory for the whole configuration dir + + class { 'fail2ban': + source_dir => 'puppet:///modules/example42/fail2ban/conf/', + source_dir_purge => false, # Set to true to purge any existing file not present in $source_dir + } + +* Use custom template for main config file. Note that template and source arguments are alternative. + + class { 'fail2ban': + template => 'example42/fail2ban/fail2ban.conf.erb', + } + +* Automatically include a custom subclass + + class { 'fail2ban': + my_class => 'example42::my_fail2ban', + } + + +== USAGE - Example42 extensions management +* Activate puppi (recommended, but disabled by default) + + class { 'fail2ban': + puppi => true, + } + +* Activate puppi and use a custom puppi_helper template (to be provided separately with a puppi::helper define ) to customize the output of puppi commands + + class { 'fail2ban': + puppi => true, + puppi_helper => 'myhelper', + } + +* Activate automatic monitoring (recommended, but disabled by default). This option requires the usage of Example42 monitor and relevant monitor tools modules + + class { 'fail2ban': + monitor => true, + monitor_tool => [ 'nagios' , 'monit' , 'munin' ], + } + +* Activate automatic firewalling. This option requires the usage of Example42 firewall and relevant firewall tools modules + + class { 'fail2ban': + firewall => true, + firewall_tool => 'iptables', + firewall_src => '10.42.0.0/24', + firewall_dst => $ipaddress_eth0, + } + + +== CONTINUOUS TESTING + +Travis {Build Status}[https://travis-ci.org/example42/puppet-fail2ban] diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..f0d1465 --- /dev/null +++ b/Rakefile @@ -0,0 +1,5 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_class_parameter_defaults') diff --git a/TODO b/TODO new file mode 100644 index 0000000..612e6f4 --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +Clean up all the unused code resulting from module cloning: + +Travis and Modulefile information surely need to be corrected, as we are not using them in our deployments right now. + +Some variables, etc. that are not in use. diff --git a/manifests/concat.pp b/manifests/concat.pp new file mode 100644 index 0000000..59af864 --- /dev/null +++ b/manifests/concat.pp @@ -0,0 +1,46 @@ +# +# Class fail2ban::concat +# +# This class builds the fail2ban jails.local file using RIPienaar's concat module +# We build it using several fragments. +# Being the sequence of lines important we define these boundaries: +# 01 - General header +# Note that the fail2ban::jail define +# inserts (by default) its rules with priority 50. +# +class fail2ban::concat { + + include fail2ban + include concat::setup + + concat { $fail2ban::jails_file: + mode => $fail2ban::jails_file_mode, + owner => $fail2ban::jails_file_owner, + group => $fail2ban::jails_file_group, + notify => Service['fail2ban'], + } + + # The File Header. With Puppet comment + concat::fragment{ 'fail2ban_header': + target => $fail2ban::jails_file, + content => "# File Managed by Puppet\n", + order => 01, + notify => Service['fail2ban'], + } + + # The DEFAULT header with the default policies + concat::fragment{ 'fail2ban_jails_header': + target => $fail2ban::jails_file, + content => template($fail2ban::jails_template_header), + order => 05, + notify => Service['fail2ban'], + } + + # The jail.local footer + concat::fragment{ 'fail2ban_jails_footer': + target => $fail2ban::jails_file, + content => template($fail2ban::jails_template_footer), + order => 99, + notify => Service['fail2ban'], + } +} diff --git a/manifests/file.pp b/manifests/file.pp new file mode 100644 index 0000000..0c383b1 --- /dev/null +++ b/manifests/file.pp @@ -0,0 +1,29 @@ +# +# Class fail2ban::file +# +# This class configures fail2ban via a base rule file +# The file itselt is not provided. Use this class (or, better, +# your custom $my_project class that inherits this) to +# manage the fail2ban file in the way you want +# +# It's used if $fail2ban_config = "file" +# +class fail2ban::file inherits fail2ban { + + if $fail2ban::manage_file_jails_source or $fail2ban::manage_file_jails_template { + file { 'jail.local': + ensure => $fail2ban::manage_file, + path => $fail2ban::jails_file, + mode => $fail2ban::jails_file_mode, + owner => $fail2ban::jails_file_owner, + group => $fail2ban::jails_file_group, + require => Package[$fail2ban::package], + notify => $fail2ban::manage_service_autorestart, + source => $fail2ban::manage_file_jails_source, + content => $fail2ban::manage_file_jails_content, + replace => $fail2ban::manage_file_replace, + audit => $fail2ban::manage_audit, + noop => $fail2ban::bool_noops, + } + } +} diff --git a/manifests/init.pp b/manifests/init.pp new file mode 100644 index 0000000..9e6cef0 --- /dev/null +++ b/manifests/init.pp @@ -0,0 +1,572 @@ +# = Class: fail2ban +# +# This is the main fail2ban class +# +# +# == Parameters +# +# Standard class parameters +# Define the general class behaviour and customizations +# +# [*my_class*] +# Name of a custom class to autoload to manage module's customizations +# If defined, fail2ban class will automatically "include $my_class" +# Can be defined also by the (top scope) variable $fail2ban_myclass +# +# [*source*] +# Sets the content of source parameter for main configuration file +# If defined, fail2ban main config file will have the param: source => $source +# Can be defined also by the (top scope) variable $fail2ban_source +# +# [*source_dir*] +# If defined, the whole fail2ban configuration directory content is retrieved +# recursively from the specified source +# (source => $source_dir , recurse => true) +# Can be defined also by the (top scope) variable $fail2ban_source_dir +# +# [*source_dir_purge*] +# If set to true (default false) the existing configuration directory is +# mirrored with the content retrieved from source_dir +# (source => $source_dir , recurse => true , purge => true) +# Can be defined also by the (top scope) variable $fail2ban_source_dir_purge +# +# [*template*] +# Sets the path to the template to use as content for main configuration file +# If defined, fail2ban main config file has: content => content("$template") +# Note source and template parameters are mutually exclusive: don't use both +# Can be defined also by the (top scope) variable $fail2ban_template +# +# [*ignoreip*] +# Fail2ban will not ban a host which matches an address in this list. +# Can an IP address, a CIDR mask or a DNS host. Several addresses can be defined +# in an array. +# Default: 127.0.0.1/8 +# +# [*bantime*] +# Value in seconds that a host is banned +# Default: 600 +# +# [*maxretry*] +# Is the number of failures before a host get banned. +# Default: 5 +# +# [*findtime*] +# A host is banned if it has generated "maxretry" during the last "findtime" +# seconds. +# Default: 600 +# +# [*backend*] +# Specifies the backend used to get files modification. +# Available options are "gamin", "polling" and "auto". +# Default: auto +# +# [*mailto*] +# Destination email address used solely for the interpolations in +# jail.{conf,local} configuration files. +# Default: "hostmaster@${::domain}" +# +# [*banaction*] +# Default banning action (e.g. iptables, iptables-new, iptables-multiport, +# shorewall, etc) It is used to define action_* variables. +# Can be overridden globally or per section within jail.local file +# Default: iptables-multiport +# +# [*mta*] +# Since 0.8.1 upstream fail2ban uses sendmail MTA for the mailing. +# Change mta configuration parameter to 'mail' if you want to revert +# to conventional 'mail'. +# Default: sendmail +# +# [*jails_protocol*] +# Default: tcp +# +# [*jails_chain*] +# Specify chain where jumps would need to be added in iptables-* actions +# Default: INPUT +# +# [*jails_config*] +# Define how you want to manage jails configuration: +# "file" - To provide jails.local as a normal file +# "concat" - To build it up using different fragments +# - This option, set as default, permits the use of the fail2ban::jail define +# +# [*jails_source*] +# Sets the content of source parameter for the jails.local configuration file +# Note that single stanzas of jails.local file can be managed also (and alternatively) +# by fail2ban::jails +# +# [*jails_template*] +# Sets the path to the template to use as content for the jails.local configuration file +# If defined, fail2ban jails config file has: content => content("$jails_template") +# Note source and template parameters are mutually exclusive: don't use both +# Can be defined also by the (top scope) variable $fail2ban_jails_template +# +# [*jails_template_header*] +# Path to the template to use as header with concat +# Used by fail2ban::jails +# +# [*jails_template_footer*] +# Path to the template to use as footer with concat +# Used by fail2ban::jails +# +# [*options*] +# An hash of custom options to be used in templates for arbitrary settings. +# Can be defined also by the (top scope) variable $fail2ban_options +# +# [*service_autorestart*] +# Automatically restarts the fail2ban service when there is a change in +# configuration files. Default: true, Set to false if you don't want to +# automatically restart the service. +# +# [*version*] +# The package version, used in the ensure parameter of package type. +# Default: present. Can be 'latest' or a specific version number. +# Note that if the argument absent (see below) is set to true, the +# package is removed, whatever the value of version parameter. +# +# [*absent*] +# Set to 'true' to remove package(s) installed by module +# Can be defined also by the (top scope) variable $fail2ban_absent +# +# [*disable*] +# Set to 'true' to disable service(s) managed by module +# Can be defined also by the (top scope) variable $fail2ban_disable +# +# [*disableboot*] +# Set to 'true' to disable service(s) at boot, without checks if it's running +# Use this when the service is managed by a tool like a cluster software +# Can be defined also by the (top scope) variable $fail2ban_disableboot +# +# [*monitor*] +# Set to 'true' to enable monitoring of the services provided by the module +# Can be defined also by the (top scope) variables $fail2ban_monitor +# and $monitor +# +# [*monitor_tool*] +# Define which monitor tools (ad defined in Example42 monitor module) +# you want to use for fail2ban checks +# Can be defined also by the (top scope) variables $fail2ban_monitor_tool +# and $monitor_tool +# +# [*monitor_target*] +# The Ip address or hostname to use as a target for monitoring tools. +# Default is the fact $ipaddress +# Can be defined also by the (top scope) variables $fail2ban_monitor_target +# and $monitor_target +# +# [*puppi*] +# Set to 'true' to enable creation of module data files that are used by puppi +# Can be defined also by the (top scope) variables $fail2ban_puppi and $puppi +# +# [*puppi_helper*] +# Specify the helper to use for puppi commands. The default for this module +# is specified in params.pp and is generally a good choice. +# You can customize the output of puppi commands for this module using another +# puppi helper. Use the define puppi::helper to create a new custom helper +# Can be defined also by the (top scope) variables $fail2ban_puppi_helper +# and $puppi_helper +# +# [*firewall*] +# Set to 'true' to enable firewalling of the services provided by the module +# Can be defined also by the (top scope) variables $fail2ban_firewall +# and $firewall +# +# [*firewall_tool*] +# Define which firewall tool(s) (ad defined in Example42 firewall module) +# you want to use to open firewall for fail2ban port(s) +# Can be defined also by the (top scope) variables $fail2ban_firewall_tool +# and $firewall_tool +# +# [*firewall_src*] +# Define which source ip/net allow for firewalling fail2ban. Default: 0.0.0.0/0 +# Can be defined also by the (top scope) variables $fail2ban_firewall_src +# and $firewall_src +# +# [*firewall_dst*] +# Define which destination ip to use for firewalling. Default: $ipaddress +# Can be defined also by the (top scope) variables $fail2ban_firewall_dst +# and $firewall_dst +# +# [*debug*] +# Set to 'true' to enable modules debugging +# Can be defined also by the (top scope) variables $fail2ban_debug and $debug +# +# [*audit_only*] +# Set to 'true' if you don't intend to override existing configuration files +# and want to audit the difference between existing files and the ones +# managed by Puppet. +# Can be defined also by the (top scope) variables $fail2ban_audit_only +# and $audit_only +# +# [*noops*] +# Set noop metaparameter to true for all the resources managed by the module. +# Basically you can run a dryrun for this specific module if you set +# this to true. Default: false +# +# Default class params - As defined in fail2ban::params. +# Note that these variables are mostly defined and used in the module itself, +# overriding the default values might not affected all the involved components. +# Set and override them only if you know what you're doing. +# Note also that you can't override/set them via top scope variables. +# +# [*package*] +# The name of fail2ban package +# +# [*service*] +# The name of fail2ban service +# +# [*service_status*] +# If the fail2ban service init script supports status argument +# +# [*process*] +# The name of fail2ban process +# +# [*process_args*] +# The name of fail2ban arguments. Used by puppi and monitor. +# Used only in case the fail2ban process name is generic (java, ruby...) +# +# [*process_user*] +# The name of the user fail2ban runs with. Used by puppi and monitor. +# +# [*config_dir*] +# Main configuration directory. Used by puppi +# +# [*config_file*] +# Main configuration file path +# +# [*config_file_mode*] +# Main configuration file path mode +# +# [*config_file_owner*] +# Main configuration file path owner +# +# [*config_file_group*] +# Main configuration file path group +# +# [*config_file_init*] +# Path of configuration file sourced by init script +# +# [*pid_file*] +# Path of pid file. Used by monitor +# +# [*data_dir*] +# Path of application data directory. Used by puppi +# +# [*log_dir*] +# Base logs directory. Used by puppi +# +# [*log_file*] +# Log file(s). Used by puppi +# +# [*port*] +# The listening port, if any, of the service. +# This is used by monitor, firewall and puppi (optional) components +# Note: This doesn't necessarily affect the service configuration file +# Can be defined also by the (top scope) variable $fail2ban_port +# +# [*protocol*] +# The protocol used by the the service. +# This is used by monitor, firewall and puppi (optional) components +# Can be defined also by the (top scope) variable $fail2ban_protocol +# +# +# == Examples +# +# You can use this class in 2 ways: +# - Set variables (at top scope level on in a ENC) and "include fail2ban" +# - Call fail2ban as a parametrized class +# +# See README for details. +# +# +# == Author +# Alessandro Franceschi +# Javier Bertoli +# +class fail2ban ( + $my_class = params_lookup( 'my_class' ), + $source = params_lookup( 'source' ), + $source_dir = params_lookup( 'source_dir' ), + $source_dir_purge = params_lookup( 'source_dir_purge' ), + $template = params_lookup( 'template' ), + $service_autorestart = params_lookup( 'service_autorestart' , 'global' ), + $options = params_lookup( 'options' ), + $version = params_lookup( 'version' ), + $absent = params_lookup( 'absent' ), + $disable = params_lookup( 'disable' ), + $disableboot = params_lookup( 'disableboot' ), + $monitor = params_lookup( 'monitor' , 'global' ), + $monitor_tool = params_lookup( 'monitor_tool' , 'global' ), + $monitor_target = params_lookup( 'monitor_target' , 'global' ), + $puppi = params_lookup( 'puppi' , 'global' ), + $puppi_helper = params_lookup( 'puppi_helper' , 'global' ), + $firewall = params_lookup( 'firewall' , 'global' ), + $firewall_tool = params_lookup( 'firewall_tool' , 'global' ), + $firewall_src = params_lookup( 'firewall_src' , 'global' ), + $firewall_dst = params_lookup( 'firewall_dst' , 'global' ), + $debug = params_lookup( 'debug' , 'global' ), + $audit_only = params_lookup( 'audit_only' , 'global' ), + $noops = params_lookup( 'noops' ), + $package = params_lookup( 'package' ), + $service = params_lookup( 'service' ), + $service_status = params_lookup( 'service_status' ), + $process = params_lookup( 'process' ), + $process_args = params_lookup( 'process_args' ), + $process_user = params_lookup( 'process_user' ), + $config_dir = params_lookup( 'config_dir' ), + $config_file = params_lookup( 'config_file' ), + $config_file_mode = params_lookup( 'config_file_mode' ), + $config_file_owner = params_lookup( 'config_file_owner' ), + $config_file_group = params_lookup( 'config_file_group' ), + $config_file_init = params_lookup( 'config_file_init' ), + $pid_file = params_lookup( 'pid_file' ), + $data_dir = params_lookup( 'data_dir' ), + $log_dir = params_lookup( 'log_dir' ), + $log_file = params_lookup( 'log_file' ), + $port = params_lookup( 'port' ), + $protocol = params_lookup( 'protocol' ), + $ignoreip = params_lookup( 'ignoreip' ), + $bantime = params_lookup( 'bantime' ), + $findtime = params_lookup( 'findtime' ), + $maxretry = params_lookup( 'maxretry' ), + $backend = params_lookup( 'backend' ), + $mailto = params_lookup( 'mailto' ), + $banaction = params_lookup( 'banaction' ), + $mta = params_lookup( 'mta' ), + $jails_config = params_lookup( 'jails_config' ), + $jails_protocol = params_lookup( 'jails_protocol' ), + $jails_chain = params_lookup( 'jails_chain' ), + $jails_file = params_lookup( 'jails_file' ), + $jails_file_mode = params_lookup( 'jails_file_mode' ), + $jails_file_owner = params_lookup( 'jails_file_owner' ), + $jails_file_group = params_lookup( 'jails_file_group' ), + $jails = params_lookup( 'jails' ), + $jails_source = params_lookup( 'jails_source' ), + $jails_template = params_lookup( 'jails_template' ), + $jails_template_header = params_lookup( 'jails_template_header' ), + $jails_template_footer = params_lookup( 'jails_template_footer' ) + ) inherits fail2ban::params { + + $bool_source_dir_purge=any2bool($source_dir_purge) + $bool_service_autorestart=any2bool($service_autorestart) + $bool_absent=any2bool($absent) + $bool_disable=any2bool($disable) + $bool_disableboot=any2bool($disableboot) + $bool_monitor=any2bool($monitor) + $bool_puppi=any2bool($puppi) + $bool_firewall=any2bool($firewall) + $bool_debug=any2bool($debug) + $bool_audit_only=any2bool($audit_only) + $bool_noops=any2bool($noops) + + ### Definition of some variables used in the module + $manage_package = $fail2ban::bool_absent ? { + true => 'absent', + false => $fail2ban::version, + } + + $manage_service_enable = $fail2ban::bool_disableboot ? { + true => false, + default => $fail2ban::bool_disable ? { + true => false, + default => $fail2ban::bool_absent ? { + true => false, + false => true, + }, + }, + } + + $manage_service_ensure = $fail2ban::bool_disable ? { + true => 'stopped', + default => $fail2ban::bool_absent ? { + true => 'stopped', + default => 'running', + }, + } + + $manage_service_autorestart = $fail2ban::bool_service_autorestart ? { + true => Service[fail2ban], + false => undef, + } + + $manage_file = $fail2ban::bool_absent ? { + true => 'absent', + default => 'present', + } + + if $fail2ban::bool_absent == true + or $fail2ban::bool_disable == true + or $fail2ban::bool_disableboot == true { + $manage_monitor = false + } else { + $manage_monitor = true + } + + if $fail2ban::bool_absent == true + or $fail2ban::bool_disable == true { + $manage_firewall = false + } else { + $manage_firewall = true + } + + $manage_audit = $fail2ban::bool_audit_only ? { + true => 'all', + false => undef, + } + + $manage_file_replace = $fail2ban::bool_audit_only ? { + true => false, + false => true, + } + + $manage_file_source = $fail2ban::source ? { + '' => undef, + default => $fail2ban::source, + } + + $manage_file_content = $fail2ban::template ? { + '' => undef, + default => template($fail2ban::template), + } + + $manage_file_jails_source = $fail2ban::jails_source ? { + '' => undef, + default => $fail2ban::jails_source, + } + + $manage_file_jails_content = $fail2ban::jails_template ? { + '' => undef, + default => template($fail2ban::jails_template), + } + + ### Managed resources + package { $fail2ban::package: + ensure => $fail2ban::manage_package, + noop => $fail2ban::bool_noops, + } + + service { 'fail2ban': + ensure => $fail2ban::manage_service_ensure, + name => $fail2ban::service, + enable => $fail2ban::manage_service_enable, + hasstatus => $fail2ban::service_status, + pattern => $fail2ban::process, + require => Package[$fail2ban::package], + noop => $fail2ban::bool_noops, + } + + file { 'fail2ban.conf': + ensure => $fail2ban::manage_file, + path => $fail2ban::config_file, + mode => $fail2ban::config_file_mode, + owner => $fail2ban::config_file_owner, + group => $fail2ban::config_file_group, + require => Package[$fail2ban::package], + notify => $fail2ban::manage_service_autorestart, + source => $fail2ban::manage_file_source, + content => $fail2ban::manage_file_content, + replace => $fail2ban::manage_file_replace, + audit => $fail2ban::manage_audit, + noop => $fail2ban::bool_noops, + } + + # How to manage fail2ban configuration + case $fail2ban::jails_config { + 'file': { include fail2ban::file } + 'concat': { include fail2ban::concat } + default: { } + } + + # The whole fail2ban configuration directory can be recursively overriden + if $fail2ban::source_dir { + file { 'fail2ban.dir': + ensure => directory, + path => $fail2ban::config_dir, + require => Package[$fail2ban::package], + notify => $fail2ban::manage_service_autorestart, + source => $fail2ban::source_dir, + recurse => true, + purge => $fail2ban::bool_source_dir_purge, + force => $fail2ban::bool_source_dir_purge, + replace => $fail2ban::manage_file_replace, + audit => $fail2ban::manage_audit, + noop => $fail2ban::bool_noops, + } + } + + + ### Include custom class if $my_class is set + if $fail2ban::my_class { + include $fail2ban::my_class + } + + + ### Provide puppi data, if enabled ( puppi => true ) + if $fail2ban::bool_puppi == true { + $classvars=get_class_args() + puppi::ze { 'fail2ban': + ensure => $fail2ban::manage_file, + variables => $classvars, + helper => $fail2ban::puppi_helper, + noop => $fail2ban::bool_noops, + } + } + + + ### Service monitoring, if enabled ( monitor => true ) + if $fail2ban::bool_monitor == true { + if $fail2ban::port != '' { + monitor::port { "fail2ban_${fail2ban::protocol}_${fail2ban::port}": + protocol => $fail2ban::protocol, + port => $fail2ban::port, + target => $fail2ban::monitor_target, + tool => $fail2ban::monitor_tool, + enable => $fail2ban::manage_monitor, + noop => $fail2ban::bool_noops, + } + } + if $fail2ban::service != '' { + monitor::process { 'fail2ban_process': + process => $fail2ban::process, + service => $fail2ban::service, + pidfile => $fail2ban::pid_file, + user => $fail2ban::process_user, + argument => $fail2ban::process_args, + tool => $fail2ban::monitor_tool, + enable => $fail2ban::manage_monitor, + noop => $fail2ban::bool_noops, + } + } + } + + + ### Firewall management, if enabled ( firewall => true ) + if $fail2ban::bool_firewall == true and $fail2ban::port != '' { + firewall { "fail2ban_${fail2ban::protocol}_${fail2ban::port}": + source => $fail2ban::firewall_src, + destination => $fail2ban::firewall_dst, + protocol => $fail2ban::protocol, + port => $fail2ban::port, + action => 'allow', + direction => 'input', + tool => $fail2ban::firewall_tool, + enable => $fail2ban::manage_firewall, + noop => $fail2ban::bool_noops, + } + } + + + ### Debugging, if enabled ( debug => true ) + if $fail2ban::bool_debug == true { + file { 'debug_fail2ban': + ensure => $fail2ban::manage_file, + path => "${settings::vardir}/debug-fail2ban", + mode => '0640', + owner => 'root', + group => 'root', + content => inline_template('<%= scope.to_hash.reject { |k,v| k.to_s =~ /(uptime.*|path|timestamp|free|.*password.*|.*psk.*|.*key)/ }.to_yaml %>'), + noop => $fail2ban::bool_noops, + } + } + +} diff --git a/manifests/jail.pp b/manifests/jail.pp new file mode 100644 index 0000000..8ef697e --- /dev/null +++ b/manifests/jail.pp @@ -0,0 +1,94 @@ +# Define: fail2ban::jail +# +# Adds a custom fail2ban jail +# Supported arguments: +# $jailname - The name you want to give the jail. If not set, defaults to == $title +# $order - The order in the jail.local file. Default 50. Generally you don't need to change it +# $status - enabled / disabled. If disabled, the rule _IS ADDED_ to the jail.local file +# but it will not be active. Compare with the next one. +# $enable - true / false. If false, the rule _IS NOT ADDED_ to the jail.local file +# $filter - The filter rule to use. If empty, defaults to == $jailname. +# $port - The port to filter. It can be an array of ports. +# $action - The action to take when +# $logpath - The log file to monitor +# $maxretry - How many fails are acceptable +# $bantime - How much time to apply the ban, in seconds + +define fail2ban::jail ( + $jailname = '', + $order = '', + $status = '', + $filter = '', + $port = '', + $action = '', + $logpath = '', + $maxretry = '', + $bantime = '', + $enable = true ) { + + include fail2ban + include concat::setup + + $real_jailname = $jailname ? { + '' => $title, + default => $jailname, + } + + # If (concat) order is not defined we find out the right one + $real_order = $order ? { + '' => '50', + default => $order, + } + + $real_status = $status ? { + /(?i:disabled)/ => false, + default => true, + } + + # If we don't specify a filter, we take as a default the jailname as filtername + $real_filter = $filter ? { + '' => $real_jailname, + default => $filter, + } + + $array_port = is_array($port) ? { + false => $port ? { + '' => [], + default => [$port], + }, + default => $port, + } + + $array_action = is_array($action) ? { + false => $action ? { + '' => [], + default => [$action], + }, + default => $action, + } + + $real_logpath = $logpath ? { + '' => '', + default => $logpath, + } + + $real_maxretry = $maxretry ? { + '' => '', + default => $maxretry, + } + + $real_bantime = $bantime ? { + '' => '', + default => $bantime, + } + + $ensure = bool2ensure($enable) + + concat::fragment{ "fail2ban_jail_$name": + ensure => $ensure, + target => $fail2ban::jails_file, + content => template('fail2ban/concat/jail.local-stanza.erb'), + order => $real_order, + notify => Service['fail2ban'], + } +} diff --git a/manifests/params.pp b/manifests/params.pp new file mode 100644 index 0000000..efc9bcd --- /dev/null +++ b/manifests/params.pp @@ -0,0 +1,153 @@ +# Class: fail2ban::params +# +# This class defines default parameters used by the main module class fail2ban +# Operating Systems differences in names and paths are addressed here +# +# == Variables +# +# Refer to fail2ban class for the variables defined here. +# +# == Usage +# +# This class is not intended to be used directly. +# It may be imported or inherited by other classes +# +class fail2ban::params { + + ### Application related parameters + + $package = $::operatingsystem ? { + default => 'fail2ban', + } + + $service = $::operatingsystem ? { + default => 'fail2ban', + } + + $service_status = $::operatingsystem ? { + default => true, + } + + $process = $::operatingsystem ? { + default => 'fail2ban', + } + + $process_args = $::operatingsystem ? { + default => '', + } + + $process_user = $::operatingsystem ? { + default => 'fail2ban', + } + + $config_dir = $::operatingsystem ? { + default => '/etc/fail2ban', + } + + $config_file = $::operatingsystem ? { + default => '/etc/fail2ban/fail2ban.conf', + } + + $config_file_mode = $::operatingsystem ? { + default => '0644', + } + + $config_file_owner = $::operatingsystem ? { + default => 'root', + } + + $config_file_group = $::operatingsystem ? { + default => 'root', + } + + # Define how you want to manage jails configuration: + # "file" - To provide jails stanzas as a normal file + # "concat" - To build them up using different fragments + # - This option, set as default, permits the use of the fail2ban::jail define + $jails_config = 'concat' + + $jails_file = $::operatingsystem ? { + default => '/etc/fail2ban/jail.local', + } + + $jails_file_mode = $::operatingsystem ? { + default => '0644', + } + + $jails_file_owner = $::operatingsystem ? { + default => 'root', + } + + $jails_file_group = $::operatingsystem ? { + default => 'root', + } + + $config_file_init = $::operatingsystem ? { + /(?i:Debian|Ubuntu|Mint)/ => '/etc/default/fail2ban', + default => '/etc/sysconfig/fail2ban', + } + + $pid_file = $::operatingsystem ? { + default => '/var/run/fail2ban.pid', + } + + $data_dir = $::operatingsystem ? { + default => '/etc/fail2ban', + } + + $log_dir = $::operatingsystem ? { + default => '/var/log/fail2ban', + } + + $log_file = $::operatingsystem ? { + default => '/var/log/fail2ban/fail2ban.log', + } + + $ignoreip = ['127.0.0.1/8'] + $bantime = '600' + $findtime = '600' + $maxretry = '5' + $backend = 'auto' + $mailto = "hostmaster@${::domain}" + $banaction = 'iptables-multiport' + $mta = 'sendmail' + $jails_protocol = 'tcp' + $jails_chain = 'INPUT' + + $jails = [] + $jails_source = '' + $jails_template = '' + $jails_template_header = 'fail2ban/concat/jail.local-header.erb' + $jails_template_footer = 'fail2ban/concat/jail.local-footer.erb' + + $protocol = '' + $port = '' + + # General Settings + $my_class = '' + $source = '' + $source_dir = '' + $source_dir_purge = false + $template = '' + $options = '' + $service_autorestart = true + $version = 'present' + $absent = false + $disable = false + $disableboot = false + + ### General module variables that can have a site or per module default + $monitor = false + $monitor_tool = '' + $monitor_target = $::ipaddress + $firewall = false + $firewall_tool = '' + $firewall_src = '0.0.0.0/0' + $firewall_dst = $::ipaddress + $puppi = false + $puppi_helper = 'standard' + $debug = false + $audit_only = false + $noops = false + +} diff --git a/manifests/spec.pp b/manifests/spec.pp new file mode 100644 index 0000000..874f25b --- /dev/null +++ b/manifests/spec.pp @@ -0,0 +1,22 @@ +# Class: fail2ban::spec +# +# This class is used only for rpsec-puppet tests +# Can be taken as an example on how to do custom classes but should not +# be modified. +# +# == Usage +# +# This class is not intended to be used directly. +# Use it as reference +# +class fail2ban::spec inherits fail2ban { + + # This just a test to override the arguments of an existing resource + # Note that you can achieve this same result with just: + # class { "fail2ban": template => "fail2ban/spec.erb" } + + File['fail2ban.conf'] { + content => template('fail2ban/spec.erb'), + } + +} diff --git a/spec/classes/standard42_spec.rb b/spec/classes/standard42_spec.rb new file mode 100644 index 0000000..b47ede4 --- /dev/null +++ b/spec/classes/standard42_spec.rb @@ -0,0 +1,157 @@ +require "#{File.join(File.dirname(__FILE__),'..','spec_helper.rb')}" + +describe 'fail2ban' do + + let(:title) { 'fail2ban' } + let(:node) { 'rspec.example42.com' } + let(:facts) { { :ipaddress => '10.42.42.42' } } + + describe 'Test standard installation' do + it { should contain_package('fail2ban').with_ensure('present') } + it { should contain_service('fail2ban').with_ensure('running') } + it { should contain_service('fail2ban').with_enable('true') } + it { should contain_file('fail2ban.conf').with_ensure('present') } + end + + describe 'Test installation of a specific version' do + let(:params) { {:version => '1.0.42' } } + it { should contain_package('fail2ban').with_ensure('1.0.42') } + end + + describe 'Test standard installation with monitoring and firewalling' do + let(:params) { {:monitor => true , :firewall => true, :port => '42', :protocol => 'tcp' } } + it { should contain_package('fail2ban').with_ensure('present') } + it { should contain_service('fail2ban').with_ensure('running') } + it { should contain_service('fail2ban').with_enable('true') } + it { should contain_file('fail2ban.conf').with_ensure('present') } + it { should contain_monitor__process('fail2ban_process').with_enable('true') } + it { should contain_firewall('fail2ban_tcp_42').with_enable('true') } + end + + describe 'Test decommissioning - absent' do + let(:params) { {:absent => true, :monitor => true , :firewall => true, :port => '42', :protocol => 'tcp'} } + it 'should remove Package[fail2ban]' do should contain_package('fail2ban').with_ensure('absent') end + it 'should stop Service[fail2ban]' do should contain_service('fail2ban').with_ensure('stopped') end + it 'should not enable at boot Service[fail2ban]' do should contain_service('fail2ban').with_enable('false') end + it 'should remove fail2ban configuration file' do should contain_file('fail2ban.conf').with_ensure('absent') end + it { should contain_monitor__process('fail2ban_process').with_enable('false') } + it { should contain_firewall('fail2ban_tcp_42').with_enable('false') } + end + + describe 'Test decommissioning - disable' do + let(:params) { {:disable => true, :monitor => true , :firewall => true, :port => '42', :protocol => 'tcp'} } + it { should contain_package('fail2ban').with_ensure('present') } + it 'should stop Service[fail2ban]' do should contain_service('fail2ban').with_ensure('stopped') end + it 'should not enable at boot Service[fail2ban]' do should contain_service('fail2ban').with_enable('false') end + it { should contain_file('fail2ban.conf').with_ensure('present') } + it { should contain_monitor__process('fail2ban_process').with_enable('false') } + it { should contain_firewall('fail2ban_tcp_42').with_enable('false') } + end + + describe 'Test decommissioning - disableboot' do + let(:params) { {:disableboot => true, :monitor => true , :firewall => true, :port => '42', :protocol => 'tcp'} } + it { should contain_package('fail2ban').with_ensure('present') } + it { should_not contain_service('fail2ban').with_ensure('present') } + it { should_not contain_service('fail2ban').with_ensure('absent') } + it 'should not enable at boot Service[fail2ban]' do should contain_service('fail2ban').with_enable('false') end + it { should contain_file('fail2ban.conf').with_ensure('present') } + it { should contain_monitor__process('fail2ban_process').with_enable('false') } + it { should contain_firewall('fail2ban_tcp_42').with_enable('true') } + end + + describe 'Test noops mode' do + let(:params) { {:noops => true, :monitor => true , :firewall => true, :port => '42', :protocol => 'tcp'} } + it { should contain_package('fail2ban').with_noop('true') } + it { should contain_service('fail2ban').with_noop('true') } + it { should contain_file('fail2ban.conf').with_noop('true') } + it { should contain_monitor__process('fail2ban_process').with_noop('true') } + it { should contain_monitor__process('fail2ban_process').with_noop('true') } + it { should contain_monitor__port('fail2ban_tcp_42').with_noop('true') } + it { should contain_firewall('fail2ban_tcp_42').with_noop('true') } + end + + describe 'Test customizations - template' do + let(:params) { {:template => "fail2ban/spec.erb" , :options => { 'opt_a' => 'value_a' } } } + it 'should generate a valid template' do + content = catalogue.resource('file', 'fail2ban.conf').send(:parameters)[:content] + content.should match "fqdn: rspec.example42.com" + end + it 'should generate a template that uses custom options' do + content = catalogue.resource('file', 'fail2ban.conf').send(:parameters)[:content] + content.should match "value_a" + end + end + + describe 'Test customizations - source' do + let(:params) { {:source => "puppet:///modules/fail2ban/spec"} } + it { should contain_file('fail2ban.conf').with_source('puppet:///modules/fail2ban/spec') } + end + + describe 'Test customizations - source_dir' do + let(:params) { {:source_dir => "puppet:///modules/fail2ban/dir/spec" , :source_dir_purge => true } } + it { should contain_file('fail2ban.dir').with_source('puppet:///modules/fail2ban/dir/spec') } + it { should contain_file('fail2ban.dir').with_purge('true') } + it { should contain_file('fail2ban.dir').with_force('true') } + end + + describe 'Test customizations - custom class' do + let(:params) { {:my_class => "fail2ban::spec" } } + it { should contain_file('fail2ban.conf').with_content(/rspec.example42.com/) } + end + + describe 'Test service autorestart' do + let(:params) { {:service_autorestart => "no" } } + it 'should not automatically restart the service, when service_autorestart => false' do + content = catalogue.resource('file', 'fail2ban.conf').send(:parameters)[:notify] + content.should be_nil + end + end + + describe 'Test Puppi Integration' do + let(:params) { {:puppi => true, :puppi_helper => "myhelper"} } + it { should contain_puppi__ze('fail2ban').with_helper('myhelper') } + end + + describe 'Test Monitoring Tools Integration' do + let(:params) { {:monitor => true, :monitor_tool => "puppi" } } + it { should contain_monitor__process('fail2ban_process').with_tool('puppi') } + end + + describe 'Test Firewall Tools Integration' do + let(:params) { {:firewall => true, :firewall_tool => "iptables" , :protocol => "tcp" , :port => "42" } } + it { should contain_firewall('fail2ban_tcp_42').with_tool('iptables') } + end + + describe 'Test OldGen Module Set Integration' do + let(:params) { {:monitor => "yes" , :monitor_tool => "puppi" , :firewall => "yes" , :firewall_tool => "iptables" , :puppi => "yes" , :port => "42" , :protocol => 'tcp' } } + it { should contain_monitor__process('fail2ban_process').with_tool('puppi') } + it { should contain_firewall('fail2ban_tcp_42').with_tool('iptables') } + it { should contain_puppi__ze('fail2ban').with_ensure('present') } + end + + describe 'Test params lookup' do + let(:facts) { { :monitor => true , :ipaddress => '10.42.42.42' } } + let(:params) { { :port => '42' } } + it 'should honour top scope global vars' do should contain_monitor__process('fail2ban_process').with_enable('true') end + end + + describe 'Test params lookup' do + let(:facts) { { :fail2ban_monitor => true , :ipaddress => '10.42.42.42' } } + let(:params) { { :port => '42' } } + it 'should honour module specific vars' do should contain_monitor__process('fail2ban_process').with_enable('true') end + end + + describe 'Test params lookup' do + let(:facts) { { :monitor => false , :fail2ban_monitor => true , :ipaddress => '10.42.42.42' } } + let(:params) { { :port => '42' } } + it 'should honour top scope module specific over global vars' do should contain_monitor__process('fail2ban_process').with_enable('true') end + end + + describe 'Test params lookup' do + let(:facts) { { :monitor => false , :ipaddress => '10.42.42.42' } } + let(:params) { { :monitor => true , :firewall => true, :port => '42' } } + it 'should honour passed params over global vars' do should contain_monitor__process('fail2ban_process').with_enable('true') end + end + +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..2c6f566 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/templates/concat/jail.local-footer.erb b/templates/concat/jail.local-footer.erb new file mode 100644 index 0000000..2f18fd6 --- /dev/null +++ b/templates/concat/jail.local-footer.erb @@ -0,0 +1 @@ +### jails footer - File managed by Puppet ### diff --git a/templates/concat/jail.local-header.erb b/templates/concat/jail.local-header.erb new file mode 100644 index 0000000..b216b8b --- /dev/null +++ b/templates/concat/jail.local-header.erb @@ -0,0 +1,45 @@ +[DEFAULT] +<% if scope.lookupvar("fail2ban::ignoreip") != '' -%> +ignoreip = <%= scope.lookupvar("fail2ban::ignoreip") * ' ' %> +<% end -%> +<% if scope.lookupvar("fail2ban::bantime") != '' -%> +bantime = <%= scope.lookupvar("fail2ban::bantime") %> +<% end -%> +<% if scope.lookupvar("fail2ban::findtime") != '' -%> +findtime = <%= scope.lookupvar("fail2ban::findtime") %> +<% end -%> +<% if scope.lookupvar("fail2ban::maxretry") != '' -%> +maxretry = <%= scope.lookupvar("fail2ban::maxretry") %> +<% end -%> +<% if scope.lookupvar("fail2ban::backend") != '' -%> +backend = <%= scope.lookupvar("fail2ban::backend") %> +<% end -%> +<% if scope.lookupvar("fail2ban::mailto") != '' -%> +destemail = <%= scope.lookupvar("fail2ban::mailto") %> +<% end -%> +<% if scope.lookupvar("fail2ban::banaction") != '' -%> +banaction = <%= scope.lookupvar("fail2ban::banaction") %> +<% end -%> +<% if scope.lookupvar("fail2ban::mta") != '' -%> +mta = <%= scope.lookupvar("fail2ban::mta") %> +<% end -%> +<% if scope.lookupvar("fail2ban::jails_protocol") != '' -%> +protocol = <%= scope.lookupvar("fail2ban::jails_protocol") %> +<% end -%> +<% if scope.lookupvar("fail2ban::jails_chain") != '' -%> +chain = <%= scope.lookupvar("fail2ban::jails_chain") %> +<% end -%> + +### Default action ### +## The simplest action to take: ban only +action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] + +## ban & send an e-mail with whois report and relevant log lines +## to the destemail. +action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] + %(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"] + +# Choose default action. To change, just override value of 'action' with the +# interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local +# globally (section [DEFAULT]) or per specific section +action = %(action_mwl)s diff --git a/templates/concat/jail.local-stanza.erb b/templates/concat/jail.local-stanza.erb new file mode 100644 index 0000000..3ebfb18 --- /dev/null +++ b/templates/concat/jail.local-stanza.erb @@ -0,0 +1,24 @@ +################## +[<%= real_jailname %>] +enabled = <%= real_status %> +<% if real_filter != '' -%> +filter = <%= real_filter %> +<% end -%> +<% if array_port != [] -%> +port = <%= array_port * ',' %> +<% end -%> +<% if array_action != [] -%> +action = <% array_action.each do |a| -%> + <%= a %> + <% end -%> +<% end -%> +<% if real_logpath != '' -%> +logpath = <%= real_logpath %> +<% end -%> +<% if real_maxretry != '' -%> +maxretry = <%= real_maxretry %> +<% end -%> +<% if real_bantime != '' -%> +bantime = <%= real_bantime %> +<% end -%> + diff --git a/templates/jail.local.erb b/templates/jail.local.erb new file mode 100644 index 0000000..4bd6a92 --- /dev/null +++ b/templates/jail.local.erb @@ -0,0 +1,55 @@ +################## +# Puppet Managed # +################## + +[DEFAULT] +ignoreip = 127.0.0.1 +bantime = 600 +findtime = 600 +maxretry = 3 +backend = auto + +[imap-iptables] +enabled = <%= scope.lookupvar('fail2ban::jails').include? "imap" %> +filter = dovecot +action = iptables[name=IMAP, port=imap, protocol=tcp] +<% unless scope.lookupvar('fail2ban::mailto').empty? -%> + sendmail-whois[name=POP3, dest=<%= scope.lookupvar('fail2ban::mailto') %>, sender=fail2ban@<%= fqdn %>] +<% end -%> +logpath = /var/log/maillog +maxretry = 5 + +[pop3-iptables] +enabled = <%= scope.lookupvar('fail2ban::jails').include? "pop3" %> +filter = mail +action = iptables[name=POP3, port=pop3, protocol=tcp] +<% unless scope.lookupvar('fail2ban::mailto').empty? -%> + sendmail-whois[name=POP3, dest=<%= scope.lookupvar('fail2ban::mailto') %>, sender=fail2ban@<%= fqdn %>] +<% end -%> +logpath = /var/log/maillog +maxretry = 5 + +[ssh-iptables] +enabled = <%= scope.lookupvar('fail2ban::jails').include? "ssh" %> +filter = sshd +action = iptables[name=SSH, port=ssh, protocol=tcp] +<% unless scope.lookupvar('fail2ban::mailto').empty? -%> + sendmail-whois[name=SSH, dest=<%= scope.lookupvar('fail2ban::mailto') %>, sender=fail2ban@<%= fqdn %>] +<% end -%> +<% if operatingsystem == "Debian" -%> +logpath = /var/log/auth.log +<% else -%> +logpath = /var/log/secure +<% end -%> +maxretry = 5 + +[vsftpd-iptables] +enabled = <%= scope.lookupvar('fail2ban::jails').include? "vsftpd" %> +filter = vsftpd +action = iptables[name=VSFTPD, port=ftp, protocol=tcp] +<% unless scope.lookupvar('fail2ban::mailto').empty? -%> + sendmail-whois[name=VSFTPD, dest=<%= scope.lookupvar('fail2ban::mailto') %>, sender=fail2ban@<%= fqdn %>] +<% end -%> +logpath = /var/log/vsftpd.log +maxretry = 5 +bantime = 1800 diff --git a/templates/spec.erb b/templates/spec.erb new file mode 100644 index 0000000..87b8c1e --- /dev/null +++ b/templates/spec.erb @@ -0,0 +1,8 @@ +# This is a template used only for rspec tests + +# Yaml of the whole scope +<%= scope.to_hash.reject { |k,v| !( k.is_a?(String) && v.is_a?(String) ) }.to_yaml %> + +# Custom Options +<%= options['opt_a'] %> +<%= options['opt_b'] %> diff --git a/tests/absent.pp b/tests/absent.pp new file mode 100644 index 0000000..b5c3bd5 --- /dev/null +++ b/tests/absent.pp @@ -0,0 +1,5 @@ +# Class removal +# +class { 'fail2ban': + absent => true, +} diff --git a/tests/disable.pp b/tests/disable.pp new file mode 100644 index 0000000..cc432c2 --- /dev/null +++ b/tests/disable.pp @@ -0,0 +1,5 @@ +# Class' service disabling +# +class { 'fail2ban': + disable => true, +} diff --git a/tests/init.pp b/tests/init.pp new file mode 100644 index 0000000..bf25d6c --- /dev/null +++ b/tests/init.pp @@ -0,0 +1,4 @@ +# Default class installation +# +class { 'fail2ban': +}