Puppet: Hiera und Defined Types

puppetlabs apache vhost hiera howto

Das waren in etwa die Stichworte in der Suche als ich versucht habe meine mit dem puppetlabs/apache Modul definierten Apache vhosts aus der site.pp nach hiera zu migrieren. Es wollte nämlich einfach nicht klappen. Nach einiger Zeit bin ich dann darauf gestoßen, dass es sich bei dem apache::vhost um einen sogenannten Defined Type handelt. Diese sind sehr ähnlich zu Klassen, lassen sich aber im Gegensatz zu diesen mehrfach auf einem Node definieren.

Das automatische Parameter-Lookup wie bei den Klassen funktioniert bei Defined Types hingegen nicht. Lösung ist die create_resources() Funktion. Aber damit ich das später auch noch verstehe wenn ich hier in den Blogeintrag hineingucke schreibe ich ein praktisches Beispiel auf.

Ein Apache vhost in der site.pp:

node 'www.example.org' inherits default {
apache::vhost {'www.example.org':
  servername    => 'www.example.org',
  serveraliases => [ 'example.org' ],
  serveradmin   => 'webmaster@example.org',
  port          => '80',
  docroot       => '/var/www/example.org/www/',
}

wird in hiera zu:

---
apache::vhost:
  'www.example.org':
    servername: 'www.example.org'
    serveraliases:
      - 'example.org'
    serveradmin: 'webmaster@example.org'
    port: '80'
    docroot: '/var/www/example.org/www/'

und damit dieser Eintrag auch ausgewertet und der vhost erzeugt wird sieht die site.pp danach wie folgt aus:

node 'www.example.org' inherits default {
  $myApacheVhosts = hiera('apache::vhost', {})
  create_resources('apache::vhost', $myApacheVhosts)
}

Ich empfehle hier wärmstens das Chapter 3 – Using Hiera von Puppet Lunch. Ich habe lange im Netz gesucht und bin schließlich dort fündig geworden.

Puppet: Virtuelle Ressourcen fuer Benutzer und SSH-Keys

Puppet kann Ressourcen nur einfach definieren. Hat man eine Klasse einmal eingebunden, kann man dieses an einer zweiten Stelle nicht erneut tun. Folgendes Beispiel:

Ich deploye eine selbst entwickelte Software und moechte sie unter einem bestimmten Nutzeraccount laufen lassen. Mit Puppet stelle ich sicher, dass der Nutzeraccount existiert. Spaeter moechte ich auf dem gleichen Server eine zweite Software deployen die ebenfalls unter diesem Benutzeraccount laeuft. Ich erzeuge dafuer ein weiteres Modul das die Abhaengigkeiten abbildet. Laesst sich wunderbar alles konfigurieren, aber spaetestens auf dem Server gibt es dann einen Fehler:

err: Could not retrieve catalog from remote server: Error 400 on SERVER: Duplicate declaration: Class[FOO] is already declared; cannot redeclare at /etc/puppet/manifests/site.pp:20 on node node01.example.org

Die Loesung fuer das Problem sind virtuelle Ressourcen. Im folgenden Beispiel moechte ich aufzeigen, wie man Benutzeraccounts als virtuelle Ressourcen definiert und auf den Nodes einbindet. Die Loesung die ich hier aufzeige habe ich mir nicht selbst ausgedacht, sondern bei meinem Kollegen abgeguckt.

Eigentlich kommt man bei mehreren Benutzeraccounts auf mehreren Servern nicht um einen LDAP Server herum. Bei kleineren Setups kann man es aber auch noch auf die klassische Art- und Weise mit lokalen Konten machen.

Viele Anleitungen die man im Internet liesst zeigen Wege, bei denen man die Benutzerinformationen als Parameter an die Klasse uebergibt. Ich habe mich bewusst dagegen entschieden und alle Infos in dem Modul. Spaeter werde ich mich wohl noch mal mit Hiera beschaeftigen, dann werde ich die Informationen dort ablegen.

Die Verzeichnisstruktur meines Moduls „users“ sieht wie folgt aus:

└── manifests
    ├── init.pp
    ├── mmuster.pp
    └── virtual.pp

Die init.pp ist sehr simpel:

class users () {
  include users::virtual
}

Spannender wird es dann, wenn man die virtual.pp anschaut:

class users::virtual {
 
  @user { 'mmuster':
    ensure      => 'present',
    comment     => 'Max Mustermann,,,',
    shell       => '/bin/bash',
    managehome  => true,
    groups      => ['sudo', 'wheel' ],
    }
 
  @ssh_authorized_key { 'mmuster-privat':
    ensure      => 'present',
    type        => 'ssh-rsa',
    user        => 'mmuster',
    key         => 'AAA [...] ZZZ='
  }
}

Virtuelle Ressourcen definiert man mit einem vorgestellten @. In dem Beispiel werden zwei virtuelle Ressourcen definiert:

  1. Den Nutzeraccount mmuster
  2. Den oeffentlichen SSH-Key mmuster-privat

Die Parameter fuer den Nutzeraccount mmuster bedeuten folgendes:

  • ensure => ‚present‘: Stellt sicher, dass der Benutzeraccount existiert. Wenn nicht wird er angelegt.
  • comment => ‚Max Mustermann,,,‘: Kommentar zum Benutzeraccountt in der /etc/passwd
  • shell => ‚/bin/bash‘: Die Shell die der Benutzeraccount haben soll.
  • managehome => true: Stellt sicher das das Homeverzeichnis existiert.
  • groups => [’sudo‘, ‚wheel‘ ]: Stellt sicher, dass der Benutzeraccount Mitglied in den Gruppen sudo und wheel ist. Die Gruppen muessen im System existieren. Sie werden nicht automatisch angelegt!

Die Einstellungen fuer den SSH-Key mmuster-privat bedeuten folgendes:

  • ensure => ‚present‘: Stellt sicher, dass der SSH-Key existiert
  • type => ’ssh-rsa‘: Definiert den Verschluesselungsalgorithmus
  • user => ‚mmuster‘: Der Benutzeraccount zu dem dieser oeffentliche SSH Schlussel gehoert
  • key => ‚AAA […] ZZZ=‘:Der eigentliche public key

Fertig ist die Definition der virtuellen Ressourcen. Damit diese spaeter auch auf dem Node ausgefuehrt werden, muessen sie mit der realize Funktion markiert werden. Das passiert in der dritten Datei: mmuster.pp

class users::mmuster inherits users::virtual {
 
  realize(
    User['mmuster'],
    Ssh_authorized_key['mmuster-privat']
  )
}

Das schoene an dieser Loesung ist, dass der Nutzer nun mit einer einzigen Zeile zu dem Node hinzugefuegt werden kann:

include users::mmuster

Zum Schluss: Das Passwort wird nicht gesetzt. Man kann den Hash bei dem Nutzer mit dem Parameter password => “ mitgeben. Das finde ich aber nicht gut, weil die Nutzer dann die Passwoerter nicht selbst aendern koennen. Man findet im Netz auch Infos zu Kruecken mit denen man das Passwort nur beim Nutzer anlegen setzen kann, sollte man an diesem Punkt angelangt sein ist definitiv wieder der Zeitpunkt erreicht sich erneut Gedanken ueber einen LDAP Server zur Benutzerverwaltung zu machen 8-)

Puppet: ein Modul mit Template, Variablen und Bedingung (/etc/apt/sources.list)

Vor kurzem erst hatte ich darueber geschrieben, wie man in puppet ein eigenes Modul definiert.
Heute moechte ich zeigen, wie in einem Modul mit Templates, Variablen und if-Bedingung gearbeitet werden kann. Ich habe mir dafuer die Datei /etc/apt/sources.list ausgesucht in die ich – je nach Host – unterschiedliche Mirror eintragen moechte. Dafuer habe ich das Verzeichnis /etc/puppet/modules/apt/ mit der folgenden Ordnerstruktur angelegt:

├── manifests
│   └── init.pp
└── templates
    └── sources.list.erb

Mein Template fuer die sources.list sieht wie folgt aus:

#############################################################################
#                                                                           #
# !!! This file is managed by puppet, all manual changes will be lost !!!   #
#                                                                           #
#############################################################################
 
 
 
deb     <%= mirror %>  <%= release %>  main
<% if source -%>
deb-src <%= mirror %>  <%= release %>  main
<% end -%>
 
deb     http://security.debian.org/  <%= release %>/updates  main
<% if source  -%>
deb-src http://security.debian.org/  <%= release %>/updates  main
<% end -%>

In dem Template kann man zwei Variablen erkennen. Diese sollen bei Bedarf ueberschrieben werden koennen <%= mirror -%> und <%= release %>. Ausserdem gibt es eine Bedingung, wenn die Variable source Wahr ist, wird auch die deb-src Zeile eingefuegt.

Die apt/manifests/init.pp fuer das Modul sieht wie folgt aus:

class apt (
  $mirror = 'http://ftp.de.debian.org',
  $release = 'wheezy',
  $source = false ) {
 
  file { '/etc/apt/sources.list':
    ensure  => 'present',
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('apt/sources.list.erb'),
    }
}

Neu ist, dass in der Klassendefinition die Variablen mit entsprechenden Standardwerten definiert wurden. Das Bild wird komplett, wenn nun ein neuer Node in der sites.pp definiert wird:

node default {
  class { 'motd': }
}
 
node 'client02.example.org' inherits default {
  class { 'apt':
    mirror => 'http://debianmirror.example.org/pub/linux/debian/debian/',
    source => true
  }
}

Hier ist neu, dass der Node „client02.example.org“ explizit angegeben wurde. Er erbt die Einstellungen des default Node. Erweiternd ist die Klasse apt definiert, bei der die Variablen mirror und source einen anderen Wert annehmen als der Standartwert.

Ich bin mir dessen bewusst, dass es ein apt Modul auf puppetforge gibt, zum Lernen finde ich das hier gerade aber einfacher.

Informationen habe ich auch aus dem Blogeintrag „Puppet templates and default values for variables„.

HowTo: ASIX AX88179 USB 3.0 Gigabit Ethernet unter Ubuntu 13.10

Vor kurzem habe ich einen Digitus USB 3.0 Gigabit Ethernet Adapter bekommen. Bei diesem ist ein AX88179 Chip von ASIX verbaut. Er wurde unter Ubuntu Linux 13.10 mit einem 3.11er Kernel problemlos erkannt, funktionierte aber nicht wirklich.

Ursache dafuer scheint das Kernelmodul zu sein, was fehlerhaft ist. Es gibt zwei Wege den Adapter dennoch zum Laufen zu bekommen:

1. Drosseln auf 100Mbit:

sudo ethtool -s eth0 speed 100 duplex full antoneg off

2. Kernel Modul von Herstellerseite herunterladen und kompilieren:

cd /tmp
wget http://www.asix.com.tw/FrootAttach/driver/AX88179_178A_LINUX_DRIVER_v1.9.0_SOURCE.tar.bz2
aptitude install build-essential linux-headers-$(uname -r)
tar -xjf AX88179_178A_LINUX_DRIVER_v1.9.0_SOURCE.tar.bz2
cd AX88179_178A_LINUX_DRIVER_v1.9.0_SOURCE/
make
sudo make install

Mit der letzteren Loesung funktioniert der Adapter bei mir problemlos auch auf Gigabit.