Red Green Repeat Adventures of a Spec Driven Junkie

Puppet and fail2ban & Email Login Notifications

I wrote a series of articles about how unsafe it is to leaving an openly accessible server.

Let’s create a manifest for the fail2ban and email login notifications I talk about and to combine them with the two factor authentication manifest from my last article.

From the last article…

Just for reference, the Vagrantfile used for every manifest in this article and the modules:

Vagrantfile

Vagrant.configure("2") do |config|
  config.vm.box = 'ubuntu/trusty64'
  # resolves error: ==> default: Warning: Could not retrieve fact fqdn
  config.vm.hostname = "vagrant.example.com"

  # http://www.terrarum.net/blog/masterless-puppet-with-vagrant.html
  config.vm.provision 'shell', :inline => <<-SHELL
	apt-get purge -y puppet # remove puppet3
	wget https://apt.puppetlabs.com/puppet5-release-trusty.deb
	dpkg -i ./puppet5-release-trusty.deb
	apt-get update
	apt-get install -y puppet-agent # install puppet5
  SHELL

  # https://github.com/mitchellh/vagrant/issues/3740#issuecomment-92106636
  config.vm.provision 'puppet' do |puppet|
	puppet.environment_path = 'environments'
	puppet.environment = 'production'
	puppet.module_path = 'modules'
  end
end

After running vagrant up, using vagrant provision is enough to apply the new puppet manifests.

Modules

Install modules in the modules folder of the vagrant directory. The most used module in the manifests will be the stdlib module:

$ puppet module install stdlib
$ cp -r ~/.puppet/modules /vagrant/              # puppet 3
$ cp -r /opt/puppetlabs/puppet/modules /vagrant  # puppet 5

fail2ban Manifest

The fail2ban manifest can be trivial, let’s create manifests for:

  • just fail2ban
  • extending the bantime
  • fail2ban email notifications

Just fail2ban

This manifest is easy as the instructions for getting fail2ban installed is:

$ sudo apt-get install fail2ban

The manifest can be just:

environment/productions/manifests/fail2ban.pp:

package { 'fail2ban':
  ensure => 'installed',
  status => 'running'
}

Extending bantime

To configure the bantime from default of 5 minutes (300 seconds) to 1 hour (86400 seconds), by adding a configuration in the jail.conf file using the file resource:

The /etc/fail2ban/jail.local content desired is:

bantime = 86400

The manifest grows with the file resource and now looks like:

environment/productions/manifests/fail2ban.pp:

package { 'fail2ban':
  ensure => 'installed',
  status => 'start'
}

file { 'jail.local':
  ensure  => 'present',
  path    => '/etc/fail2ban/jail.local',
  content => "bantime = 86400"
}

The manifest creates the content of the jail.local file inline with the content option. The path option specifies exactly where and the filename. Creating files this way reduces additional dependencies.

fail2ban Email Notifications

Let’s increase security by having fail2ban send an email when an attacker attempts too login more than six times in five minutes. To send emails, install the sendmail package and add an additional jail.local configuration:

Including sendmail package in the manifest:

package { 'sendmail':
  ensure => 'installed'
}

The jail.local configuration needed:

destemail = [email protected]
action = %(action_mw)s

The fail2ban manifest for this would be:

environment/productions/manifests/fail2ban.pp:

package { 'fail2ban':
  ensure => 'installed',
  status => 'start'
}

package { 'sendmail':
  ensure => 'installed'
}

file { 'jail.local':
  ensure  => 'present',
  path    => '/etc/fail2ban/jail.local',
  content => "destemail = [email protected]\naction = %(action_mw)s"
}

note: Use " for the content line so \n produces a new line/return instead of writing \n in the jail.local file.

Combined fail2ban Manifest

Combining everything into one fail2ban manifest:

environment/productions/manifests/fail2ban.pp:

package { 'fail2ban':
  ensure => 'installed',
  status => 'start'
}

package { 'sendmail':
  ensure => 'installed'
}

file { 'jail.local':
  ensure  => 'present',
  path    => '/etc/fail2ban/jail.local',
  content => "bantime = 86400\ndestemail = [email protected]\naction = %(action_mw)s"
}

fail2ban Puppet Module

By using a jail.local it updates those fail2ban configurations instead all fail2ban configurations, which are in jail.conf.

If one wants to configure more options, using the fail2ban puppet module would be a better method to manage and configure this.

Email Login Notifications Manifest

The next step is to send emails when a login occurs, steps required:

  • install mailutils
  • configure pam.d to run a script on successful login
  • create a shell script for execution by pam.d

Install mailutils

The manifest will be:

package { 'mailutils':
  ensure => 'installed'
}

Configure pam.d

Now this is a bit trickier to have pam.d add a line, but using the file_line method:

file_line { 'pamd_config':
  path => '/etc/pam.d/sshd',
  line => 'session   optional      pam_exec.so /usr/local/bin/send_email.sh'
}

The path option looks for a specific file and ensures the line is in the file. Provisioning the system multiple times will insert the line once.

Create Email Shell Script

The script to send an email is a bit more complicated as it has quotes and shell variables, " & $, inside it. Puppet also accepts variables using $, so escape them using \.

The manifest file:

file { 'send_email.sh':
  ensure => 'file',
  path => '/usr/local/bin/send_email.sh',
  mode => 'ugo+x',
  content => "#!/bin/sh
  if [ \"\$PAM_TYPE\" = \"open_session\" ]
  then
  {
	echo \"User: \$PAM_USER\"
	echo \"Remote Host: \$PAM_RHOST\"
	echo \"Service: \$PAM_SERVICE\"
	echo \"TTY: \$PAM_TTY\"
	echo \"Date: `date`\"
	echo \"Server: `uname -a`\"
	} | mail -s \"\$PAM_SERVICE login on `hostname -s` for account \$PAM_USER\" <[email protected]>
  fi
  exit 0
  "
}

The mode option configures the file to be executable by the pam.d module (otherwise, pam.d will raise an error.)

Complete Manifest

environment/productions/manifests/email_login_notifications.pp:

package { 'mailutils':
  ensure => 'installed'
}

file_line { 'pamd_config':
  path => '/etc/pam.d/sshd',
  line => 'session   optional      pam_exec.so /usr/local/bin/send_email.sh'
}


file { 'send_email.sh':
  ensure => 'file',
  path => '/usr/local/bin/send_email.sh',
  mode => 'ugo+x',
  content => "#!/bin/sh
  if [ \"\$PAM_TYPE\" = \"open_session\" ]
  then
  {
	echo \"User: \$PAM_USER\"
	echo \"Remote Host: \$PAM_RHOST\"
	echo \"Service: \$PAM_SERVICE\"
	echo \"TTY: \$PAM_TTY\"
	echo \"Date: `date`\"
	echo \"Server: `uname -a`\"
	} | mail -s \"\$PAM_SERVICE login on `hostname -s` for account \$PAM_USER\" <[email protected]>
  fi
  exit 0
  "
}

Conclusion

By building up two manifests: fail2ban and email login notifications I learned how to use puppet’s file resource utilizing mode and content options. These manifests have come a long way from the ones I built earlier and these are easier to read!