Puppet: Klassen in der Nodekonfiguration aus Hiera einbinden

Nachdem Hiera installiert ist moechte ich aufschreiben, wie man in der site.pp Klassen in der Nodekonfiguration aus Hiera einbindet. Dieser Blogeintrag baut inhaltlich auf der Struktur auf, die ich unter Puppet: Hiera und Puppet – Installation, Einrichten, Moduleinbindung beschrieben habe.

Im ersten Schritt werden die Klassen fuer den default Node extrahiert. Der Eintrag in meiner site.pp sieht vorher wie folgt aus:

node default {
  class { 'foo': }
  class { 'bar': }
  class { 'baz::moo': }
  include users::mmuster
}

Die Klassen werden dann in der folgenden Syntax in die common.yaml uebertragen:

---
classes:
  - foo
  - bar
  - baz:moo

Danach kann die Nodedefinition in der site.pp wie reduziert werden. Wichtig ist den abschliessenden whitespace nach dem Komma zu beruecksichtigen:

node default {
  hiera_include('classes','')
  include users::mmuster
}

Das ganze ist auch in der offiziellen Puppet Dokumentation beschrieben unter „Assigning Classes to Nodes With Hiera (hiera_include)„.

Puppet: Hiera und Puppet – Installation, Einrichten, Moduleinbindung

Hiera ist eine key-/value Datenbank von Konfigurationsdaten fuer Puppet. Als ersten Schritt moechte ich aufzeigen wie man es installiert, minimal einrichtet und testweise in ein Modul einbindet.

Zuerst muss auf dem Puppetmaster Server (ich arbeite unter Debian Wheezy) das folgende Paket inklusive seiner Abhaengigkeiten installiert werden:

aptitude install ruby-hiera-puppet

Nach der Installation muss eine Konfigurationsdatei fuer Hiera unter Puppet erzeugt werden. Die Datei ist per Definition die /etc/puppet/hiera..yaml. Sie hat folgenden Inhalt:

---
:backends:
    - yaml

:hierarchy:
    - common

:yaml:
    :datadir: '/etc/puppet/hieradata'

Erklaerung der hier gemachten Einstellungen:

  • : Definiert den Beginn der Datei
  • :backends: Gibt das Format an in dem die die Daten vorliegen. In diesem Fall ist YAML definiert.
  • :hierarchy: Definiert die Hierarchie der Datenquellen. In diesem Fall gibt es nur eine moegliche Datenquelle ‚common‘
  • :yaml::datadir:Definiert fuer das YAML Backend das Verzeichnis in dem die Daten gespeichert sind. In diesem Fall das Verzeichnis /etc/puppet/hieradata

Sicherstellen das das gerade angesprochene Verzeichnis existiert:

mkdir -p /etc/puppet/hieradata

Anschliessend die folgende Datei common.yaml in dem Verzeichnis anlegen:

---
testmessage: hallo welt

Testen ob alles funktioniert kann man das ganze danach mit dem folgenden Aufruf:

hiera -c /etc/puppet/hiera.yaml testmessage

Zum Schluss noch ein Beispiel wie man den Wert nun in Puppet in einer Klasse ausliesst und in einer Datei speichert:

class hieratest {
    $testmessage = hiera("testmessage")
    file { '/tmp/hieratest.txt':
        content => inline_template("<%= testmessage %>n"),
    }
}

Wenn man daraus zum Beispiel ein Modul ‚hieratest‘ macht, auf einem Node einbindet und Puppet laufen laesst, dann findet man dort die Datei /tmp/hieratest.txt mit dem definierten Inhalt ‚hallo welt‘.

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-)