News

Das Neuste aus der Welt von Adfinis SyGroup

MariaDB Galera Cluster auf Red Hat OpenShift/Kubernetes

23/11/2017

In diesem Blogpost stellen wir eine Lösung vor mit der ein MariaDB Galera Cluster auf Kubernetes oder kompatiblen Lösungen (z.B. Red Hat OpenShift Container Plattform, CoreOS Tectonic und Canonical’s Kubernetes) betrieben werden kann. Es ermöglicht damit cloud-native Applikationen, die in Kubernetes betrieben werden, und die Datenquellen, die benötigt werden, in der gleichen Infrastruktur zu betreiben und mit den gleichen Werkzeugen zu verwalten.

Die Funktionalität baut auf dem Feature PetSets von Kubernetes auf, eine API die für Stateful Applikationen gedacht ist.

Was sind PetSets

Kubernetes hat sich im Lauf der letzten Jahre von einem neuen Tool für Container Management, zu dem Projekt entwickelt, welches in diesem Bereich den Ton und Takt vorgibt. Aufgrund der Architektur von Kubernetes war es bisher nur schwer möglich Applikationen und Services, die nicht stateless sind, darauf zu betreiben. Erste Ideen für so genannte “Nominal Services” wurden früh vorgeschlagen, aber aufgrund anderer Prioritäten nicht umgesetzt.

Mit dem Release von Kubernetes v1.3 gibt es nun eine erste Vorschau auf eine Lösung. Mit PetSets wurde in Kubernetes eine API im Alpha Status geschaffen, die spezifisch auf die Anforderungen von stateful Applikationen und Services zugeschnitten ist. Pods in einem PetSet erhalten einen eindeutigen Namen und numerischen Index (z.B. app-0, app-1, …), der über die Lebensdauer des Pods gleich bleibt. Zusätzlich bleiben auch Persistent Volumes dem Pod zugeordnet, auch wenn dieser auf einen anderen Host migriert wird. Da jedem PetSet ein Service zugeordnet wird, ist es möglich über Abfragen an den Service Informationen über die Pods des PetSets zu verarbeiten. Dies ermöglicht es z.B. das Bootstrapping eines Clusters zu automatisieren, oder die Konfiguration zur Laufzeit entsprechend anzupassen, sollte sich der Zustand des Clusters ändern (Scale Up, Scale Down, Wartung oder Ausfall eines Hosts, …).

Wie kann MariaDB Galera Cluster auf Kubernetes betrieben werden

Um ein MariaDB Galera Cluster auf Kubernetes zu betreiben ist es vor allem notwendig den Bootstrap des Clusters und die Anpassung der Konfiguration auf Basis der aktuellen Pods im PetSet zu implementieren. In unserem Fall werden diese beiden Aufgaben von zwei Init Containern übernommen. Im Docker Image galera-init sind die entsprechenden Tools verpackt um den Bootstrap durchzuführen, der zweite Container startet das Binary peer-finder welches den SRV Record des Kubernetes Services abfragt und auf Basis der Mitglieder des PetSets die Konfiguration für das MariaDB Galera Cluster generiert.

Beim Starten des ersten Pets wird wsrep_cluster_address=gcomm:// in der Konfiguration gesetzt und der Pod führt automatisch einen Bootstrap des Cluster durch. Alle weiteren Pets die gestartet werden, befüllen wsrep_cluster_address mit den Hostnamen aus dem SRV Record des Services, und treten automatisch dem bestehenden Cluster bei. Hier noch ein Beispiel der Konfiguration nach dem Starten des zweiten Pets:

wsrep_cluster_address=gcomm://mariadb-0.galera.lf2.svc.cluster.local,mariadb-1.galera.lf2.svc.cluster.local
wsrep_cluster_name=mariadb
wsrep_node_address=mariadb-1.galera.lf2.svc.cluster.local

Die entsprechenden Quellen um MariaDB Galera Cluster auf der eigenen Kubernetes oder Red Hat OSCP Infrastruktur zu testen sind auf Github veröffentlicht: https://github.com/adfinis-sygroup/openshift-mariadb-galera

Beschränkungen von PetSets im Alpha Status

Die Beschränkungen von PetSets im Alpha Status sind in der Kubernetes Dokumentation erläutert und betreffen diverse Bereiche, vor allem Tasks die manuell bearbeitet werden müssen. So ist es zum Beispiel nur möglich die Anzahl der Replikas über den Parameter replicas zu erhöhen, aber nicht möglich sie zu senken. Auch ein Update der PetSet Definition ist nicht automatisch möglich, daher muss ein Update auf eine neue Image Version manuell über ein neues PetSet gemacht werden.

Wie sieht die Zukunft von PetSets aus

Mit dem Release von Kubernetes v1.5 verlässt PetSets den Alpha Status und wird unter dem Namen StatefulSet in den Beta Status gehoben. Dies ist eine wichtige Entwicklung, da sich die externe API in Kubernetes nach dem Beta Status nicht mehr ändert. Der Release von Kubernetes v1.5 ist für den 9. Dezember 2016 geplant.

zukünftige Arbeiten

Ein weiterer Blogpost wird veröffentlicht sobald Kubernetes v1.5 released wurde. Dieser enthält Updates der YAML Definitionen und zusätzliche Anweisungen wie man MariaDB Galera Cluster auf seiner eigenen Infrastruktur testen kann.

Links

GitLab CI

10/09/2017

In diesem zweiteiligen Artikel werden wir die Continuous Integration Funktionalitäten von GitLab CI genauer unter die Lupe nehmen.

Der erste Teil widmet sich der Installation und Konfiguration, während der zweite Teil detaillierter auf einzelne Anwendungsszenarien eingehen wird.

Was ist CI überhaupt?

CI steht in diesem Fall für Continous Integration und bedeutet vereinfacht gesagt, dass nach jeder Änderung am Source Code eines Projektes ein Script ausgeführt wird.
Das CI-Script wird in den meisten Fällen über eine Versionsverwaltung getriggert. Dateien welche dieses Script erstellt, können später als sogenannte Build Artifacts weiterverwendet werden.

In vielen Fällen startet dieses Script Unit-Tests, welche überprüfen, ob die soeben gemachten Änderungen nicht unbeabsichtigt andere Teile des Projektes beeinflussen.
Unit-Tests sind jedoch nur ein Szenario, ein CI kann beispielsweise auch Pakete bauen, Dokumentationen erstellen oder sogar Anwendungen deployen.

GitLab CI

GitLab ist eine Open Source Lösung für Git-Hosting, die unter anderem auch CI-Funktionalität bietet. Seit Version 8.2 ist der CI-Teil fest in GitLab integriert und muss nicht mehr wie zuvor auf einem separaten Server installiert werden.

Das Konzept von GitLab CI basiert auf so genannten Runnern, welche die eigentlichen Build-Scripts ausführen. GitLab CI ist nur für das Orchestrieren dieser Runner und dem Zusammentragen von Resultaten und Artifacts zuständig. Runner können Docker-Container, VMs oder auch bare metal Maschinen sein, die optimalerweise nicht auf demselben Server wie der Orchestrator ausgeführt werden. Dadurch haben die CI-Scripte kein Zugriff auf GitLab selbst.

Die Runner melden sich alle paar Sekunden über HTTPS bei GitLab und fragen nach, ob es für sie einen Job gibt, GitLab CI kann die Runner von sich aus nicht kontaktieren.
Dies erleichtert die Einrichtung von neuen Runnern erheblich, einzige Voraussetzung ist, dass sich der Runner per HTTPS mit GitLab verbinden kann.

Die Runner-Komponente von GitLab nennt sich gitlab-ci-multi-runner [1] und ist ein statisch kompiliertes Binary, welches neben Linux auch Builds auf Windows, OSX und BSD unterstützt.

Das CI-Script für GitLab muss im Hauptverzeichnis des jeweiligen Repos liegen und den Dateinamen .gitlab-ci.yml haben.

CI für ein Projekt aktivieren

Seit Version 8.2 ist die CI-Komponente in GitLab integriert und standardmässig aktivert. Sie kann jedoch auch nur für einzelne Projekte aktiviert bzw. deaktiviert werden.
Die Einstellung nennt sich Builds und ist in den Projekteinstellungen unter Features zu finden:

GitLab löscht keine bereits ausgeführten Builds und Artifacts wenn das Feauture deaktiviert wird, sondern versteckt nur den Menüpunkt Builds.

Unter gitlab.example.com/group/repo/builds sind diese weiterhin einsehbar. Builds zu löschen ist über das Webinterface machbar. Seit GitLab 8.9 ist es möglich im .gitlab-ci.yml zu konfigurieren, wie lange die Resultate und Artifacts aufbewahrt werden. [2]

Runner

Setup

Nach dem Aktivieren von CI im Webinterface, muss ein Runner eingerichtet werden, welcher die effektiven Builds ausführt. In diesem Beispiel wird gitlab-ci-multi-runner auf einer Debian Jessie VM mit dem Docker-Executor eingerichtet. Im zweiten Teil dieses Artikels wird detaillierter auf weitere Runner-Konfigurationen eingegangen.

Als erster Schritt sollte das System aktualisiert werden:

# apt-get update
# apt-get upgrade

Da Docker-Runner verwendet werden, braucht es entsprechend Docker:

# curl -sSL https://get.docker.com/ | sh

GitLab stellt ein Script zur Verfügung, welches das Repository automatisch konfiguriert. Dies kann mit folgendem Kommando ausgeführt werden:

# curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash

Wer keine Scripte aus dem Internet unkontrolliert ausführen möchte, kann das Repo auch auf den traditionellen Weg aktivieren:

# echo "deb https://packages.gitlab.com/runner/gitlab-ci-multi-runner/debian/ jessie main" >> /etc/apt/sources.list
# wget -qO - https://packages.gitlab.com/gpg.key | apt-key add -
# apt-get update

Anschliessend kann gitlab-ci-multi-runner über APT installiert werden:

# apt-get install gitlab-ci-multi-runner

Dies richtet einen systemd-Dienst ein, welcher (auf Debian Systemen) per Default aktiviert und gestartet sein sollte:

# systemctl status gitlab-runner.service
● gitlab-runner.service - GitLab Runner
Loaded: loaded (/etc/systemd/system/gitlab-runner.service; enabled)
Active: active (running) since Mon 2016-09-19 18:33:32 CEST; 1 weeks 0 days ago
Main PID: 28102 (gitlab-ci-multi)
CGroup: /system.slice/gitlab-runner.service
└─28102 /usr/bin/gitlab-ci-multi-runner run --working-directory /home/gitlab-runner --config /etc/gitlab-runner/config.toml --service gitlab-runner --syslog...

Der Runner ist nun bereit und kann in Projekten registriert und verwendet werden.

Runner registrieren

Der neu installierte Runner muss nun in GitLab registriert werden. Jedes Projekt hat hierzu ein eindeutiges Runner-Token, welches im Webinterface unter Project Settings > Runner eingesehen werden kann:

Mit diesem Token kann der zuvor eingerichtete Runner aktiviert werden. Dazu auf der Runner-VM folgendes Kommando eingeben:

# gitlab-ci-multi-runner register \
--url gitlab.example.com
--registration-token L4zpDbiAAA86sDnvYPkn \
--description "CI hello world" \
--executor "docker" \
--docker-image debian:jessie

Parameter:

  • --url GitLab CI URL
  • --registration-token Registration Token
  • --description Beschreibung des Runners
  • --executor Executor, in diesem Fall Docker
  • --docker-image Docker-Image, unter dem der Runner läuft. In diesem Beispiel wird ein Jessie-Container als Runner verwendet.

Der Runner sollte nach einigen Sekunden im Webinterface erscheinen:

.gitlab-ci.yml

Nachdem der Runner eingerichtet und registriert ist, muss das .gitlab-ci.yml im Hauptverzeichnis des Projektes erstellt werden. In unserem Beispiel wird nur eine Datei erstellt und getestet, ob diese vorhanden ist:

stages:
- test

run-test:
stage: test
script:
- touch foo
- test foo

Nach dem Commiten und Pushen sollte unter Builds bereits ein laufender Build erscheinen:

Der Build sollte bereits nach einigen Sekunden abgschlossen sein:

Ausblick

Im zweiten Teil werden wir detaillierter auf Real-Life Anwendungsszenarien eingehen und weitere Runner-Typen vorstellen.

Referenzen


  1. https://gitlab.com/gitlab-org/gitlab-ci-multi-runner
  2. https://docs.gitlab.com/ce/ci/yaml/README.html#artifactsexpire_in

LUKS encrypted devices remote über Dropbear SSH öffnen

21/08/2017

Manchmal wäre es doch praktisch, wenn man nicht im Büro sitzt, die Kiste schön brav abgestellt ist zum Strom sparen und man einfach einem Kollegen sagen könnte, er solle mal den Rechner hochfahren. Man könnte danach die wunderbare Technologie namens SSH benutzen. Leider macht einem da die verschlüsselte Festplatte einen Strich durch die Rechnung: Um die Verschlüsselung aufzuheben, muss jemand die Passphrase eintippen.

Abhilfe hierfür bietet der lightweight SSH-Server Dropbear, welchen man im initramfs installieren kann. Zusätzlich sorgt man dafür, dass das Netzwerk-Interface gestartet wird und gibt die verschlüsselte Partition an.

Auf meinem Arch Linux waren hierfür folgende Schritte nötig:

Dropbear installieren und konfigurieren

Als erstes müssen aus dem AUR die Pakete mkinitcpio-utils und mkinitcpio-dropbear installiert werden:

$ yaourt -S mkinitcpio-dropbear mkinitcpio-utils

Diese installieren die nötigen Hooks für das initramfs. Im Anhang ist ein Link zu einem Beispiel, wie in Ubuntu ein solcher Hook gemacht werden kann.

Als nächstes muss der Public Key, mit dem man sich später am System anmelden will, in der Datei /etc/dropbear/root_key hinterlegt werden. Die Datei ist aufgebaut wie die bekannten authorized_key Dateien unter ~/.ssh/, es können also mehrere Keys angefügt werden. Bei jedem neuen Key muss aber das initfamfs neu gebaut werden!

mkinitcpio.conf vorbereiten

Als nächstes wird die /etc/mkinitcpio.conf angepasst. Einerseits muss das Kernel-Modul für den Netzwerkchip unter MODULES="" hinzugefügt werden. Um das Modul herauszufinden, kann lspci benutzt werden:

$ lspci -k

Unter den jeweiligen Devices findet man das dafür geladene Modul in der Zeile Kernel modules. Andererseits muss in den Hooks netconf, dropbear und encryptssh vor filesystems hinzugefügt werden.

Das Ganze sieht danach ungefähr so aus:

[..]
MODULES="r8169"
HOOKS="base udev autodetect modconf block netconf dropbear encryptssh filesystems keyboard fsck"

Anschliessend muss das initramfs neu gebaut werden:

$ mkinitcpio -p linux

Bootloader konfigurieren

Im Bootloader müssen dem Linux-Kernel noch Optionen zur Kernel-Zeile hinzugefügt werden. In meinem Fall (systemd-boot) ist dies unter /boot/loader/entries/arch.conf zu finden. Bei GRUB kann dies über Variablen in der Datei /etc/default/grub gemacht werden (siehe Arch Linux Wiki).

Die eine Option ist cryptdevice, womit angegeben wird, welches Device (sprich: Partition) überhaupt entschlüsselt werden soll. Am besten gibt man hier die UUID (ls -l /dev/disk/by-uuid/) an, im Format UUID=<effektive-uuid> und nicht der Blockdevice Name unter /dev, da sich dieser ändern kann.

Die andere Option sagt dem Kernel, wie das Netzwerk konfiguriert werden soll. Diese Option heisst schlicht ip und kann entweder per DHCP oder manuell konfiguriert werden. Um DHCP zu verwenden, kann einfach ip=:::::eth0:dhcp angegeben werden. In meinem Falle wollte ich jedoch eine statische Konfiguration, damit ich weiss, wohin ich per SSH verbinden muss. Das Format dafür ist folgendermassen: ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip> (siehe auch die Kernel-Dokumentation), wobei dies bei mir nicht vollständig zutraf. Den letzten Doppelpunkt musste ich auslassen, damit es funktioniert. Zu beachten ist auch, dass für <device> der Kernel-Name (ethX) und nicht der Udev-Name verwendet werden muss:

dmesg | grep eno1
16.593465] r8169 0000:03:00.0 eno1: renamed from ==eth0==

Das Resultat sieht so aus:

title    Arch Linux
linux    /vmlinuz-linux
initrd    /initramfs-linux.img
options   ip=10.9.4.70::10.9.1.1:255.255.0.0:huckfinn:eth0:none: cryptdevice=UUID=9c53e8b6-07bf-4e9d-bb3e-feaf55c02f41 root=/dev/sda2 rw

Crypttab

Ich hatte vorher in meinem Boot-Prozess im /etc/crypttab meine verschlüsselte Partition zur Entschlüsslung angegeben. Da die Partition bereits mit dem Parameter cryptdevice definiert wird, ist keine crypttab Konfiguration mehr nötig. Der Eintrag muss dort deshalb entfernt werden, da die Partition ja bereits geöffnet ist. In der fstab kann nun auch nicht mehr /dev/mapper/XYZ verwendet werden; wenn man die UUID benutzt, funktioniert es aber ohne Probleme.

Nun ist es an der Zeit, sich mit dem Live-Boot USB Stick zu bewaffnen und den Rechner neu zu starten. Im Idealfall sollte es etwa folgendermassen aussehen:

Hat man sich in der IP-Konfiguration vertan, ist dies weniger schlimm: Die Fehlermeldung sollte recht aufschlussreich sein, man kann einfach weiter booten und die Anpassung vornehmen. Ist hingegen der cryptdevice-Parameter falsch, kommt man in einen Zustand, in dem es nicht weiter geht. Dann heisst es, Live-System booten, Partition, auf der die Konfiguration liegt, mounten und den Parameter korrigieren.

Weiterführende Links

Yubikey Validation Server Setup

09/07/2017

Soll nicht die YubiCloud für das Validieren von Yubico One-time Passwörtern (OTP) genutzt werden, können die Validation Server auch selber betrieben werden. Dies ist grundsätzlich nicht sehr schwer, jedoch ist das redundante Setup eher dürftig dokumentiert.
OTP Validation Process

Überblick

Als Backend wird MySQL und als Frontend Apache eingesetzt. Die zwei Komponenten yubikey-val und yubikey-ksm sind simple PHP Applikationen, welche aus ein paar wenigen Dateien bestehen.
Blockschema Validation Server

Installation

Webserver

Als erstes sollte Apache, PHP und MySQL installiert werden. Aufgrund der Paket Dependency von yubikey-ksm und yubikey-val kann kein MariaDB Server genutzt werden.
Danach sollten zwei Datenbanken (ykksm und ykval) mit dazugehörigen Benutzern (ykksm_reader und ykval_verifier) erstellt werden.

yubikey_ksm

Im Yubikey Key Storage Module (KSM) werden die einzelnen Yubikeys gespeichert, sprich hier sind alle Daten (inkl. des geheimen AES Keys) aller Yubikeys erfasst. Der Service kann dazu genutzt werden, zu verifizieren, ob ein Yubikey mit dazugehörigem One-Time Passwort (OTP) gültig ist, jedoch nicht ob es eine Replay Attacke ist.

Da die Software keinen Mechanismus kennt, wie die Server synchronisiert werden können, wird dies mit einem MySQL Master-Slave Setup gelöst. Eine entsprechende Anleitung, wie dies gemacht werden kann, ist z.B. auf DigitalOcean vorhanden. Zu beachten ist, dass nur die Datenbank ykksm gesynct werden sollte (binlog_do_db = ykksm).

Auf einem Debian ist die Software yubikey_ksm simpel per apt install yubikey-ksm installiert. Danach ist der Apache und die Datenbank auch schon konfiguriert. Wer die Konfiguration nachträglich noch ändern will, findet die entsprechenden Dateien in /etc/yubico/ksm/. Eine umfassendere Installations Anleitung ist bei Yubico zu finden.

Apache ist so konfiguriert, dass es einen globalen Alias /wsapi/decrypt als /usr/share/yubikey-ksm/ykksm-decrypt.php gibt, falls auf dem Apache mehrere VirtualHosts vorhanden sind, sollten diese in der Konfiguration deaktiviert und nur den für yubikey_ksm benötigten VirtualHost aktiviert werden.

Neue Yubikeys können mit dem Tool ykksm-gen-keys erstellt werden. Dies gibt folgenden Output:

$ ykksm-gen-keys 1
1,cccccccccccb,42e31d069785,cf00b1f4c2c80e395b5e7532a5929cba,d05f7e394f0e,2016-03-22T13:12:25, 

In der Datenbank ist die Tabelle yubikeys vorhanden. Diese hat folgendes Schema:

CREATE TABLE `yubikeys` (
  `serialnr` int(11) NOT NULL,
  `publicname` varchar(16) NOT NULL,
  `created` varchar(24) NOT NULL,
  `internalname` varchar(12) NOT NULL,
  `aeskey` varchar(32) NOT NULL,
  `lockcode` varchar(12) NOT NULL,
  `creator` varchar(8) NOT NULL,
  `active` tinyint(1) DEFAULT '1',
  `hardware` tinyint(1) DEFAULT '1',
  PRIMARY KEY (`publicname`),
  UNIQUE KEY `publicname` (`publicname`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Sollen neue Yubikeys erstellt und gerade in die Datenbank hinterlegt werden, kann dies mit folgendem Script erledigt werden:

#!/bin/bash

MYSQL='mysql'

NEXTID=$(echo "SELECT t1.serialnr + 1 FROM ykksm.yubikeys t1 WHERE NOT EXISTS (SELECT serialnr FROM ykksm.yubikeys t2 WHERE t2.serialnr = t1.serialnr + 1) LIMIT 1;" | $MYSQL | tail -n 1)
if [ -z "${NEXTID}" ]; then
    NEXTID='1'
fi

KEY="$(ykksm-gen-keys ${NEXTID} | grep -v ^#)"

IFS=',' read -r -a ARR <<< "$KEY"

SQL="INSERT INTO ykksm.yubikeys VALUES (${ARR[0]}, '${ARR[1]}', '${ARR[5]}', '${ARR[2]}', '${ARR[3]}', '${ARR[4]}', 'bash', 1, 1);"
echo $SQL | $MYSQL

echo "Set Yubikey:"
echo "ykpersonalize -1 -y -a${ARR[3]} -o fixed=${ARR[1]} -o uid=${ARR[2]}"

yubikey_val

Der Yubikey Validation Service macht die eigentliche Validierung der One-Time Passwörtern (OTP). Es werden folgende Punkte verifiziert und verarbeitet:

  • Client: Der anfragende Client muss sich verifizieren, jeder Client hat eine ID und ein dazugehöriges Passwort. Dies verhindert, dass nicht jeder Client eine Anfrage stellen kann. Als Client wird in diesem Fall z.B. ein ssh Daemon oder eine Webseite verstanden, nicht der User davor.
  • Yubikey: Das OTP wird an den Service yubikey_ksm weitergeleitet. Ist das OTP gültig, wird durch den Service yubikey_val getestet, ob es sich um eine Replay Attacke handelt.
  • Sync: Die anderen Valdiation Server werden über den aktuellen Counter des Yubikeys benachrichtigt, damit keine Replay Attacke an einem anderen Validation Server getätigt werden kann.

Auf einem Debian System ist die Installation mit apt install yubikey-val gemacht. Die entsprechenden Konfigurationsdateien befinden sich im Verzeichnis /etc/yubico/val/.
Danach ist Apache auch schon wieder global konfiguriert. Bei mehreren VirtualHosts sollte die Konfiguration wieder spezifisch für nur einen VirtualHost eingerichtet werden. Folgende Aliase sollten dabei konfiguriert sein:

  • /wsapi/2.0/verify als /usr/share/yubikey-val/ykval-verify.php
  • /wsapi/verify als /usr/share/yubikey-val/ykval-verify.php
  • /wsapi/2.0/sync als /usr/share/yubikey-val/ykval-sync.php
  • /wsapi/2.0/resync als /usr/share/yubikey-val/ykval-resync.php
  • /wsapi/revoke als /usr/share/yubikey-val/ykval-revoke.php

Datenbank

Die Datenbank beinhaltet drei Tabellen:

  • clients
    Hier werden die Clients (z.B. ein PAM oder Mediawiki) erfasst, welche den Validation Server anfragen dürfen.
CREATE TABLE `clients` (
  `id` int(11) NOT NULL,
  `active` tinyint(1) DEFAULT '1',
  `created` int(11) NOT NULL,
  `secret` varchar(60) NOT NULL DEFAULT '',
  `email` varchar(255) DEFAULT NULL,
  `notes` varchar(100) DEFAULT '',
  `otp` varchar(100) DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  • queue
    In der queue sind die Einträge, welche noch mit anderen Validation Servern abgeglichen werden müssen. Die Einträge werden vom System Service ykval-queue bearbeitet.
CREATE TABLE `queue` (
  `queued` int(11) DEFAULT NULL,
  `modified` int(11) DEFAULT NULL,
  `server_nonce` varchar(32) NOT NULL,
  `otp` varchar(100) NOT NULL,
  `server` varchar(100) NOT NULL,
  `info` varchar(256) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  • yubikeys
    In dieser Tabelle sind die Yubikeys mit ihren entsprechenden Countern eingetragen. Diese Tabelle wird durch yubikey_val selber mit den anderen Validation Server synchronisiert.
CREATE TABLE `yubikeys` (
  `active` tinyint(1) DEFAULT '1',
  `created` int(11) NOT NULL,
  `modified` int(11) NOT NULL,
  `yk_publicname` varchar(16) NOT NULL,
  `yk_counter` int(11) NOT NULL,
  `yk_use` int(11) NOT NULL,
  `yk_low` int(11) NOT NULL,
  `yk_high` int(11) NOT NULL,
  `nonce` varchar(40) DEFAULT '',
  `notes` varchar(100) DEFAULT '',
  PRIMARY KEY (`yk_publicname`),
  UNIQUE KEY `yk_publicname` (`yk_publicname`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Synchronisation

Der Service kennt einen eigenen Mechanismus zur Synchronisation. Da jeweils der höchste Counter (der OTP) jedes Yubikeys auf allen Servern bekannt sein sollte, wird dies nicht über eine MySQL Replication gelöst. Dies hätte zur Folge, dass auch tiefere Counter synchronisiert werden.

Damit die Server synchronisieren dürfen, muss zuerst noch die Konfiguration angepasst werden. Als Beispiel werden die beiden Hosts srv-tfvalid-01 (IP 128.66.1.1) und srv-tfvalid-02 (IP 128.66.1.2) genutzt. Folgende Konfigurationsparameter müssen angepasst werden:

  • __YKVAL_SYNC_POOL__
    Hier werden die Server eingetragen, mit welchen synchronisiert werden soll.
$baseParams['__YKVAL_SYNC_POOL__'] = array(
  "https://srv-tfvalid-01/wsapi/2.0/sync",
  "https://srv-tfvalid-02/wsapi/2.0/sync"
);
  • __YKVAL_ALLOWED_SYNC_POOL__
    Hier werden alle IP Adressen aller Validation Server eingetragen, damit diese synchronisieren dürfen.
$baseParams['__YKVAL_ALLOWED_SYNC_POOL__'] = array(
  "127.0.0.1",
  "128.66.1.1",
  "128.66.1.2"
);
  • __YKVAL_RESYNC_IPS__
    Dieser Wert wird auf den Wert von __YKVAL_ALLOWED_SYNC_POOL__ gesetzt.
$baseParams['__YKRESYNC_IPS__'] = $baseParams['__YKVAL_ALLOWED_SYNC_POOL__'];
  • __YKVAL_SYNC_DEFAULT_LEVEL__
    Dieser Wert definiert den mindest Level in Prozent, wie viele Validation Server erfolgreich synchronisiert werden müssen, bevor das OTP als gültig deklariert wird. Sind Server nicht erreichbar, kann dies zur Folge haben, dass das OTP, aufgrund zu wenigen Antworten bei der Synchronisation, als ungültig deklariert wird.
    Werte zwischen 0 und 100 sind möglich. Sind im gesamten Sync Pool zwei Server, und wird Server 1 angefragt, hat dieser folgende Sync Level:

    • Server 2 online: 100
    • Server 2 offline: 0

Sind im gesamten Sync Pool drei Server und wird Server 1 angefragt, hat er folgende Sync Level:

  • Beide Server online: 100
  • Ein Server online, anderer offline: 50
  • Beide Server offline: 0

Damit bei einem Setup mit zwei Servern einer offline sein darf, muss der Wert also folgendermassen auf 0 gesetzt werden:

$baseParams['__YKVAL_SYNC_DEFAULT_LEVEL__'] = 0;
Services für die Synchronisation

Der System Service ykval-queue kann bei systemd mit folgendem Service File gestartet werden:

[unit]
Description=Yubikey Validation Server Sync Queue
After=network.target

[Service]
ExecStart=/usr/sbin/ykval-queue
Restart=on-failure

[Install]
WantedBy=multi-user.target

Zusätzlich muss noch der Cronjob, welcher die Synchronisation auslöst, erstellt werden. Der Cronjob muss pro Kombination Validation Server – Validation Server erstellt werden und sieht in etwa wie folgt aus:

* * * * * /usr/sbin/ykval-synchronize validation-server-2 all

Neue Validation Clients erstellen

Neue Clients können mit dem Tool ykval-gen-clients erstellt werden. Mit folgendem Bash Script können Clients automatisch erstellt und in die Datenbank gespeichert werden:

#!/bin/bash

MYSQL='mysql'
ERRORMSG='Failed to insert new client with query '

CLIENT="$(ykval-gen-clients 1 2>&1)"

echo "USE ykval; ${CLIENT#$ERRORMSG}" | $MYSQL

echo "Client configuration:"
echo ${CLIENT#$ERRORMSG}

Validation Server nutzen

Die Validation Server sollten vor der Nutzung noch getestet werden. Zum Beispiel sollte getestet werden, ob eine Replay-Attacke an den zweiten Server nicht möglich ist, nachdem am ersten Server die Validierung schon gemacht wurde. Ein weiterer Test ist, ob die MySQL Datenbanken sich synchronisieren und die Einträge in der Tabelle ykval.queue abarbeiten.

SSHd

Soll ein SSH Server mit TwoFactor abgesichert werden, wird dies am einfachsten im PAM (Pluggable Authentication Modules for Linux) gemacht. Es gibt ein extra PAM Modul (pam_yubico), welches die Yubikey Validation Server anfragt.

PAM ist in diesem Beispiel ein Client vom Validation Server, also muss für PAM eine ID und ein Key mit ykval-gen-clients generiert werden. Es sollen nur User in der POSIX Gruppe Users eine TwoFactor Validierung machen müssen, bei den anderen reicht ein Login ohne. Die ID der Yubikeys wird im LDAP mit dem User verknüpft, sprich die User, welche eine TwoFactor Validierung machen müssen, haben im LDAP ein Attribut (im Beispiel: yubikey), welches dem publicname ihres Yubikeys entspricht (Beispiel: cccccccccccb).

Die folgende Konfiguration wird zusätzlich in /etc/pam.d/sshd eingefügt. Am besten nach der Zeile, welche die Passwort Authentifizierung macht, also beispielsweise nach pam_unix.so oder pam_ldap.so in der Chain auth. Diese können auch mit einem @include aus z.B. der common-auth eingebunden werden, daher kann hier nicht eine fertige Konfiguration angegeben werden.

auth [success=2 default=ignore] pam_succeed_if.so user notingroup Users
auth [success=1 default=ignore] pam_yubico.so id=1 \
                                              
key=bjMN3jRHquwHr5NqNKN+LEFZUjY= \
                                              
urllist=https://srv-tfvalid-01/wsapi/2.0/verify;https://srv-tfvalid-02/wsapi/2.0/verify \
                                             
ldap_uri=ldap://ldap1.example.com:389/;ldap://ldap2.example.com:389/ \
                                             
ldapdn=cn=users,dc=example,dc=com \
                                              
user_attr=uid \
                                              
yubi_attr=yubikey \
                                              
verbose_otp
auth requisite                  
pam_deny.so

Danach sollte das Login auf den Server getestet werden (Achtung: Dabei immer ein root Login offen halten, da wenn etwas nicht funktioniert, gegebenenfalls kein Login mehr möglich ist).

SSL Zertifikate monitoren mit Bash

30/06/2017

certcheck

Damit wir im Monitoring-Tool sehen können, ob und wann ein SSL-Zertifikat abläuft, wurde ein Script zur Ermittlung des Ablaufdatums geschrieben. Im Vergleich zu anderen Checks, kann dieses Script nicht nur Zertifikate via HTTPS prüfen.

Hier findest du eine Beschreibung, wie das Ablaufdatum eines SSL Zertifikats geholt wird.

openssl s_client -servername "${HOST}" -connect "${HOST}":"${PORT}" 2>&- | openssl x509 -enddate -noout

Damit überhaupt eine Verbindung zu einem SSL Host gemacht werden kann, braucht man zuerst einen SSL client. Hier wird der s_client von OpenSSL verwendet, welcher auch TLS beherrscht. Der s_client dient allgemein zum Diagnostizieren und Debuggen von SSL/TLS Zertifikaten.

servername

Über den Parameter “servername” kann man verifizieren, dass das richtige Zertifikat benützt wird, falls ein Server über VHosts mehrere SSL Zertifikate hat.
Dies geschieht über SNI (Server Name Indication).

connect

Hier geschieht die Verbindung zum Host über den Hostname und Port.

openssl x509 -enddate -noout

Nun wird das x509 Zertifikat ausgelesen und durch die Parameter “enddate” und “noout” wird das Ablaufdatum ausgegeben respektive dass das Zertifikat nicht nach Datum XY gültig ist. Durch “noout” wird der restliche Output verhindert.

Alarm, Alarm!

Das Ziel war es selbst bestimmen zu können, wie und wann Nagios / Icinga alarmiert. Damit Nagios / Icinga alarmiert braucht es den Error Status Code 2. Error Codes:

  • 1 – Warning
  • 2 – Critical

0 ist natürlich OK. Es werden Parameter zum Einstellen der Warning- und Critical-Werte vergeben. Standartwerte sind:

  • Warning=30 (Tage)
  • Critical=5 (Tage)

Das Ablaufdatum wird mit dem aktuellem Datum verglichen, so ist erkennbar, wie viele Tage man noch hat bis das Zertifikat abläuft.

Das Monitoring Script ist auf GitHub verfügbar.

Integration ins Monitoring

Das Script wird bei uns als Icinga Plugin ausgeführt.

Commands sind im configfile “/etc/icinga/icinga.cfg” definiert bzw. es ist beschrieben wo diese definiert sind.

Untenstehend der Auszug aus der Icinga-Konfiguration:

define command {
    command_name check _ssl
    command_line /bin/bash $USER1$/check_ssl.sh -H $ARG1$ -p $ARG2$ -P $ARG3$ -w $ARG4$ -c $ARG5$
}

Die $ARG$ Variablen werden dann durch eine Service Definition gegeben, welche sich standartmässig in /etc/icinga/objects/services_icinga.cfg befindet.

So wird der SSL Check Service für einen Host definiert:

define service {
        use                             template-service
        host_name                       Hostname (e.g. adfinis.com)
        service_description             check_ssl
        max_check_attempts              5
        check_interval                  360
        retry_interval                  1
        check_command                   check _ssl!'adfinis.com'!'443'!'no_tls'!'30'!'5'
}

Bei der check_command Zeile werden die $ARG$ Variablen durch Ausrufezeichen getrennt und werden danach auch in dieser Reihenfolge vergeben. Das heisst:

  • adfinis.com = $ARG1
  • 443 = $ARG2$
  • no_tls = $ARG3$
  • 30 = $ARG4$
  • 5 = $ARG5$

Links