Infrastructure as secure code

Im Rahmen meiner Abschlussarbeit habe ich mich damit beschäftigt den Code des Konfigurationsmanagementsystems Puppet automatisiert auf unsichere Praktiken zu analysieren. Herausgekommen ist ein puppet-lint Plugin zur Prüfung von Puppetcode aus mögliche Sicherheitsrisiken.

Das Plugin steht auf rubygems.org zur Verfügung. Der Quellcode ist auf Github verfügbar. Die eingebauten Sicherheitschecks sind als Basis zu verstehen und sollten auf die eigenen Bedürfnisse angepasst werden. Gerne können und sollten weitere Checks eingebaut werden.

Gem Version

Nun noch ein paar Hintergrundinfos.

Continue reading

post

Multienvironment Puppet

Im Puppet-Umfeld passiert immer noch relativ viel. Standards ändern sich und werden mit deprecated markiert. Daher hier eine Zusammenfassung für eine aktuelle Puppet-Umgebung.

Verzeichnisstruktur

Wer mit verschiedenen Umgebungen (production/testing) arbeitet und keinen external node classifier verwendet und node Definitionen lieber in klassisch in „node.pp“ Files ablegt, braucht eine Verzeichnisstruktur für die Unterscheidung der Umgebungen. Best Practice aktuell (Puppet v3.7.4) ist in der /etc/puppet/puppet.conf:

Die entsprechende Verzeichnisstruktur für die Ablage der Nodedefinitionen  sieht dann so aus:

 Hiera

Wer dem Design Pattern „Separation Of Data And Code“ folgt (und ich kann nur empfehlen es zu tun) fragt sich früher oder später wie er die ganzen Default Werte und Übergabeparameter an Puppet Klassen aus dem Code oder aus den Nodedefinitionen wieder herausbekommt. Im ersten Schritt wird man eine params.pp (Siehe https://docs.puppetlabs.com/guides/module_guides/bgtm.html) bauen und da zumindest die ganzen Defaultwerte einbauen. Aber so richtig los wird man das Ganze mit Hiera.
Hier werden alle Daten, ob Defaultwerte oder speziellere Daten, die z.B. pro Umgebung/Betriebssystem/Rechner/… unterschiedlich sind, in YAML Files abgelegt. Wie der Name „Hiera“ schon sagt ist der Clou an der Sache das hierarchische Element. So kann man Werte mehrfach definieren. Hiera geht dann, ähnlich wie bei Firewallregeln, von oben nach unten durch und nimmt den ersten Treffer. Die Konfiguration ist trivial:

Auf der Suche nach dem Wert einer Variable sucht Hiera in /etc/puppet/hieradata nach Files die auf den Rechnernamen, die Umgebung oder „common“ matchen und liest den Wert aus der ersten passenden Datei, welche die entsprechende Variable enthält. Im Puppet Code kann man Hiera auf zwei Arten nutzen.

1. Nutzung der Funktion hiera()

 2. Implizit über Übergabeparameter

Das ist mein Favorit. Ggf. benötigte Parameter werden als Übergabeparameter in der Puppetklasse definiert:

Diese werden dann automatisch ohne Angabe in der Nodedefinition in Hiera gesucht und genutzt, wenn vorhanden.

Beispiel

  • Rechnername: web01.domain.tld
  • Umgebung: production

Folgende Hieradateien sind abgelegt (hiera.yaml wie oben angegeben):

Somit wird eine Variable „$pwd“ der Klasse „web„, bzw. „hiera(‚web::pwd‘)“ zuerst in der „web01.domain.tld.yaml„, dann in der „production.yaml“ und falls sie noch nicht gefunden wurde in der „common.yaml“ gesucht. Die YAML Datei könnte wie folgt aussehen:

Vorteil ist, dass nun sehr einfach in jeder Umgebung ein anderes Passwort gesetzt sein könnte und das passende einfach so ohne irgendwelche If-Abfragen im Puppet genutzt wird.

post

Ruby on Rails | Massenverarbeitung mit ActiveRecord

Bei der produktiven Nutzung eines von mir entwickelten Tools hat es zugeschlagen: Es ist halt doch etwas anderes mit ein paar Testdatensätzen oder mit ein paar Hunderttausend Datensätzen zu arbeiten. Die Laufzeiten für INSERTs und UPDATEs gingen gar nicht. Also was tun? Das Projekt ist mit ActiveRecord realisiert.

Etwas Recherche hat gezeigt: Da geht einiges!

Abrufen von Datensätzen

Das Abrufen von Datensätzen via ActiveRecord kann über verschiedene Techniken beschleunigt werden. Erster Ansatzpunkt sind die Funktionen zum Abruf von Datensätzen in Bündeln (Batches). Hierzu bietet ActiveRecord bereits Funktionen an, so dass eine große Menge an Datensätzen nicht auf einmal, sondern stapelweise und somit schonend für Arbeitsspeicher und Swap Bereich verarbeitet werden kann (Retrieving Multiple Objects in Batches).

Generell sollte man die Menge an Datensätzen aus der Datenbank bereits bei der Abfrage weitestgehend reduzieren um im Programmcode möglichst nur noch mit den relevanten Datensätzen zu arbeiten. Es sollten also nicht die Ruby Methoden find bzw. find_all zum Filtern von Datensätzen genutzt werden, sondern besser bereits bei der Abfrage entsprechende Einschränkungen gemacht werden.

Also so eher nicht:

Besser:

Bei n:m Beziehungen sollte man mit der ‚through‘ Anweisung arbeiten um die Beziehungen zwischen Objekten darzustellen (Siehe http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association). ActiveRecord generiert dann SQL mit entsprechenden JOIN Anweisungen, anstatt über mehrere SELECTs die Anfragen zusammenzubauen.

UPDATEs auf vielen Datensätzen

Updates auf vielen Datensätzen ist ineffizient, wenn für jeden Datensatz ein eigenes UPDATE Statement generiert wird. Hier schafft update_all Abhilfe. Man selektiert vorab alle Datensätze die aktualisiert werden sollen und kann dann diese in einem Rutsch bearbeiten:

Somit wird ein UPDATE Statement für alle Datensätze erzeugt.

Massen-INSERT

Das massenhafte Anlegen neuer Datensätze frisst ebenso sehr viel Performance, da für jeden INSERT eine eigene Transaktion gestartet wird. Hier kann man manuell eingreifen und ActiveRecord beibringen alle INSERTs ein einer Transaktion zu verarbeiten:

post

Jenkins SVN Plugin mag kein htaccess

Das aktuelle Jenkins SVN Plugin in der Version 2.5 (https://wiki.jenkins-ci.org/display/JENKINS/Subversion+Plugin) ist leider etwas buggy. Unser SVN läuft unter Linux/Apache mit Kerberos Authentifizierung, so dass man sich normalerweise ganz einfach via Basic Authentifizierung anmelden kann. Mit älteren Jenkins SVN Plugin Versionen ist das auch keine Problem. Allerdings scheint die aktuelle Version hier fehlerhaft zu arbeiten. Jenkins versucht gar nicht erst die Credentials via Basic Authentifizierung weiterzugeben, sondern scheint nur noch NTLM zu versuchen. Im Apache Log kommt nicht einmal ein Username an.

Was hilft: Eine ältere Version (z.B. 1.54) verwenden. Zumindest scheint schon ein Bug offen zu sein:

https://issues.jenkins-ci.org/browse/JENKINS-27084

Performanceprobleme bei virtualisierter Astaro Firewall

Beim Einsatz der Astaro Security Gateway Virtual Appliance für VMware auf einem VMWare ESXi 5 Hypervisor sind eklatante Performanceprobleme aufgetreten. Die Latenz z.B. beim Aufruf von Webseiten war sehr hoch und alles gefühlt „langsam“. Laut Internet scheint das Problem primär bei der Kommunikation zu virtuellen Maschinen, die auf dem selben ESXi Host laufen, zu aufzutreten. Die Umstellung des Netzwerkkartentyps von „flexibel“ auf „e1000“ in VMWare behebt das Problem.

xVM finally dead :-(

Leider scheint die Solaris Portierung von Xen nun endgültig rausgeflogen zu sein. Nach einem Upgrade von Solaris 11 Express auf Solaris 11 gibt es leider weder einen xend noch das Hypervisorfile xen.gz. Um das offizielle Statement hierzu zu finden musste man etwas suchen. Aus http://www.oracle.com/technetwork/systems/end-of-notices/eonsolaris11-392732.html:

xVM Hypervisor
xVM hypervisor, the Oracle Solaris Xen-based hypervisor for x86 systems, has been removed. Oracle offers two x86-based hypervisor solutions for Oracle Solaris users: Oracle VM Server for x86 and Oracle VM VirtualBox. See http://www.oracle.com/virtualization

Schade 🙁

Change default browser on lion per script

Ich verwende einen Browser @work und einen anderen @home. Mittels locationchanger (http://tech.inhelsinki.nl/locationchanger/) möchte ich diesen umschalten, was unter OS X nicht ohne weiteres möglich ist. Ein Wechsel von Firefox auf Safari ginge zwar z.B. so:

defaults write com.apple.LaunchServices LSHandlers "defaults read com.apple.LaunchServices LSHandlers | sed -e 's/org.mozilla.firefox/com.apple.safari/'"

Leider muss jedoch anschließend dem System mitgeteilt werden, dass sich hier etwas geändert hat. Hierzu habe ich nichts sinnvolles gefunden. Allerdings gibt es duti (http://duti.sourceforge.net/). Damit ist es ganz einfach:

Safari:

/usr/local/bin/duti -s com.apple.safari http
/usr/local/bin/duti -s com.apple.safari https

Firefox:

/usr/local/bin/duti -s org.mozilla.firefox https
/usr/local/bin/duti -s org.mozilla.firefox http

 

Xen + ZFS in a box performance

Leider musste ich immer wieder feststellen, dass ZFS sehr (und ich meine sehr) Performance hungrig ist. Dies führt immer wieder dazu, dass die Performance der VMs die im xVM laufen beeinträchtigt wird. Workaround: PIN der Dom0 CPU auf einen Core und Nutzung der verbleibenden Cores für die VMs. Dies geht im laufenden Betrieb so:


xm vcpu-set Domain-0 1 # Nur eine vCPU für die Dom0
xm vcpu-pin Domain-0 0 0 # Diese eine vCPU soll nur auf Core 0 laufen
xm vcpu-pin VM all 1-3 # Die vCPU(s) der VM sollen nur Cords 1-3 nutzen

Erfreulicherweise bekommt die libvirt dies auch gleich mit und fügt ein <vcpu cpuset='1-3'>1</vcpu> in die XML Config mit ein.

Damit die Dom0 nach einem Reboot mit einer vCPU und nur auf Core 0 läuft müssen dem Hypervisor die Optionen dom0_vcpus_pin=true dom0_max_vcpus=1 mitgegeben werden.

ZFS root Dateisystem

Leider musste ich diese Tage ein paar Einschränkungen des ZFS Pools mit dem root Dateisystem meines Servers feststellen. Aktuell verwende ich zwei gespiegelte Platten für diesen Pool. Erster Punkt: Schreiben ist aus unerfindlichen Gründen langsam. Mehr wie 2-3 MB/s gehen nicht. Daher war der Plan den Spiegel um zwei weitere Platten zu erweitern (Stripped Mirror). Anscheinend geht das aber nicht. Man kann den Root Pool nur zu einem dreifach Spiegel erweitern. Das Anhängen eines weiteren Mirrors aus zwei Platten wird nicht erlaubt und schlägt mit der Meldung
„cannot add to ‚rpool‘: root pool can not have multiple vdevs or separate logs“
fehl. Somit macht es für mich keinen Sinn den Root Pool intensiver zu Nutzen. Man braucht immer einen zweiten Pool.