Select Page

NETWAYS Blog

Logstash-Konfiguration im Team

Das folgende Setup hat sich als Entwurf bei einem Kundenprojekt ergeben. Es ist noch nicht in die Realität umgesetzt, aber ich fand es interessant genug, um es hier teilen zu wollen.

Aufgabe

Hier kurz die Ausganslage, für die das Konzept erstellt wurde.

  • Mehrere Teams wollen Logs über den Elastic Stack verarbeiten
  • Die Logs sind teilweise Debuglogs aus Eigenentwicklungen. (Erfahrene Logmanagement-Admins lesen hier “sich häufig ändernde und nicht immer klar strukturierte Logformate”)
  • Die Logmanagement-Admins haben nicht die Kapazität, Logstash-Regeln für alle verwalteten Applikationen zu schreiben und vor allem nicht ständig anzupassen
  • Das zentrale Logmanagement ist sehr wichtig und soll durch unerfahrene Anwender nicht gefährdet werden

Lösungsansatz

Im Gespräch hat sich dann ein Setup ergeben, das ungefähr so aussehen soll:

  • Es wird eine Entwicklungsmaschine geschaffen, auf der die einzelnen Teammitglieder ssh-Logins bekommen. Auf dieser Maschine ist Logstash installiert, wird aber nicht ausgeführt
  • Jedes Team bekommt ein Repository in der zentralen Versionsverwaltung (z.B. GitLab ) und kann das auf der Entwicklungsmaschine auschecken. In diesem Repository befindet sich die gesamte Logstash-Konfiguration einer Pipeline und ggf. Testdaten (siehe weiter unten)
  • Die Mitglieder des Teams entwickeln ihre Pipeline und nutzen den lokalen Logstash zum Testen auf syntaktische Richtigkeit.
  • Optional können zusätzliche Pipelines zur Verfügung gestellt werden, die Beispieldaten einlesen und wieder in Dateien rausschreiben. Diese beiden Dateien können mit eingecheckt werden, man muss nur darauf achten, sie nicht so zu benennen, dass Logstash sie als Konfiguration ansieht. Es empfiehlt sich, die Beispieldaten aus allen möglichen Logzeilen der Applikation zusammenzusetzen. Bei Änderungen sollten auch alte Zeilen belassen werden, um Logs aller möglichen Versionen einlesen zu können. Sollen die Daten mit Elasticsearch und Kibana veranschaulicht werden, kann in den Beispieldaten ein Platzhalter für den Zeitstempel verwendet werden, der vor dem Einlesen durch die aktuelle Zeit ersetzt wird. Die Pipelines werden einmal eingerichtet und bleiben dann statisch, sie werden nicht von den Teams bearbeitet und befinden sich nicht in zugänglichen Repositories
  • Beim Commit der Konfiguration in Git wird ein pre-commit-hook ausgeführt, der nochmal mit Logstash testet, ob die Konfiguration fehlerfrei ist. (Vertrauen ist gut, etc.)
  • Ist der Commit gut verlaufen, wird nach dem Push ein CI Tool wie Jenkins benutzt, um die aktuellen Stände sämtlicher Pipelines auf einer Testmaschine auszurollen. Dort wird Logstash dann kurz gestartet, mit fest konfigurierten Pipelines, die Daten einlesen und ausgeben. Statt wie auf einem Produktionssystem Ports zu öffnen und an Elasticsearch oder Icinga zu schreiben, werden die Logdaten aus den eingecheckten Dateien mit Beispieldaten gelesen und in Dateien geschrieben. Dabei ist es wichtig, dass alle Daten von allen Teams in die selbe Instanz gelesen werden. Nur so kann verhindert werden, dass sich die Teams  gegenseitig “dreinpfuschen”
  • Die ausgegebene Dateien werden auf ihre Richtigkeit geprüft. Das klingt aufwändiger als es ist. Es reicht eigentlich, eine funktionierende Konfiguration mit den oben genannten in- und outputs laufen zu lassen und das Ergebnis als Vorlage zu verwenden. Diese Vorlage wird dann mit dem tatsächlichen Ergebnis verglichen. Arbeiten die Teams schon im ersten Schritt mit den optionalen In- und Outputdateien, kann dieser Punkt stark vereinfacht werden, da man davon ausgehen kann, dass jeder seine eigenen Outputdateien schon berichtigt hat und sie nur mehr geprüft werden müssen
  • Abweichungen von der Vorlage werden überprüft und danach entweder die Logstash-Konfiguration erneut angepasst oder die Vorlage angepasst, sodass weitere Tests erfolgreich sind
  • War der Test erfolgreich, kann die Konfiguration getagged und auf den Produktionsservern ausgerollt werden

Weitere wichtige Punkte

Hier noch ein paar Punkte, die beim Umsetzen helfen sollen.

  • Logstash kann mit der Option -t auf der Shell gestartet werden. Dann prüft er nur die Konfiguration und beendet sich gleich wieder. Das kann auch in der logstash.yml hinterlegt werden
  • Um letztendlich zu verhindern, dass zwei Teams das selbe Feld mit unterschiedlichem Typ schreiben muss extra Aufwand betrieben werden. Entweder muss sich jeder an eine bestimmte Nomenklatur halten, oder jedes Team bekommt ein eigenes Feld und darf nur Unterfelder davon anlegen oder jedes Team schreibt in einen eigenen Index
  • Wenn jedes Team eine eigene Pipeline mit eigenem Input und Output bekommt, kann die Gefahr einer gegenseitigen Beeinflussung minimiert werden. Das ist aber nicht immer möglich oder sinnvoll. Oft schreibt ein Beat Logs von verschiedenen Applikationen oder es gibt nur einen gemeinsamen UDP/TCP input für syslog.
  • Jedes Team kann sich nach wie vor die eigene Konfig zerstören, aber mit dem oben geschilderten Setup kann man ziemlich umfassend sicherstellen, dass auch wirklich jeder nur seine eigene Konfig zerlegt und nicht alle anderen in den Tod reisst.
  • Die von den Teams verwalteten Pipelines haben jeweils nur einen Input aus einem Messagebroker (z.B. Redis) und einen Output ebenfalls in einen Messagebroker. Das kann der selbe sein, wobei dann darauf zu achten ist, dass der verwendete key ein anderer ist. So kann auf den verschiedenen Maschinen jeweils eine völlig andere Pipeline in den Broker schreiben oder raus lesen. Auf den Entwicklungsmaschinen wären es Pipelines, die mit Dateien interagieren, auf der Produktion dagegen z.B. ein beats input und ein elasticsearch output. Diese ändern sich ja nicht und können weitgehend statisch bleiben.
  • Das ganze ist momentan ein Konzept. Es kann natürlich sein, dass in der Umsetzung noch das eine oder andere Problem auftaucht, das bisher nicht bedacht wurde. Über Feedback würde ich mich auf jeden Fall freuen.
Thomas Widhalm
Thomas Widhalm
Manager Operations

Pronomina: er/ihm. Anrede: "Hey, Du" oder wenn's ganz förmlich sein muss "Herr". Thomas war Systemadministrator an einer österreichischen Universität und da besonders für Linux und Unix zuständig. Seit 2013 ist er bei der NETWAYS. Zuerst als Consultant, jetzt als Leiter vom Operations Team der NETWAYS Professional Services, das unter anderem zuständig ist für Support und Betriebsunterstützung. Nebenbei hat er sich noch auf alles mögliche rund um den Elastic Stack spezialisiert, schreibt und hält Schulungen und macht auch noch das eine oder andere Consulting zum Thema. Privat begeistert er sich für Outdoorausrüstung und Tarnmuster, was ihm schon mal schiefe Blicke einbringt...

JSON in bequem

Wenn man mit diversen Tools arbeitet, die wir in diesem Blog immer wieder bearbeitet, stößt man unweigerlich irgendwann auf JSON formatierte Texte. Während es sicher für den einen oder anderen IT-God kein Problem ist, das JSON im Hirn zu parsen und JSON-formatierter Text höchstens eine angenehme Erleichterung zu Bytecode ist, möchte ich hier kurz zweieinhalb Wege für Sterbliche vorstellen, um im
Wirrwarr von Klammern nicht die Übersicht zu verlieren.
Dabei bringen manche Dienste bereits eine Möglichkeit mit, den Output einfacher lesbar zu machen, andere verlassen sich dabei ganz auf externe Tools. Warum er nicht immer “einfach” gehalten wird, lässt sich ganz einfach damit erklären, das JSON eingentlich eh nur entworfen wurde, um von Maschinen verarbeitet zu werden und Menschen in den Wahnsinn zu treiben. Da sorgen Zeilenumbrüche und Einrückungen nur dafür, dass unnötig viele Daten übertragen werden.
Die REST API von Elasticsearch bietet je nach Endpunkt mal schön formatiertes, mal unformatierters JSON an.

# curl localhost:9200
{
  "name" : "y2G7v2X",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "1KYRb5mUQ8CaTSJDM-6djQ",
  "version" : {
    "number" : "6.3.2",
    "build_flavor" : "default",
    "build_type" : "rpm",
    "build_hash" : "053779d",
    "build_date" : "2018-07-20T05:20:23.451332Z",
    "build_snapshot" : false,
    "lucene_version" : "7.3.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

Dagegen sieht jeder Unterpunkt entsprechend unübersichtlich aus.

# curl localhost:9200/_cluster/health
{"cluster_name":"elasticsearch","status":"green","timed_out":false,"number_of_nodes":2,"number_of_data_nodes":2,"active_primary_shards":341,"active_shards":682,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}

Für Elasticsearch (und mittlerweile auch für Icinga 2!) gibt’s hier aber Abhilfe durch den Schalter ?pretty.

# curl localhost:9200/_cluster/health?pretty
{
  "cluster_name" : "elasticsearch",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 2,
  "number_of_data_nodes" : 2,
  "active_primary_shards" : 341,
  "active_shards" : 682,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

Da man diverse JSON Monster aber auch mal als Datei vorliegen hat oder nicht alle Dienste solche Komfortfunktionen bieten, braucht man immer wieder externe Hilfe. Dafür gibt’s die Allroundlösung, die überall funktioniert, wo Python installiert ist, also quasi bei jedem ernst zu nehmenden Betriebssystem.

# curl -s localhost:9200/_cluster/health | python -m json.tool
{
    "active_primary_shards": 341,
    "active_shards": 682,
    "active_shards_percent_as_number": 100.0,
    "cluster_name": "elasticsearch",
    "delayed_unassigned_shards": 0,
    "initializing_shards": 0,
    "number_of_data_nodes": 2,
    "number_of_in_flight_fetch": 0,
    "number_of_nodes": 2,
    "number_of_pending_tasks": 0,
    "relocating_shards": 0,
    "status": "green",
    "task_max_waiting_in_queue_millis": 0,
    "timed_out": false,
    "unassigned_shards": 0
}

Das war’s dann aber auch schon mit dem, was man einfach auf diese Weise machen kann. Oft reicht das ja auch aus. Wer aber gern etwas mehr an Möglichkeiten haben will, sollte sich unbedingt mal jq installieren.

# curl -s localhost:9200/_cluster/health | jq
{
  "cluster_name": "elasticsearch",
  "status": "green",
  "timed_out": false,
  "number_of_nodes": 2,
  "number_of_data_nodes": 2,
  "active_primary_shards": 341,
  "active_shards": 682,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 0,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 100
}

Soweit so fad. Ausser, dass ich auf meiner Shell noch schöne farbliche Hervorhebungen sehe und Ihr im Blog hier nicht. 😛
Interessant wird’s dann aber, wenn man mit jq anfängt, den Output auch gleich zu filtern. Dazu ein Auszug eines API Outputs von Icinga 2.

{
  "results": [
    {
      "attrs": {
        "__name": "canis",
        "acknowledgement": 0.0,
        "acknowledgement_expiry": 0.0,
[...]
    },
    "joins": {},
    "meta": {},
    "name": "canis",
    "type": "Host"
  },
[...]

Wenn man den Output dann durch einen jq Aufruf inkl. Filtern schickt, kann man auf die eigentlich interessanten Daten filtern. Ich hab’ mir freundlicherweise genehmigt, die folgenden Beispiele aus dem Icinga 2 Buch zu entnehmen.

$ curl ... |jq '{name: .results[].name}'
{
  "name": "canis"
}
{
  "name": "fornax"
}
{
  "name": "virgo"
}
[...]

Diese Filter kann man dann beliebig erweitern.

$ curl ... |jq '{name: .results[].name, address: .results[].attrs.address}'
{
  "name": "sculptor"
  "address": "172.16.2.11"
}
{
  "name": "fornax"
  "address": "172.16.1.11"
}
...

Eine umfassende Anleitung für jq gibt’s auch. Je nach Bedarf kann es sich aber auszahlen, sich erstmal diverse Tutorials anzusehen, da hier oft der Zugang etwas leichter gemacht wird.

Thomas Widhalm
Thomas Widhalm
Manager Operations

Pronomina: er/ihm. Anrede: "Hey, Du" oder wenn's ganz förmlich sein muss "Herr". Thomas war Systemadministrator an einer österreichischen Universität und da besonders für Linux und Unix zuständig. Seit 2013 ist er bei der NETWAYS. Zuerst als Consultant, jetzt als Leiter vom Operations Team der NETWAYS Professional Services, das unter anderem zuständig ist für Support und Betriebsunterstützung. Nebenbei hat er sich noch auf alles mögliche rund um den Elastic Stack spezialisiert, schreibt und hält Schulungen und macht auch noch das eine oder andere Consulting zum Thema. Privat begeistert er sich für Outdoorausrüstung und Tarnmuster, was ihm schon mal schiefe Blicke einbringt...

Rundum updaten mit Ansible

Nachdem wir ja nun auch alle möglichen Dienstleistungen rund um Ansible anbieten, dachte ich mir, es kann nicht schaden, es mir auch mal zu Gemüte zu führen. Kurzum: Ich bin bisher begeistert davon, wie einfach man damit auch komplexe Aufgaben lösen kann und werde sicherlich mein Wissen in dieser Richtung noch vertiefen.

Meine ersten Gehversuche haben mir dann auch gleich geholfen, ein Problem zu lösen, das mich schon länger geplagt hat. IT’ler neigen ja dazu, auch selber eine umfangereiche Infrastruktur zu betreiben und dabei wird der Aufwand üblicherweise mit der Zeit nicht weniger. Ich habe durchaus einige ähnliche Systeme im Einsatz und kann getrost sagen, wenn ich bei ein paar repräsentativen Systemen geprüft habe, ob ich anstehende Updates sofort einspielen oder lieber warten sollte, kann ich sie für die restlichen Rechner auch gleich machen. Aber wie, ohne ständig von System zu System hüpfen zu müssen. Es gibt dafür natürlich Lösungen, aber die meisten, die ich gefunden habe, brauchen einfach deutlich zu viele Ressourcen und rentieren sich auch für meine mittlere zweistellige Anzahl an Rechnern nicht.
Also habe ich versucht, das Problem mit Ansible zu erschlagen und war auch in erstaunlich kurzer Zeit fertig. Inzwischen weiss ich, dass meine Lösung nicht sonderlich elegant ist und man noch einiges daran verbessern kann, aber ich denke, sie ist eingängig und deshalb möchte ich sie hier vorstellen.
Zuerst lege ich mir die Datei ~/ansible/hosts an, in der sämtliche Hosts namentlich aufgelistet werden. Ich habe sie anonymisiert und stark gekürzt, um den Blogartikel nicht unnötig in die Länge zu ziehen. z.B. ist hier nur ein NAT mit Hosts dargestellt, in meiner “echten” Liste sind es mehr, was “händische” Updates nicht einfacher macht.

[centos:children]
centos6
centos7
[debian:children]
debian-jessie
raspbian-stretch
debian-stretch
[centos6]
odin.example.com
kvasir.example.com
[centos7]
balder.example.com
heimdall.example.com
tyr.example.com
[raspbian-stretch]
rerun.asgard.example.com
[debian-jessie]
gullveig.example.com
[debian-stretch]
freyr.example.com
[solaris11]
surtur.asgard.example.com
[special-centos]
loki.asgard.example.com
[asgard]
loki.asgard.example.com
rerun.asgard.example.com
surtur.asgard.example.com

Zuerst werden hier Gruppen definiert, die andere Gruppen zusammenfassen. So sind die Gruppen centos6 und centos7 Teil von centos und alle Hosts in den Untergruppen auch gleichzeitig Mitglied in den Übergruppen. Bei genauem Hinsehen erkennt man auch, dass loki.asgard.example.com Mitglied in zwei Gruppen ist, was durchaus so beabsichtigt ist.
Das folgende Playbook ( ~/ansible/playbooks/update-all.yml ) kann nun (fast) alle Systeme auf einen Schlag aktualisieren. Wenn mir also mein Icinga 2 meldet, dass Updates anstehen, suche ich mir einen repräsentativen Host jeder Gruppe aus und prüfe, welche Updates anstehen. Denke ich, dass ich sie gefahrlos einspielen kann, starte ich das folgende Playbook.

---
- hosts: centos*:!special-centos
  remote_user: alice
  become: yes
  tasks:
    - name: update everything
      yum:
        name: "*"
        state: latest
        exclude: "owncloud*"
- hosts: special-centos
  remote_user: alice
  become: yes
  tasks:
    - name: update everything
      yum:
        name: "*"
        state: latest
        skip_broken: yes
- hosts: debian*:raspbian*
  remote_user : alice
  become: yes
  tasks:
    - name: install dmidecode
      apt:
        name: dmidecode
        update_cache: yes
    - name: update package cache
      apt:
        update_cache: yes
    - name: upgrade packages
      apt:
        upgrade: dist
        force: yes
    - name: autoremove dependencies
      apt:
        autoremove: yes

Das erste Play verbindet sich zu allen centos Hosts, die nicht Teil der Gruppe centos-special sind und führt das Modul yum aus, das mit der Option latest alle Pakete aktualisiert. Explizit ausgeklammert ist dabei owncloud, da hier immer Nacharbeiten nötig sind. Dabei nutzt Ansible den User alice, für den ein SSH Key hinterlegt ist und wird per sudo zu root. Das muss natürlich vorher erlaubt werden, wobei Ansible auch ermöglicht, für sudo Passwörter mitzugeben. Ist dieses Play durchgelaufen, sind alle CentOS Maschinen, ausser loki aktualisiert. Dabei handelt es sich um eine “gewachsene Speziallösung” auf der diverse Pakete aus Drittrepositories installiert sind, die inzwischen in defekten Abhängigkeiten festhängen. Hier wird das yum Modul also mit der skip_broken Option aufgerufen, das diese verbogenen Pakete ausklammert.
Das Paket deltarpm um Gebrauch von deltarpms zu machen, habe ich über ein anderes Playbook installiert, könnte man aber prinzipiell auch noch hier vor den ersten Plays aufnehmen.
Nach den CentOS Hosts kommen die Debian Maschinen an die Reihe. Hier wird erst dmidecode installiert, das für den reibungslosen Ablauf nötig ist und z.B. bei raspbian nicht gleich installiert war. Ist das Paket bereits installiert, überspringt Ansible diesen Schritt nach einer Prüfung. Die Tasks für Debian haben alle das Update des package cache deaktiviert, was den Ablauf sehr beschleunigt. Der erste Task führt dieses Update durch und das reicht für alle weiteren. Zum Schluss werden noch nicht mehr benötigte Dependencies entfernt.
Damit Ansible sich auch in das NAT “asgard” verbinden kann, habe ich entsprechende Optionen in ~/ansible/hosts/group_vars/asgard hinterlegt.

ansible_ssh_common_args: '-o ProxyCommand="ssh -W %h:%p -q asgard.example.com" -o ConnectTimeout=800s'
ntp_server: 192.168.74.19

Damit wird festgelegt, dass ansible sich zu allen diesen Hosts über einen Jumphost verbinden muss. Dabei gibt der Name der Datei an, für welche Gruppe von Hosts die darin enthaltenen Optionen gelten. Die Adresse des NTP Servers hat keinen Einfluss auf dieses Playbook, soll aber zeigen, dass man hier auch einfach andere Variablen setzen kann.
Der Solaris Host bleibt hier aussen vor, da es dafür noch kein fertiges Modul für Updates gibt. Hier greife ich ggf. noch händisch ein.
Inzwischen weiss ich, dass man die verwendeten Module auch einfach abhängig vom Fact des Betriebssystems machen kann und noch den einen oder anderen Kniff. Aber in der aktuellen Version ist das Playbook wohl besser verständlich.
Wer mehr über Ansible wissen will, sollte unbedingt mal in einer unserer Schulungen vorbeischauen.

Thomas Widhalm
Thomas Widhalm
Manager Operations

Pronomina: er/ihm. Anrede: "Hey, Du" oder wenn's ganz förmlich sein muss "Herr". Thomas war Systemadministrator an einer österreichischen Universität und da besonders für Linux und Unix zuständig. Seit 2013 ist er bei der NETWAYS. Zuerst als Consultant, jetzt als Leiter vom Operations Team der NETWAYS Professional Services, das unter anderem zuständig ist für Support und Betriebsunterstützung. Nebenbei hat er sich noch auf alles mögliche rund um den Elastic Stack spezialisiert, schreibt und hält Schulungen und macht auch noch das eine oder andere Consulting zum Thema. Privat begeistert er sich für Outdoorausrüstung und Tarnmuster, was ihm schon mal schiefe Blicke einbringt...

Generationswechsel bei GnuPG/PGP Schlüsseln

Die für digitale Signaturen und zum Verschlüsseln von Daten verwendeten GnuPG bzw. PGP Schlüssel sind keine reinen Schlüssel im eigentlichen Sinne. Stattdessen enthalten sie den eigentlichen Schlüssel und diverse zusätzliche Informationen wie, für welche “IDs” (also Kombinationen aus Name und E-Mailadresse) sie gültig sind, wie lange sie gültig sind und einige Informationen über Hash- und Verschlüsselungsalgorithmen, die der Besitzer des Schlüssels vorzieht oder überhaupt zulässt.
Da sich das Wissen über die verwendeten Algorithmen weiterentwickelt und immer mehr Rechenleistung immer günstiger zu haben ist kann man leider nicht davon ausgehen, dass man einen Schlüssel für immer verwenden kann. Irgendwann ist er schlichtweg überholt und kann nicht mehr als sicher gelten. Dafür sind vor allem drei Komponenten ausschlaggebend: Die Länge des eigentlichen Schlüssels, der bevorzugte Verschlüsselungsalgorithmus und der für Signaturen verwendete Hash-Algorithmus.
Bei der Länge wird man beim Erstellen des Schlüssels mit einer Auswahl konfrontiert, die eine minimale und eine maximale Länge angibt. Manchmal kann man diese Auswahl auch übersteuern, läuft dann aber Gefahr, einen Schlüssel zu haben, mit dem viele Kommunikationspartner gar nichts anfangen können oder der so lange für jede Aktion braucht, dass er alleine schon deshalb unbrauchbar ist. Unter einigen Umständen bringt ein extralanger Schlüssel überhaupt nichts, da z.B. damit nur eine weitere, einmalige, Passphrase gesichert wird, die eine fixe Länge hat. (Asymmetrische Verfahren für lange Texte zu verwenden ist auch heute noch zu rechenintensiv, weshalb meist ein symmetrisches Verfahren mit einer einmaligen Passphrase eingesetzt wird und nur die wird asymmetrisch verschlüsselt). Etwas mehr dazu findet man in einem älteren Post zu dem Thema.
Die für die Kommunikation bevorzugten Algorithmen kann man in den “Preferences” des Schlüssels setzen. Mehr dazu im zuvor genannten Blogpost.
Signaturen jedoch werden einmalig erstellt und sollen dann gelten. Wer schon länger mit GnuPG arbeitet wird also noch einige Signaturen mit SHA1, so auch die Selbstsignatur, mit der der Schlüssel ursprünglich ausgestattet wurde. SHA1 gilt aber schon lange nicht mehr als sicher, weshalb man ihn auch nicht mehr benutzen sollte. (Ebenso MD5, mit dem auch früher Signaturen erstellt wurden). Wer einen DSA Schlüssel mit 1024bit Länge hat, sollte sich jetzt angesprochen fühlen.
Es gibt also mittlerweile mehrere Gründe, manche alte Schlüssel zu überdenken. Die Liste der bevorzugten Algorithmen lässt sich auch bei einem bestehenden Schlüssel einfach austauschen. Die Schlüssellänge liesse sich durch einen neuen Subkey realisieren, der aber auch einige extra Schritte beim Handling braucht. Signaturen auszutauschen wird aber ein aufwändiger Prozess, weil man mit allen, die den alten Schlüssel signiert haben, nochmal in Kontakt treten und sie um neue Signaturen auf dem neuen Schlüssel bitten muss. Und da wir ja hoffentlich alle sehr viele Signaturen auf unseren Schlüsseln haben, kann das schon recht aufwändig werden.
Um also möglichst alle Probleme, die ein alter Schlüssel mit sich bringt, auf einmal zu erschlagen, gehen viele User den Weg, einfach einen neuen Schlüssel anhand möglichst aktueller Best Practices (als Ausgangspunkt kann wieder der vorherige Blogpost dienen) zu erstellen und auf den umzusteigen.
Um einen neuen Schlüssel zu etablieren und einen alten dadurch zu ersetzen, kann man folgendermassen vorgehen:

  • Einen neuen Schlüssel anhand aktueller Best Practices erstellen
  • Den neuen Schlüssel mit dem alten Schlüssel signieren
  • Ob man auch den alten Schlüssel mit dem neuen signieren soll, da gehen die Meinungen auseinander. (Ich hab’s jedenfalls immer gemacht)
  • Den neuen Schlüssel (und ggf. den alten, aktualisierten) auf Schlüsselsever hochladen
  • Den neuen Schlüssel bekanntmachen. Am besten über möglichst viele Kanäle, die man sonst auch nutzt, um mit der Welt zu kommunizieren. E-Mail, Soziale Medien, Blog, Website,…
  • “Dinge”, die bestehen bleiben sollen, also Dokumente, Pakete, etc. die mit dem alten Schlüssel signiert wurden mit dem neuen Schlüssel signieren. Wenn mehrere Signaturen möglich sind, sollte die neue hinzukommen, sonst muss sie ersetzt werden. Vor diesem Schritt sollte man etwas warten, damit die Empfänger eine Chance haben, den neuen Schlüssel zu holen bzw. zu bemerken.
  • Die Leute, die den alten Schlüssel signiert haben, sollten informiert und gebeten werden, den neuen Schlüssel ebenfalls zu signieren.

Für den letzten Punkt gibt es einige Vorlagen, die man verwenden kann. Es bietet sich an, ein Klartextdokument aufzusetzen, in dem über den Tausch informiert wird und gleich die nötigen Befehle mitzuliefern, mit der der neue Schlüssel geholt und signiert werden kann. Ein entsprechendes Beispiel habe ich für meine persönlichen Schlüssel verfasst. Dieses Dokument wurde auch gleich sowohl mit dem alten als auch dem neuen Schlüssel unterschrieben, um den Empfängern die Echtheit zu bestätigen.

gpg --digest-algo SHA512 --clearsign -u 6265BAE6 -u A84CB603 key-transition-2018-01-05.txt

Dieses Dokument sollte man möglichst öffentlich machen und an die Leute schicken, die den alten Schlüssel signiert haben.
Verwendet man den Schlüssel auch zum Signieren von Paketen, ist der Umstieg etwas schwieriger. Man kann leider nicht erwarten, dass jeder User, der die Software einsetzt, auch regelmässig ein Blog oder andere Neuigkeiten liest, weshalb ein einfaches Ersetzen des Schlüssels dazu führen würde, dass die User neue Pakete nicht mehr installieren können. Um den Umstieg einfacher zu machen sollte bereits im Vorfeld ein “release”-Paket bereitgestellt werden, also eines das das eigene Repository in den Packagemanager der User konfiguriert und den entsprechenden Schlüssel ablegt. In diesem Paket kann man in einem neuen Release dann auch den neuen Schlüssel hinterlegen, sodass beide, der neue und der alte in den Packagemanager kommen. Stellt man später um, mit welchem Schlüssel man Pakete signiert, werden sie automatisch als valide signiert erkannt. (Da sich verschiedene Packagemanager unterschiedlich verhalten, sollte man das unbedingt vorher testen!)
Nicht abgedeckt sind jene User, die das Repository “von Hand” angelegt und den Schlüssel manuell importiert haben. Klar könnte man in eine Version der Software selbst den neuen Schlüssel-Import ins Paket verbauen, aber das würden einem die User (zu recht) um die Ohren hauen, da das Software Paket nur die Software installieren soll und nichts am Paketmanager verändern. Beim Release-Paket ist das was anderes, da das ja genau für solche Aufgaben gebaut wurde. Hier heisst es, im Vorfeld umso mehr über diverse Kanäle zu kommunizieren und es auch möglichst in alle fertigen Module, Cookbooks, Playbooks, etc. zu integrieren, dass für kurze Zeit zwei Schlüssel vorhanden sind und wann umgestiegen wird. Besonders geeignet ist natürlich der Release einer grösseren neuen Version, wo vielleicht sowieso ein neues Repository gebaut wird.
Einige Zeit, nachdem der Umstieg vollzogen wurde, sollte man den alten Schlüssel “revoken”, damit er nicht mehr benutzt wird. Auch könnte man das Release-Paket dazu bringen, den alten Schlüssel aus den Packagemanagern zu entfernen. Sollte tatsächlich jemand den alten Schlüssel knacken, ist man so auf der sicheren Seite und genau dafür hat man den Umstieg ja gemacht. Alte Informationen, die mit dem alten Schlüssel verschlüsselt wurden, kann man übrigens auch mit einem revoked key noch immer entschlüsseln. Jedenfalls kenne ich keine Software, die das nicht mehr erlauben würde. Nur verschlüsseln geht dann eben nicht nicht mehr.
Etwas mehr zum Nachlesen zum Thema gibt’s unter anderem auf den Debian Seiten.

Thomas Widhalm
Thomas Widhalm
Manager Operations

Pronomina: er/ihm. Anrede: "Hey, Du" oder wenn's ganz förmlich sein muss "Herr". Thomas war Systemadministrator an einer österreichischen Universität und da besonders für Linux und Unix zuständig. Seit 2013 ist er bei der NETWAYS. Zuerst als Consultant, jetzt als Leiter vom Operations Team der NETWAYS Professional Services, das unter anderem zuständig ist für Support und Betriebsunterstützung. Nebenbei hat er sich noch auf alles mögliche rund um den Elastic Stack spezialisiert, schreibt und hält Schulungen und macht auch noch das eine oder andere Consulting zum Thema. Privat begeistert er sich für Outdoorausrüstung und Tarnmuster, was ihm schon mal schiefe Blicke einbringt...

Liebes Icinga 2, sag "Aaaaaah"

Die meisten, die schon mal ein Support Ticket aufgemacht haben, kennen das:
“Hallo, ich hab’ ein Problem, weil blah blubb.”
“Hi, das ist schade. Ich hab’ hier ein paar Seiten Doku über Euer Setup. Ist das noch aktuell?”
Und erst dann geht’s los mit der eigentlichen Fehlersuche. Das hat vor allem den Hintergrund, dass viele unserer Kunden sehr unterschiedliche Ansätze haben, wie sie ihr Setup betreuen und mit uns zusammenarbeiten. Manche kontaktieren uns nur im Notfall, andere nehmen nur grössere Umbauten mit uns gemeinsam vor und pflegen alles andere selbst und wieder andere lassen alles von uns machen. Deshalb kommt auch immer erst die Frage, ob sich nicht vielleicht was geändert hat, was natürlich völlig ok ist, nur wissen muss man es für den Support.
Oder man stellt eine Frage im Monitoringportal. Dann kommt da auch gern mal: “Sag erstmal, was für ein Setup Du hast.”
Und genau für diese Fälle gibt’s jetzt (oder eher demnächst) das “Icinga 2 Diagnostics Script”. Das Script soll zwei Anwendungsfälle haben:

  • Einen ersten Überblick über die Eckdaten einer Installation
  • Eine umfassende Datensammlung um alle Feinheiten abzuklopfen

Dabei liegt der Fokus klar auf der ersten Anwendung. Icinga und die damit verbundenen Tools haben eine unglaubliche Flexibilität und können auf so viele unterschiedliche Arten verwendet und konfiguriert werden, dass es oft ziemlich aufwändig sein kann, einen ersten Überblick zu erhalten. Deshalb soll das Script einen nicht gleich mit Unmengen an Information überwältigen, sondern sie sinnvoll und übersichtlich aufbereiten. Dazu probiert es meist verschiedene Befehle durch, die einem Betriebssystem Informationen entlocken können und listet die Ausgabe dann entsprechend aufbereitet auf.
Der andere Modus sammelt so viele Informationen wie er nur kann, inklusive Logfiles, Konfiguration, Datenbankdumps, etc. Weil diese Daten manchmal Informationen enthalten, die man nicht rausgeben darf oder will, ist das nicht der Default Modus.
Egal, welchen Modus man verwendet, das Script sammelt die Daten und legt sie auf dem Host ab, der sie eingesammelt hat. Es gibt also keinen versteckten “Phone-Home” Mechanismus oder eine “praktische” Verschlüsselung, die nur verschlüsselte Daten zurücklässt, sodass der User nicht sieht, was er da verschickt. Auch ein Ändern der Daten ist vor dem Versand natürlich möglich, um z.B. Passwörter zu entfernen.
Ein Wunschtraum für die Zukunft wäre, dass das Script im Überblicksmodus auch auf ungewöhnliche Dinge prüft und nur meldet, wenn es etwas Erwähnenswertes gefunden hat. Also z.B. wenn eine Datenbank nicht dem Schema entspricht, das mitgeliefert wird – entspricht es, gibt’s aber keinen “Ok” Eintrag, um die Übersicht nicht zu beeinträchtigen.
Das Script ist aktuell noch auf dem Stand eines schnellen Hacks, der nach einem Dokumentationstermin bei einem Kunden entstanden ist. Ich habe damals versucht, die relevantesten Eckdaten des Setups zu dokumentieren und mir notiert, welche Befehle ich benutzt habe. Das wurde noch etwas erweitert (Danke Dirk, für die vielen guten Ideen) und einige davon in ein Script gegossen. Wie wahrscheinlich die meisten, die selber Projekte laufen haben, bin ich noch höchst unzufrieden mit dem Funktionsumfang, dem Errorhandling, dem Scripting-Stil, usw. Da ich aber nicht wieder ewig warten will, bis ich doch mal Zeit finde, daran weiter zu schrauben, folge ich der Empfehlung meiner Kollegen und mache ein offizielles Icinga-Projekt daraus. Das motiviert mich erstens sicherlich dazu, eher weiter zu arbeiten und andererseits, wird so wohl eher auch mal jemand den ein oder anderen Pull-Request schicken. Bitte habt aber Verständnis dafür, dass der Fokus darauf bleibt, den Output übersichtlich zu halten.
Inzwischen befindet sich das Script schon in einem GitHub Repo im Icinga Bereich. Wer es testen möchte, Ideen dafür hat oder auch mitarbeiten möchte, findet dort alles Nötige.

Thomas Widhalm
Thomas Widhalm
Manager Operations

Pronomina: er/ihm. Anrede: "Hey, Du" oder wenn's ganz förmlich sein muss "Herr". Thomas war Systemadministrator an einer österreichischen Universität und da besonders für Linux und Unix zuständig. Seit 2013 ist er bei der NETWAYS. Zuerst als Consultant, jetzt als Leiter vom Operations Team der NETWAYS Professional Services, das unter anderem zuständig ist für Support und Betriebsunterstützung. Nebenbei hat er sich noch auf alles mögliche rund um den Elastic Stack spezialisiert, schreibt und hält Schulungen und macht auch noch das eine oder andere Consulting zum Thema. Privat begeistert er sich für Outdoorausrüstung und Tarnmuster, was ihm schon mal schiefe Blicke einbringt...