Puppet: Pruefung auf Syntaxfehler von .pp und .erb Dateien

In dem Blogeintrag Konfiguration in GIT Repository verwalten / push-to-deploy habe ich den update Hook verlinkt, der eine Syntaxpruefung von .pp Dateien macht. Diesen habe ich heute morgen noch erweitert, so das auch die Templates mit der Endung .erb ueberprueft werden.

Wer die beiden Kommandos manuell ausfuehren moechte, sie sind wie folgt:

  1. Pruefen der Syntax von .pp Dateien:
    puppet parser validate /path/to/file.pp

    Im Fehlerfall gibt es eine Ausgabe wie zum Beispiel:

    Error: Could not parse for environment production: Syntax error at 'jails' at /home/user/git/puppet/modules/fail2ban/manifests/init.pp:13
  2. Pruefen der Syntax von .erb Template Dateien:
    erb -x -T '-' /path/to/file.erb  | ruby -c

    Im Fehlerfall gibt es eine Ausgabe wie zum Beispuel:

    -:44: syntax error, unexpected ')', expecting $end
    ...:jails').include? "ssh" ).to_s); _erbout.concat "n"
    ...

Wie bereits frueher geschrieben, richtig praktisch ist das ganze als update Hook im Git Repository. Die Fehler werden zwar trotzdem nicht zu 100% korrekt abgefangen, dafuer muss man noch Tests ausfuehren (dazu spaeter mehr), aber fuers Erste fangen die beiden Pruefungen schon eine Menge ab.

Bildschirmfoto vom 2014-05-07 07:26:27

Puppet: Service bei Aenderung neu starten / fail2ban

Heute moechte ich mein fail2ban Modul vorstellen. Neu dabei ist, dass der Service neu gestartet wird, wenn sich eine Datei aendert. Das ist wichtig, damit die Einstellungen auch uebernommen werden.

Die Verzeichnisstruktur sieht wie folgt aus:

├── files
│   └── etc
│       └── fail2ban
│           └── action.d
│               └── sendmail-whois-lines.conf
├── manifests
│   └── init.pp
└── templates
    └── jail.local.erb

In der init.pp ist wird in dem file Abschnitt ein „notify“ in Richtung des Services aufgerufen. Das fuehrt dazu, dass wenn die Datei auf dem Node geaendert wird, auch der Service benachrichtigt wird damit dieser neu startet. Siehe dazu auch „Restart service when file changes“ aus dem Puppet CookBook. Folgendermassen sieht die init.pp aus:

# class to configure fail2ban
 
class fail2ban (
  $jails       = [],
  $sshport     = 'ssh',
  $apacheport  = 'http,https',
  $proftpdport = 'ftp,ftp-data,ftps,ftps-data',
  $postfixport = 'smtp,ssmtp',
  $dovecotport = 'smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s',
  ) {
 
  package { 'fail2ban':
    ensure => installed
  }
 
  service { 'fail2ban':
    ensure => running,
    enable => true,
  }
 
  file { '/etc/fail2ban/jail.local':
    ensure  => 'present',
    content => template('fail2ban/jail.local.erb'),
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    notify  => Service['fail2ban'],
  }
 
  file { '/etc/fail2ban/action.d/sendmail-whois-lines.conf':
    ensure  => 'present',
    source  => 'puppet:///modules/fail2ban/etc/fail2ban/action.d/sendmail-whois-lines.conf',
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    notify  => Service['fail2ban'],
  }
 
}

Mein Template jail.local.erb ist wie folgt:

#############################################################################
#                                                                           #
# !!! This file is managed by puppet, all manual changes will be lost !!!   #
#                                                                           #
#############################################################################
 
 
 
#
# Local Fail2Ban configuration file.
#
 
 
[DEFAULT]
ignoreip = 127.0.0.1/8
bantime  = 600
maxretry = 3
backend = auto
destemail = fail2ban@localhost
 
 
 
#
# ACTIONS
#
 
banaction = iptables-multiport
mta = sendmail
protocol = tcp
chain = INPUT
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"]
action = %(action_mwl)s
 
 
 
 
#
# JAILS
#
 
[ssh]
enabled  = <%= scope.lookupvar('fail2ban::jails').include? "ssh" %>
port     = <%= sshport %>
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 6
 
 
 
[ssh-ddos]
enabled  = <%= scope.lookupvar('fail2ban::jails').include? "ssh-ddos" %>
port     = <%= sshport %>
filter   = sshd-ddos
logpath  = /var/log/auth.log
maxretry = 6
 
 
 
[apache]
enabled  = <%= scope.lookupvar('fail2ban::jails').include? "apache" %>
port     = <%= apacheport %>
filter   = apache-auth
logpath  = /var/log/apache*/*error.log
maxretry = 6
 
 
 
[proftpd]
enabled  = <%= scope.lookupvar('fail2ban::jails').include? "proftpd" %>
port     = <%= proftpdport %>
filter   = proftpd
logpath  = /var/log/proftpd/proftpd.log
maxretry = 6
 
 
 
[postfix]
enabled  = <%= scope.lookupvar('fail2ban::jails').include? "postfix" %>
port     = <%= postfixport %>
filter   = postfix
logpath  = /var/log/mail.log
 
 
 
[dovecot]
enabled = <%= scope.lookupvar('fail2ban::jails').include? "dovecot" %>
port    = <%= dovecotport %>
filter  = dovecot
logpath = /var/log/mail.log

Auch im Template gab es den scope.lookupvar Aufruf bisher nicht. Die Rueckgabe ist true oder false und so kann man nur die Dienste aktivieren, die auch manuell definiert wurden. Weitere Informationen gibt es in der Puppetlabs Doku unter Out-of-Scope Variables.

Definiert wird das ganze dann in der sites.pp wie folgt:

node 'node1.example.org' inherits default {
  class { 'fail2ban':
    jails   => [ 'ssh', 'ssh-ddos', 'apache' ],
    sshport => '2222',
  }
}

Zu der sendmail-whois-lines.conf gibts noch nen separaten Blogeintrag

Puppet: Ein Modul mit mehreren Klassen

Bisher hab ich immer nur pro Modul eine Klasse definiert. Es existiert an dieser Stelle aber keine Beschraenkung. In der init.pp wird eine Klasse definiert die den Modulnamen tragen muss. Daneben koennen aber weitere .pp Dateien liegen die ebenfalls Klassen implementieren.

Ich persoenlich habe auf verschiedenen Servern verschiedene Dateien unter /etc/cron.d/ abgelegt die ich auch in puppet ueberfuehrt habe. Da ich nicht alle Cronjobs auf allen Nodes deployen (und auch ausfuehren) moechte, gleichzeitig aber nicht fuer jedes Datei ein eigenes Modul definieren will, habe ich ein Modul „cronjobs“ erzeugt mit verschiedenen Klassen. Hier eine beispielhafte Struktur:

├── files
│   └── etc
│       └── cron.d
│           └── foo
└── manifests
    ├── foo.pp
    └── init.pp

Die init.pp hat fast keinen Inhalt:

# class that contains several cronjobs on different hosts
 
class cronjobs () {
 
}

Die Datei foo.pp sieht wie folgt aus:

# place cron.d file for foo job
 
class cronjobs::foo {
 
  file { '/etc/cron.d/foo':
    ensure => 'file',
    owner  => 'root',
    group  => 'root',
    mode   => '0644',
    source => 'puppet:///modules/cronjobs/etc/cron.d/foo'
  }
}

Wichtig ist hier, dass die Klasse MODULNAME::KLASSE heisst. Genauso wird diese Klasse dann auch in einem Node definiert:

node 'client1.example.org' inherits default {
 
  class { 'cronjobs::foo': }
}

Haette man in dem gleichen Modul noch eine Klasse cronjobs::bar koennte man diese in einem anderen Node definieren. So kann ich in einem Modul verschiedene Cronjob Dateien haben die auf unterschiedlichen Hosts deployt werden.

Puppet: Mailaliases erzeugen

Mir ist es immer wichtig, dass ich die Mails von meinen Servern bekomme. Dafuer habe ich logcheck installiert und einen Mailalias an den alles geschickt wird. Auch lasse ich mir die Mails an root immer an eine eigene Adresse weiterleiten.

Mit puppet kann man sicherstellen, dass Aliase existieren. Meine Klasse dafuer sieht wie folgt aus:

# make sure that the alias for root and logcheck is set correct
 
class aliases {
 
  file { '/etc/aliases' :
    mode  => '0644',
    owner => 'root',
    group => 'root',
  }
 
  mailalias { 'root' :
    name      => 'root',
    recipient => 'user@example.org',
    notify    => Exec['newaliases'],
  }
 
  mailalias { 'logcheck' :
    name      => 'logcheck',
    recipient => 'logcheckalias@example.org',
    notify    => Exec['newaliases'],
  }
 
 exec { 'newaliases':
    command     => '/usr/bin/newaliases',
    refreshonly => true,
    subscribe   => File['/etc/aliases'],
  }
}

Informationen zu dem „mailalias“ und dem „exec“ Type gibt es in der puppet Type Reference: