SSH Konfiguration: Privat, Arbeit, teilen mit Kollegen?!

Ich habe meine .ssh/config Datei mit den Einträgen die ich benötige. In dieser Datei habe ich Einträge für dienstliche Systeme aber auch private. Aus diesem Grund kann die Datei nicht einfach mit anderen Kollegen geteilt werden. Wie das dennoch erreicht werden kann, kurz und pragmatisch hier beschrieben:

Als allererstes muss ich die Konfigurationsdatei aufsplitten können in mehrere Teile. Also ein ~/.ssh/config.d/ Verzeichnis anstatt einer einzelnen Datei. SSH unterstützt das von Haus aus nicht, man kann sich aber an dieser Stelle eines Tricks bedienen: ein Alias für das Kommando „ssh“, das bei jedem Aufruf die .ssh/config einfach neu schreibt. Dafür die folgende Zeile in die .bash_aliases  eintragen:

alias ssh="cat ~/.ssh/config.d/* > ~/.ssh/config; ssh"

Als zweites gilt es nun die Dateien aufzuspalten. Ich persönlich pflege drei Dateien:

  1. general
  2. privat
  3. work

In der ersten Datei habe ich Optionen, die für alle Hosts gelten:

Host *
Compression             yes
CompressionLevel        9
ServerAliveInterval     30
#VisualHostKey          yes

In den anderen Dateien sind die Definitionen für die privaten und die Arbeitsrechner.

Der dritte Punkt ist der einfachste: Die Dateien einfach in ein Repository legen, symbolische Links aus dem Repository in das config.d Verzeichnis erzeugen und mit den Kollegen teilen.

Natürlich Backups zwischendrin etc. nicht vergessen ;-)

Snippet: Fingerprint eines SSH Keys anzeigen

Gerade stellte sich die Frage ob ich auch den korrekten public SSH-Key irgendwohin kopiere und zum Abgleich ist der Fingerprint deutlich angenehmer als alles andere, deswegen:

user@host ~ $ ssh-keygen -lf .ssh/work.pub 
2048 a1:b2:c3:d4:e5:f6:a7:b8:c9:d1:e2:f3:a4:b5:c6:d7  user@host (RSA)

Puppet: Testen in einer Testumgebung – Hallo Vagrant

Wenn ich mit Puppet arbeite passiert dieses leider viel zu häufig direkt auf dem Livesystem. Das ist natürlich Mist und darf so nicht sein. Eine Lösung ist Vagrant. Damit können sehr einfach „Wegwerf-VMs“ erzeugt werden um die Puppetkonfiguration zu testen. Wie das geht möchte ich in diesem Blogeintrag kurz festhalten.

Als Vorbereitung ist auf meinem PC das Paket virtualbox aus den Standardrepositories und Vagrant von deren Webseite installiert.

Um nun eine VirtualBox mit Ubuntu 12.04 LTS 64bit über Vagrant zu starten sind nur wenige Schritte notwendig:

mkdir ~/vagrant
cd ~/vagrant
touch HELLO_I_WAS_HERE
vagrant init puppetlabs/debian-7.4-64-puppet
vagrant up

Die Ausgabe sollte ungefähr so aussehen:

user@host:~/vagrant$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'hashicorp/precise64' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: >= 0
==> default: Loading metadata for box 'hashicorp/precise64'
    default: URL: https://vagrantcloud.com/hashicorp/precise64
==> default: Adding box 'hashicorp/precise64' (v1.1.0) for provider: virtualbox
    default: Downloading: https://vagrantcloud.com/hashicorp/precise64/version/2/provider/virtualbox.box
==> default: Successfully added box 'hashicorp/precise64' (v1.1.0) for 'virtualbox'!
==> default: Importing base box 'hashicorp/precise64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'hashicorp/precise64' is up to date...
==> default: Setting the name of the VM: vagrant_default_1408706120277_30417
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default: 
    default: Guest Additions Version: 4.2.0
    default: VirtualBox Version: 4.1
==> default: Mounting shared folders...
    default: /vagrant => /home/user/vagrant
user@host:~/vagrant$

Mit dem Befehl vagrant ssh kann nun eine SSH-Verbindung zu der VM hergestellt werden. Der Ordner ~/vagrant in dem wir uns befinden ist in der VM unter /vagrant/ verfügbar:

user@host:~/vagrant$ vagrant ssh
Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com/
New release '14.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
 
Welcome to your Vagrant-built virtual machine.
Last login: Fri Sep 14 06:23:18 2012 from 10.0.2.2
vagrant@precise64:~$ ls -l /vagrant
total 20
-rw-r--r-- 1 vagrant vagrant    0 Aug 22 11:30 HELLO_I_WAS_HERE
-rw-r--r-- 1 vagrant vagrant 4826 Aug 22 11:11 Vagrantfile
vagrant@precise64:~$

Die VM kann mit vagrant destroy wieder gelöscht werden.

In der Datei Vagrantfile wird die VM konfiguriert. Das praktische ist, dass dort Puppet eingebunden werden kann, so dass beim Hochfahren der VM dieses gleich ausgeführt wird. Um nun meine Konfiguration zu testen kopiere ich das puppet Verzeichnis in den Ordner vagrant. Kopieren ist wichtig, ich habe zuerst einen symbolischen Link erzeugt, aber die VM kann dem ja nicht folgen 8-)

scp -r puppetmaster:/etc/puppet ~/vagrant/

Anschließend sind in der Vagrantfile zwei Optionen zu konfigurieren. Erstens den Hostnamen der VM, damit wenn Puppet ausgeführt wird dieser korrekt ist und die richtigen Einstellungen übernommen werden. Zweitens den Pfad zu dem von puppet benötigten Manifest und den Modulen:

  config.vm.hostname = "mynode.example.org"
 
  config.vm.provision "puppet" do |puppet|
    puppet.manifests_path = "puppet/manifests"
    puppet.manifest_file  = "site.pp"
    puppet.module_path = "puppet/modules"
    #puppet.options = "--verbose --debug"
  end

Beim Starten der VM wird Puppet dann automatisch ausgeführt. Läuft die VM bereits kann das mit dem Befehl vagrant provision erneut angestoßen werden.

Auf diese Art- und Weise kann die Puppetkonfiguration in einer VM getestet werden bis alles so ist wie ich es haben möchte.

Zum Schluss:

  • Die Virtualbox wird standardmäßig im headless Mode gestartet. Wer über die Applikation eine GUI möchte muss dafür den folgenden Codeschnippsel in seine Vagrantfile hinzufügen:

      config.vm.provider "virtualbox" do |v|
             v.gui = true
      end
  • Damit beim ersten hochfahren der VM als erstes die Paketquellen aktualisiert werden die Datei bootstrap.sh mit dem folgenden Inhalt in dem Verzeichnis ~/vagrant/ ablegen:

    #!/bin/bash
    apt-get update

    und danach in der Vagrantfile VOR den puppet Block die folgende Zeile einfügen:

      config.vm.provision :shell, path: "bootstrap.sh"
  • Webseite des Projektes: www.vagrantup.com
  • Viele fertige Vagrant Boxes gibt es in der Vagrant Cloud: vagrantcloud.com
  • Für die vielen Einstellmöglichkeiten in der Vagrantfile gibt es eine Dokumentation unter: docs.vagrantup.com

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