Archiv des Autors: Jörg Kastning

Reverse-Proxy für Kanboard im Container

Im folgenden möchte ich kurz die Konfiguration dokumentieren, mit der ich Ziel 4 aus „Kanboard im Container…“ umgesetzt habe.

Zuerst habe ich meinen Pod mit dem Kanboard- und dem Posgresql-Container erstellt:

$ cat create-kanboard-pod.sh 
#!/bin/bash
kanboard_data=$(podman volume inspect kanboard_data --format {{.Mountpoint}})
kanboard_plugins=$(podman volume inspect kanboard_plugins --format {{.Mountpoint}})
kanboard_ssl=$(podman volume inspect kanboard_ssl --format {{.Mountpoint}})
pgsql_data=$(podman volume inspect pgsql_data --format {{.Mountpoint}})
podman run -d --pod new:kanboardpod --name kanboard -p 127.0.0.1:8080:80 -v $kanboard_data:/var/www/app/data:Z -v $kanboard_plugins:/var/www/app/plugins:Z kanboard/kanboard
podman run -d --pod kanboardpod --name pgsql_db -e POSTGRESQL_USER=root -e POSTGRESQL_PASSWORD=SuperGeheimesPasswort -e POSTGRESQL_DATABASE=kanboard -v $pgsql_data:/var/lib/pgsql/data:Z rhel8/postgresql-96

Als Reverse-Proxy nutze ich nginx, welchen ich mittels sudo dnf -y install nginx installiert habe. Die Konfigurationsdatei /etc/nginx/nginx.conf habe ich wie folgt ergänzt:

    server {
        listen 80;
        listen [::]:80;
        server_name kanboard.beispiel.de;
        return 301 https://kanboard.beispiel.de$request_uri;
    }

    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name kanboard.beispiel.de;

        ssl_certificate "/etc/letsencrypt/kanboard_fullchain.cer";
        ssl_certificate_key "/etc/letsencrypt/kanboard.beispiel.de.key";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers PROFILE=SYSTEM;
        ssl_prefer_server_ciphers on;

        location / {
            proxy_pass http://127.0.0.1:8080;
        }
    }

Damit nimmt der NGINX Anfragen auf den TCP-Ports 80 und 443 an, wobei ein Redirect von Port 80 auf 443 erfolgt. Diese Anfragen leitet der NGINX an den TCP-Port 8080 des Kanboard-Pods weiter.

Die Erneuerung des SSL-Zertifikats habe ich nach dem Tutorial „Let’s Encrypt: Nutzung des DNS-Alias-Modus mit dem acme.sh-Client“ automatisiert.

Backup und Restore im Kanboard-Container-Land

In „Kanboard im Container“ habe ich einen Pod ins Leben gerufen, welcher die Anwendung Kanboard und eine dazugehörige Postgresql-Datenbank mittels Container bereitstellt. Backup und Restore zu konfigurieren und zu testen, habe ich letztes Wochenende nicht mehr geschafft. Dies hole ich in diesem Artikel nach.

Da die Container selbst zustandslos sind, interessieren mich nur die persistent gespeicherten Daten, welche außerhalb der Container im Dateisystem des Hosts gespeichert werden.

Umgebung

Auf einer virtuellen Maschine mit dem Gastbetriebssystem RHEL 8 läuft ein podman-Pod namens kanboardpod. Dieser Pod beinhaltet neben dem Infrastruktur-Container, einen Kanboard-Container namens kanboard und einen Postgresql-Container namens pgsql_db.

Die persistenten Daten des Kanboard-Containers werden in den Volumes kanboard_data und kanboard_plugins gespeichert. Die Datenbank-Dateien der Postgresql-DB liegen im Volume pgsql_data.

Die Namen des Pods und der Container sind hilfreich, da die Container darüber referenziert werden können. So muss man nicht mit den sperrigen IDs hantieren.

Backup

Kein Restore ohne Backup! Kein Backup, kein Mitleid!

Ich habe im Folgenden beschriebe Ansätze für ein Backup. Wenn ihr noch weitere habt, freue ich mich über eure Eingaben.

Ansatz 1: Backup auf Dateiebene — verworfen

Dieser Ansatz liegt nahe und ist einfach umzusetzen.

  1. Container stoppen
  2. Verzeichnisse im Dateisystem sichern
  3. Container wieder starten

Für meinen konkreten Anwendungsfall wäre dies auch ausreichend. Einen Dienst für die Dauer einer Datensicherung komplett stoppen zu müssen, ist jedoch nicht ideal. Daher schaue ich nach weiteren Möglichkeiten.

Ansatz 2: DB-Dump und Datei-Backup — Praktikabel aber möglicherweise inkonsistent.

Bei diesem Ansatz bleibt die Anwendung während der Datensicherung verfügbar. Während das Volume kanboard_data auf Dateiebene mittels tar gesichert wird, wird die Datenbank mittels pg_dump aus dem Container heraus gesichert. Das Backup-Skript sieht wie folgt aus:

#!/bin/bash
kanboard_data=$(podman volume inspect kanboard_data --format {{.Mountpoint}})
kanboard_plugins=$(podman volume inspect kanboard_plugins --format {{.Mountpoint}})
tar czf kanboardfiles.tgz $kanboard_data $kanboard_plugins
podman exec -t pgsql_db /usr/bin/pg_dump kanboard | gzip > ~/kanboard.sql.gz

Hinweis: Obiger Code-Schnipsel stammt aus der Kategorie Schnell-und-Schmutzig und sollte nicht in produktiven Umgebungen verwendet werden.

Der DB-Dump wird dabei direkt aus dem Container in mein Home-Verzeichnis geschrieben. Am Ende habe ich zwei unabhängige Dateien, die nun außerhalb der Container-Umgebung liegen. Da diese jedoch immer noch auf dem gleichen Host liegen, handelt es sich um kein richtiges Backup. Doch ist es nun nicht mehr schwer, sie auf ein anderes, entferntes Medium zu übertragen.

Der größte Nachteil dieses Ansatzes besteht darin, dass die Konsistenz der Sicherung nicht garantiert ist. Die Datenbank enthält Referenzen in das Dateisystem. Referenzen in der Datenbank und Inhalt des gesicherten Dateisystems müssen nicht in jedem Fall zueinander passen, da während der Sicherung weiter in der Anwendung gearbeitet werden kann.

Nun kann man natürlich vor der Sicherung den Container kanboard stoppen und anschließend wieder starten. Damit erhält man eine konsistente Sicherung zu dem Preis, dass die Anwendung temporär nicht verfügbar ist. Damit unterscheidet sich der Ansatz gegenüber Ansatz 1 nur noch darin, dass der Container pgsql_db online bleibt und man die DB mit pg_dump sichert, statt eine Sicherung auf Dateisystemebene zu machen.

Schön ist das nicht, doch mache ich es meiner Umgebung genau so.

Ansatz 3: Backup auf Basis eines konsistenten Dateisystem-Snapshots

Dies ist in meinen Augen der vernünftigste Ansatz.

Die persistenten Daten der Container liegen in einem Dateisystem. Unterstützt dieses Dateisystem Snapshots, können diese genutzt werden, um die Dateisysteminhalte zum Zeitpunkt des Snapshots auf das Backup-Medium zu übertragen und den Snapshot anschließend wieder zu entfernen. Anwendung und Datenbank können bei diesem Verfahren während der Sicherung weiterlaufen.

Das so erstellte Datenbank-Backup befindet sich allerdings in einem Zustand, als wäre die Datenbank unsauber beendet worden. Nach einem Restore werden demnach die WAL-Logs benötigt (siehe PostgreSQL 9.6.20 Documentation: 25.2. File System Level Backup).

Ich habe mich gegen diesen Ansatz entschieden, da mir in meiner Test-Umgebung noch die Erfahrung mit Dateisystem-Snapshots fehlt. Dies werde ich evtl. zu einem späteren Zeitpunkt unter die Lupe nehmen.

Restore

Ich habe mich für den Ansatz 2 entschieden. Damit habe ich folgende zwei Dateien:

  • kanboard.tgz – Enthält die persistenten Daten des Kanboards
  • kanboard.sql.gz – Enthält den Dump der Kanboard-Datenbank

Das tar-Archiv wird extrahiert und fertig. Um den DB-Dump mit dem Werkzeug pg_restore wieder einspielen zu können, muss dieser zuvor in ein Volume kopiert werden, das innerhalb des Postgresql-Containers zur Verfügung steht. Anschließend kann die Datenbank mit folgendem Befehl wiederhergestellt werden:

# pg_restore -C -d kanboard kanboard.sql

Fazit

Grundsätzlich habe ich Ziel Nummer 5 „Backup und Restore“ ebenfalls erreicht.

Der Restore erfordert noch einiges an Handarbeit und ist etwas fummelig. Soetwas möchte man in einer angespannten Situation nicht gerne haben. Hier ist noch etwas Feinschliff nötig.

Darüber hinaus kann ich mir vorstellen auch noch einen automatisierten Restore-Test zu etablieren, welcher prüft, ob sich ein erstellter DB-Dump auch wieder herstellen lässt. Das ist dann aber sicher ein eigenes Wochenendprojekt. Und nächstes Wochenende mache ich mal frei.

Kanboard im Container…

… aber das sind ja gleich zwei Dinge auf einmal. Richtig. Denn hier versuche ich, etwas nützliches (Kanboard) mit der Möglichkeit, etwas zu lernen (Container), zu kombinieren.

Inspiriert durch Dirks Artikel und einem darauf folgenden, regen E-Mail-Verkehr, widme ich mich mal wieder dem Thema Linux-Container. Zuletzt hatte ich mich ca. 2016/2017 damit befasst und es mit der Erkenntnis zu den Akten gelegt, dass es noch ein Hype und für den produktiven Einsatz wenig geeignet war. Mittlerweile hat sich die Lage etwas geändert. Einige Unternehmen haben sich des Themas angenommen und arbeiten daran, Linux-Container Enterprise-Ready zu gestalten. So nehme ich wahr, dass in meinem beruflichen Netzwerk seit ca. Anfang 2019 OpenShift-Cluster wie Pilze aus dem Boden schießen. Man könnte den Eindruck gewinnen, dass Red Hat diese Subskriptionen wie geschnitten Brot verkauft. Andere Hersteller scheinen den relativ jungen Markt nicht allein den roten Hüten überlassen zu wollen. So hat VMware mit vSphere 7 und Tanzu hier ebenfalls eine Lösung im Portfolio und auch SUSE scheint sich mit dem Kauf von Rancher in diesem Segment stärker zu engagieren.

Ich selbst möchte mein Wissen rund um dieses Thema auffrischen und habe mir daher folgendes Wochenendprojekt überlegt. Um Projekte, Aufgaben oder schlicht den Alltag besser zu organisieren, möchte ich zukünftig die Anwendung Kanboard nutzen. Diese Anwendung unterstützt die Aufgaben- bzw. Projekt-Organisation nach der Kanban-Methode. Sie macht einen minimalistischen Eindruck, kommt ohne viel Schnick-Schnack daher und scheint daher gut zu mir zu passen. Um gleichzeitig praktische Erfahrungen im Umgang mit Linux-Containern zu sammeln, werde ich Kanboard mit einer Postgresql-Datenbank mit Hilfe von zwei Containern betreiben.

In meinen Augen wird Docker in den nächsten Jahren sowohl als Firma wie auch als Werkzeug stetig an Bedeutung verlieren. Daher setze ich bei der Umsetzung meines Wochenend-Projekts auf die Werkzeuge podman, skopeo und buildah.

Ich gehe in diesem Text nicht auf die Konzepte, die Architektur, sowie die Vor- und Nachteile von Linux-Containern ein. Hierzu wurde in den letzten Jahren bereits genug an anderer Stelle geschrieben. Informationen zu diesen Themen finden sich in der — im Aufbau befindlichen — Linksammlung und am Ende dieses Artikels.

Umfeld

Als Basis für dieses Projekt dient mir eine virtuelle Maschine in meinem heimischen Labor. Als Betriebssystem nutze ich ein aktuelles RHEL 8 mit der kostenlosen Developer-Subskription. Diese VM dient mir als Host zum Ausführen diverser Linux-Container. Um die Container aus dem Netzwerk erreichbar zu machen, installiere ich NGINX aus den Paketquellen von RHEL 8. Dieser kümmert sich als Reverse-Proxy um die Portweiterleitung zu den Containern.

Ziele

Mit diesem Wochenendprojekt möchte ich folgende Ziele erreichen:

  1. Bereitstellung der Anwendung Kanboard mittels Linux-Container
  2. Nutzung von Postgresql mittels Container als Kanboard-Datenbank-Backend
  3. Persistente Speicherung der Kanboard-Inhalte im Dateisystem des Hosts
  4. Erreichbarkeit und Nutzbarkeit von Kanboard über den NGINX-Reverse-Proxy
  5. Einrichtung Backup und Restore
  6. Updates

Schritt 1: rootless-Container-Umgebung einrichten

Während Entwickler viel Schweiß und Tränen investiert haben, damit Dienste wie Apache oder NGINX nach ihrem Start die root-Rechte ablegen können, liefen die ersten Linux-Container durchgängig mit root-Rechten. Dies ist aus Sicht der IT-Sicherheit nicht wünschenswert. Daher ist es in meinen Augen erfreulich, dass es mittlerweile auch ohne root-Rechte geht; Kernel User Namespaces sei Dank.

Ich folge der Red Hat Dokumentation (Kapitel 1.4, [1]), um den User Alice für die Nutzung von rootless-Containern einzurichten.

# echo "alice:165537:65536" >> /etc/subuid
[root@podhost-r8-1 ~]# echo "alice:165537:65536" >> /etc/subgid
[root@podhost-r8-1 ~]# echo "user.max_user_namespaces=65636" > /etc/sysctl.d/userns.conf
[root@podhost-r8-1 ~]# sysctl -p /etc/sysctl.d/userns.conf
user.max_user_namespaces = 65636

Anschließend installiere ich wie in Kap. 1.3 [1] beschrieben die Container-Tools.

# yum module install -y container-tools
$ podman --version
podman version 2.0.5

Der Werkzeugkasten ist bestückt. Weiter zu Schritt 2.

Schritt 2: Container-Images suchen, inspizieren und herunterladen

Mit dem Kommando podman mache ich mich auf die Suche nach Containern für Kanboard.

$ podman search kanboard
INDEX       NAME                                            DESCRIPTION                                       STARS   OFFICIAL   AUTOMATED
docker.io   docker.io/kanboard/kanboard                     Official Docker image for Kanboard                34
docker.io   docker.io/webhippie/kanboard                    Docker images for Kanboard                        2                  [OK]
docker.io   docker.io/larueli/kanboard-nonroot              Safe image for Kanboard as Non Root / Suitab...   0
docker.io   docker.io/masker/kanboard                       use alpine linux build kanboard server            0
docker.io   docker.io/xoxys/kanboard                        Deprecated                                        0
docker.io   docker.io/dotriver/kanboard                     Kanboard on Alpine Linux + S6 Overlay             0
docker.io   docker.io/thegeeklab/kanboard                   Custom image for Kanboard Kanban project man...   0
docker.io   docker.io/jonats/kanboard-pi                    Raspberry Pi image for Kanboard                   0
docker.io   docker.io/bastilian/kanboard                                                                      0
docker.io   docker.io/oriaks/kanboard                       Kanboard                                          0                  [OK]
docker.io   docker.io/kanboard/tests                                                                          0
docker.io   docker.io/blufor/kanboard                       Kanboard with Postgres, SMTP and GitLab inte...   0                  [OK]
docker.io   docker.io/boomer/kanboard                       Kanboard is a simple visual task board web a...   0
docker.io   docker.io/joshuacox/kanboard-redmine            kanboard redmine importer                         0                  [OK]
docker.io   docker.io/janost/kanboard-unit                  Kanboard + nginx unit, running rootless with...   0
docker.io   docker.io/benoit/kanboard                                                                         0                  [OK]
docker.io   docker.io/lidstah/kanboard                      Kanboard armv71 debian (nginx/php7-fpm) base...   0
docker.io   docker.io/doc75/kanboard                                                                          0
docker.io   docker.io/witsec/kanboard                       Kanboard, with the option to filter (hide) s...   0                  [OK]
docker.io   docker.io/ionutalexandru97/kanboard-openshift   Kanboard ready to be deployed on OpenShift        0
docker.io   docker.io/hihouhou/kanboard                     simple kanboard                                   0                  [OK]
docker.io   docker.io/alxsdhm/kanboard                      kanboard image                                    0
docker.io   docker.io/papango/kanboard                                                                        0
docker.io   docker.io/mrtheduke/kanboard                    kanboard                                          0
docker.io   docker.io/kvorobyev/kanboard_app

Herzlichen Glückwunsch. Die Trefferliste stellt für mich als SysAdmin einen Alptraum dar. Sämtliche Treffer stammen vom Docker-Hub, einem riesigen Misthaufen für Software (welcher durchaus ein paar Perlen enthalten kann). Von den 26 Treffern ist keiner als OFFICIAL markiert, lediglich die Anzahl STARS bietet einen Anhaltspunkt, welcher Container den meisten Zuspruch findet. In einer Produktiv-Umgebung sollte man sich jedoch nicht allein auf diese Sterne verlassen. Ich inspiziere das Container-Image mit den meisten Sternen mit skopeo:

$ skopeo inspect docker://docker.io/kanboard/kanboard | less

Die vollständige Ausgabe spare ich hier aus. Sie ist wenig hilfreich. Mit ein wenig Internet-Recherche ([2], [3] und [4]) bin ich hinreichend sicher, das „offizielle“ Container-Image des Projekts gefunden zu haben.

Als nächstes mache ich mich auf die Suche nach Postgresql:

$ podman search postgresql | wc -l
64

Naja, zumindest an Auswahl scheint es auch diesmal nicht zu mangeln. Hier komme ich so jedoch nicht weiter. Also nehme ich einen Webbrowser zur Hand und recherchiere ein geeignetes Container-Image unter der URL: https://catalog.redhat.com/software/containers/explore

Da ich Red Hat bereits zutraue, eine stabile und hinreichend sichere Enterprise Linux Distribution zu bauen, traue ich ihnen auch zu, ordentliche Container-Images für Postgresql zu bauen. Daher fasse ich folgende drei Kandidaten ins Auge:

  1. rhel8/postgresql-96
  2. rhel8/postgresql-10
  3. rhel8/postgresql-12

Zu diesem Zeitpunkt (2020-12-27) fehlt Nr. 3 eine ordentliche Beschreibung. Dafür kommt dieses Image mit 6 offenen Sicherheitslücken daher. Nr. 2 besitzt nur 3 Schwachstellen und eine deutliche bessere Dokumentation zu dessen Verwendung. Und Nr. 1 ist zwar das Älteste, jedoch auch das mit einer guten Dokumentation und ohne Schwachstellen.

Kanboard erfordert Postgresql >= 9.4. Damit ist Nummer 1 mein Gewinner. Mit den beiden folgenden Kommandos hole ich mir die Kanboard- und Postgresql-Container-Images auf meinen Host.

$ podman pull docker.io/kanboard/kanboard
Trying to pull docker.io/kanboard/kanboard...
Getting image source signatures
Copying blob df20fa9351a1 done  
Copying blob 3108c8300796 done  
Copying blob b190b4dd9bb5 done  
Copying blob bb1f52abd628 done  
Copying blob e37ffd2cbe7b done  
Copying config c355188e0c done  
Writing manifest to image destination
Storing signatures
c355188e0c187bc891826d282cc850cbe0907ccd7df28d4487d024d831c4f9af

$ podman login --username=Joerg-Dev registry.redhat.io
Password: 
Login Succeeded!
$ podman pull registry.redhat.io/rhel8/postgresql-96
Trying to pull registry.redhat.io/rhel8/postgresql-96...
Getting image source signatures
Copying blob cca21acb641a done  
Copying blob 620696f92fec done  
Copying blob fca753c96be9 done  
Copying blob d9e72d058dc5 done  
Copying config f7266b012d done  
Writing manifest to image destination
Storing signatures
f7266b012db03478b858eba6af4264829b99ce9ac67d6bc8a7c273b5fc5c8e9a

Damit ist dieser Schritt abgeschlossen. In Schritt drei erstelle ich sogenannte Volumes, um Daten außerhalb der Container persistent im Dateisystem des Hosts speichern zu können.

Schritt 3: Persistenten Speicher für Container erzeugen

Nach dem Container-Mantra haben diese zustandslos zu sein. Dies bedeutet, dass in ihnen gespeicherte Daten verloren gehen, wenn der Container entfernt wird. Nun hat es die Elektronische Datenverarbeitung (EDV) so an sich, dass das Ergebnis der Verarbeitung häufig persistent zu speichern ist. Dies kann im Container-Universum mit sogenannten Volumes erledigt werden. Hierbei wird ein Verzeichnis vom Host in den Container eingehängt.

Für mein Projekt erstelle ich nach Kapitel 3.4 [1] folgende Volumes:

  • kanboard_data
  • kanboard_plugins
  • kanboard_ssl
  • pgsql_db
$ podman volume create VOLUMENAME

Um im Folgenden etwas leichter mit diesen Volumes arbeiten zu können, speichere ich den Einhängepfad in Variablen à la:

$ mntPoint=$(podman volume inspect VOLUMENAME --format {{.Mountpoint}})

Schritt 4: Kanboard konfigurieren

Um eine angepasste, persistente config.php-Datei für den Kanboard-Container zu schreiben, ist etwas Vorarbeit notwendig. Der Kanboard-Container wird gestartet und das Volume „kanboard_data“ wird dabei in den Pfad /var/www/app/data gemountet. Anschließend starte ich eine Shell im Container und kopiere die Datei /var/www/app/config.default.php nach /var/www/app/data/config.php.

$ podman run -d --name kanboard -v $kanboard_data:/var/www/app/data  kanboard/kanboard
93e6d7e3847fb94639b8fce89ddb93a3879a80522f95ed13dff91f6558594ac6
$ podman ps
CONTAINER ID  IMAGE                               COMMAND  CREATED        STATUS            PORTS   NAMES
93e6d7e3847f  docker.io/kanboard/kanboard:latest           5 seconds ago  Up 5 seconds ago          kanboard
$ podman exec -it 93e6d7e3847f /bin/bash
bash-5.0# cp /var/www/app/config.default.php /var/www/app/data/config.php
bash-5.0# exit
exit
$ podman stop 93e6d7e3847f && podman rm 93e6d7e3847f
$ vi $kanboard_data/config.php

Um Postgresql als Datenbank-Backend zu nutzen, werden folgende Werte in der config.php gesetzt:

// Run automatically database migrations
// If set to false, you will have to run manually the SQL migrations from the CLI during the next Kanboard upgrade
// Do not run the migrations from multiple processes at the same time (example: web page + background worker)
define('DB_RUN_MIGRATIONS', true);

// Database driver: sqlite, mysql or postgres (sqlite by default)
define('DB_DRIVER', 'postgres');

// Mysql/Postgres username
define('DB_USERNAME', 'root');

// Mysql/Postgres password
define('DB_PASSWORD', 'SuperSicheresPasswort');

// Mysql/Postgres hostname
define('DB_HOSTNAME', 'localhost');

// Mysql/Postgres database name
define('DB_NAME', 'kanboard');

// Mysql/Postgres custom port (null = default port)
define('DB_PORT', null);

Normalerweise würde man eine Anwendung niemals mit dem Datenbank-Root-User auf eine Datenbank zugreifen lassen. In dieser Umgebung ist es hingegen verschmerzbar, da nur die Daten des Kanboards in diesem Postgresql-Container gespeichert werden. Im Falle einer Kompromittierung verliere ich nur die zur Anwendung gehörende Datenbank.

Schritt 5: Pod erstellen und Container hinzufügen

Mit diesem Schritt habe ich etwas Mühe. Zuerst wollte ich einen Pod erstellen, den Kanboard- und Postgresql-Container zu diesem hinzufügen, um sie stets gemeinsam starten und stoppen zu können. Dies ist laut [1] und [7] der einfachste Weg. Allerdings habe ich dann in [5] und [7] gelesen, dass sich die Container eines Pods dessen IP, MAC und Port-Bindings teilen. Dies bedeutet, dass Portfreigaben für Kanboard (80 und 443 TCP) auch für den Postgresql-Container gültig sind. Dies möchte ich eigentlich nicht. Doch ist mir bisher nichts besseres eingefallen. Falls ihr Anregungen oder konkrete Beschreibungen habt, wie ich dies besser umsetzen kann, immer her damit.

Frickelpit hat mich in seinem Kommentar darauf hingewiesen, dass man den Zugriff auf den Port des Pods noch weiter beschränken kann, indem man diesen an 127.0.0.1 bindet. Ich habe unten stehenden Code-Block entsprechend aktualisiert.

Ich erstelle nun gemäß [7] einen neuen Pod, welcher den Kanboard-Container beinhaltet und für diesen Port-Bindings besitzt:

$ podman run -d --pod new:kanboardpod --name kanboard -p 127.0.0.1:8080:80 -v $kanboard_data:/var/www/app/data -v $kanboard_plugins:/var/www/app/plugins kanboard/kanboard
e62c7fa2ecf771f4085e788e9f0f7d24b7f87d487e9951a403847d8a7a2a6471

$ podman pod ps
POD ID        NAME         STATUS   CREATED        # OF CONTAINERS  INFRA ID
d7afa6821382  kanboardpod  Running  8 seconds ago  2                6b065fe7ecc7

$ podman ps
CONTAINER ID  IMAGE                               COMMAND  CREATED         STATUS             PORTS                 NAMES
6b065fe7ecc7  k8s.gcr.io/pause:3.2                         10 seconds ago  Up 10 seconds ago  0.0.0.0:8080->80/tcp  d7afa6821382-infra
e62c7fa2ecf7  docker.io/kanboard/kanboard:latest           10 seconds ago  Up 10 seconds ago  0.0.0.0:8080->80/tcp  kanboard

Im zweiten Schritt füge ich den Postgresql-Container hinzu:

$ podman run -d --pod kanboardpod --name pgsql_db -e POSTGRESQL_USER=root -e POSTGRESQL_PASSWORD=SuperGeheimesPasswort -e POSTGRESQL_DATABASE=kanboard -v $pgsql_data:/var/lib/pgsql/data rhel8/postgresql-96
c242a4b9b57d53a822585c9eb83d081d5abbd40cb2b5952aee4457fee041e128

$ podman ps
CONTAINER ID  IMAGE                                          COMMAND         CREATED        STATUS            PORTS                 NAMES
6b065fe7ecc7  k8s.gcr.io/pause:3.2                                           2 minutes ago  Up 2 minutes ago  0.0.0.0:8080->80/tcp  d7afa6821382-infra
c242a4b9b57d  registry.redhat.io/rhel8/postgresql-96:latest  run-postgresql  3 seconds ago  Up 3 seconds ago  0.0.0.0:8080->80/tcp  pgsql_db
e62c7fa2ecf7  docker.io/kanboard/kanboard:latest                             2 minutes ago  Up 2 minutes ago  0.0.0.0:8080->80/tcp  kanboard

Nun läuft ein Pod mit drei Containern (Infra-, Kanboard- und Postgresql-Container). Rufe ich http://IP-DES-HOSTS:8080 in einem Webbrowser auf, begrüßt mich bereits die Kanboard-Anmeldemaske (Bild 1).

Bild 1: Kanboard-Anmeldemaske

Schritt 6: Nacharbeiten

Der Start meines Postgresql-Containers wollte anfangs nicht glücken, da das Verzeichnis /var/lib/pgsql/data/userdata nicht erstellt werden konnte. Abhilfe für das Problem findet sich in der Red Hat Wissensdatenbank unter: https://access.redhat.com/solutions/3508731 (Login required)

Zwar konnte ich mich bereits an der Kanboard-Anwendung anmelden, neue Nutzer erstellen und Profileinstellungen verwalten, doch beim Dateiupload klemmte es noch. Hier musste ich noch das Verzeichnis $kanboard_data/files mit Dateimode ‚0777‘ erstellen. Anschließend habe ich in der config.php-Datei des Kanboard-Containers den folgenen Standardwert, wie im Codeblock gezeigt angepasst:

// Folder for uploaded files (must be writeable by the web server user)
// Folgende Zeilen wurden auskommentiert
// define('FILES_DIR', DATA_DIR.DIRECTORY_SEPARATOR.'files');

// Folgender Eintrag wurde hinzugefuegt
define('FILES_DIR', 'data/files');

Abschließend habe ich den Kanboard-Container mittels podman restart kanboard neugestartet.

Fazit

Bei der Internet-Recherche nach guter Dokumentation und der Arbeit mit einigen Container-Registries erinnere ich mich an ein Zitat:

Das Internet ist ein großer Misthaufen, in dem man allerdings auch kleine Schätze und Perlen finden kann.

Joseph Weizenbaum, Vortrag in Hamburg am 2. Mai 2001, heise.de

Bisher wurden die Ziele 1-3 erreicht. Die dabei verwendeten Befehlszeilen besitzen eine beachtliche Länge. Hier bietet es sich an, die Befehle in kurze Shell-Wrapper zu verpacken.

Die Ziele 4 und 5 werde ich in einem Folgeartikel, an einem anderen Wochenende, in Angriff nehmen. Und auch der Frage, wie man diesen Verhau am besten aktualisiert und Updates implementiert, werde ich noch nachgehen.

Ihr habt bis hierhin durchgehalten? Dann danke ich euch für euer Interesse. Was haltet ihr von diesem Wochend-Projekt? Wieviel Sinn bzw. Unsinn steckt darin? Bitte lasst es mich in den Kommentaren oder auch gern per E-Mail wissen.

Quellen und weiterführende Links

  1. Building, running, and managing Linux containers on Red Hat Enterprise Linux 8
  2. Kanban Project Management Software — Kanboard
  3. Running Kanboard with Docker
  4. Kaboard Releases
  5. https://podman.io/getting-started/network
  6. Error „mkdir cannot create directory /var/lib/pgsql/data/userdata : Permission denied“ when deploying Postgresql with persistent storage on Openshift Container Platform 3
  7. Podman: Managing pods and containers in a local container runtime; Brent Baude; January 15, 2019

Das war das Jahr 2020 auf My-IT-Brain

Das Jahr 2020 war schon ein paar Wochen alt, als ich verkündete, was euch 2020 hier erwartet. Ich glaube, ich habe mich an den Ausblick gehalten.

Das Jahr fing ruhig an, bevor es unser aller Leben radikal veränderte. War mein Berufsleben2019 noch vom Pendeln zur Dienststelle und zurück geprägt, hat die Pandemie auch meinen Arbeitsplatz radikal verändert. Seit Mitte März arbeite ich konsequent im heimischen Arbeitszimmer. Seither habe ich screen in den Ruhestand entlassen und arbeite konsequent mit tmux und xpanes.

Ich habe lange über die Anschaffung eines elektrisch höhenverstellbaren Schreibtischs für mein Arbeitszimmer nachgedacht, bevor ich knapp 5 Monate später einen solchen mein Eigen nannte. Ich denke, dies war in diesem Jahr meine sinnvollste Anschaffung.

Auch die berufliche Kommunikation hat sich stark verändert; in meinen Augen jedoch weder zum Positiven noch zum Negativen. Bisher bin ich im Home-Office sehr zufrieden. Eine Rückkehr in die Dienststelle kann ich mir aktuell hingegen nicht vorstellen.

Mitte des Jahres durfte ich mit einem Team rund um Mohit Goyal (Senior Principal Product Manager, Red Hat) zusammen arbeiten und habe Red Hat Insights unter die Lupe genommen. Dabei herausgekommen ist unter anderem folgende Artikelserie:

  1. Einführung in Red Hat Insights
  2. Erkundung von Red Hat Insights — Advisor
  3. Schwachstellen-Management mit Red Hat Insights
  4. Red Hat Insights – Compliance
  5. Red Hat Insights – Patch and Drift
  6. Persönliche Bewertung von Red Hat Insights

In der Kategorie Ansible gab es hingegen nicht so viel Neues. Ich habe in diesem Jahr eher ein wenig Projektpflege beim Spiegelserver für arme Admins und dem Patchmanagement für RHEL betrieben.

Zusammen mit einem Kollegen habe ich noch ein Tutorial zur Nutzung des DNS-Alias-Modus mit dem acme.sh-Client geschrieben. Wir haben einiges an positiven Rückmeldungen dafür bekommen, was mich persönlich sehr gefreut hat.

Wie an den Artikeln zu erkennen ist, lag der Fokus in diesem Jahr auf Technologien und Produkten von Red Hat. Dies wird sich in 2021 vermutlich in Teilen fortsetzen. Vermutlich wird hier dann vermehrt etwas zu Linux-Containern zu lesen sein, mit denen ich mich etwas ausführlicher beschäftigen möchte.

Darüber hinaus plane ich die Einführung einer neuen Kategorie, deren Name noch nicht feststeht. In dieser möchte ich technische Sachverhalte möglichst einfach erklären, so dass auch Menschen ohne IT-Ausbildung verstehen können, wie das Internet und unsere digitale Welt funktionieren.

Ich wünsche euch allen einen guten Rutsch ins Jahr 2021!

Freie Software und Open Source…

… was ist das eigentlich? Und wie wirkt sich die Nutzung für den einzelnen Nutzer oder eine Organisation wie ein Unternehmen oder eine Behörde aus? Zu diesen Fragen mache ich mir in diesem Beitrag ein paar Gedanken, die ich gern mit euch diskutieren möchte.

Die Antwort auf die erste Frage fällt mir dabei noch leicht. Freie Software bzw. Open Source Software (FLOSS) sind Anwendungen, die unter einer freien bzw. freizügigen Lizenz stehen. Dabei orientiere ich mich an den Debian-Richtlinien für Freie Software (DFSG), welche u. a. bestimmen:

  1. Die Software darf uneingeschränkt weitergegeben oder verkauft werden.
  2. Der Quelltext der Software muss offen und für jeden frei zugänglich sein. Eine Weitergabe der Software muss sowohl als Quelltext als auch in kompilierter Form erlaubt sein.
  3. Es muss erlaubt sein, die Software zu untersuchen, zu ändern, zu erweitern und unter den gleichen Lizenzbedingungen wie die Original-Software weiterzugeben.
  4. Die Lizenz darf keine Person oder Gruppe von Personen diskriminieren.
  5. Die Lizenz darf keine Einschränkungen hinsichtlich des Einsatzbereichs vornehmen. Beispielsweise darf sie nicht verhindern, dass das Programm geschäftlich oder für genetische Forschungen verwendet wird.

Was habe ich als (privater) Nutzer davon?

Auch wenn es schön ist, den Quelltext bei Interesse studieren zu können, glaube ich persönlich nicht, dass viele Nutzer von dieser Möglichkeit Gebrauch machen. Und wenn doch, haben sie den Text vermutlich schnell wieder von ihrem Bildschirm verbannt.

Nun sind viele FLOSS-Anwendungen kostenlos erhältlich und nutzbar. Und obwohl ich die Geiz-ist-geil-Mentalität nicht mag, ist dies für den Anwender tatsächlich ein großer Vorteil.

Zu meinen Schul- und Ausbildungszeiten kostete professionelle und oftmals proprietäre Bürosoftware verdammt viel Geld. Teilweise waren dies mehrere hundert DM bzw. EUR. Und dafür durfte man die entsprechenden Anwendungen nur auf einem einzigen PC installieren. Nun hatte ich damals weder die Bereitschaft noch die Mittel, so viel Geld für ein Office-Paket aufzubringen, von dessen Funktionsumfang ich nur einen Bruchteil benötigte und nutzen würde.

Daher war ich hoch erfreut, dass es OpenOffice gab. Entstanden aus den offengelegten Quelltexten von StarOffice bot sich mir hiermit die Möglichkeit, meine Briefe, Aufsätze, Tabellen und Präsentationen zu gestalten, ohne mich dafür in Unkosten zu stürzen. Zugegeben sahen die Präsentationsvorlagen damals schon wie Tapeten aus den siebziger Jahren aus. Aber die proprietären Alternativen waren damals nicht viel besser.

Viele unter euch kennen sicherlich die Überraschungen, die man erleben kann, wenn man Text- und Tabellen-Dokumente zwischen freien und proprietären Office-Suiten austauscht. Aber glaubt mir, diese Problemchen sind nicht mit denen vergleichbar, als ich meinem Lehrer den Aufsatz, verfasst auf einem C64, auf einer 5,25-Zoll-Diskette überreicht habe. Zum Glück hatte ich noch die auf Endlospapier gedruckte Fassung dabei, erstellt auf einem 9-Düsen-Tintenstrahl-Drucker, welche mir die Note rettete.

Ein ärgerliches Problem jedoch bleibt. Es nützt dem Bürger nichts, wenn seine mit freier Software erstellten Dokumente von Behörden nicht angenommen bzw. verarbeitet werden können. Genauso doof ist die Situation anders herum. Wenn man von Behörden Dateien übermittelt bekommt, welche sich nur mit der proprietären Software anzeigen lassen, mit der sie erstellt wurden. Hier ist in den letzten zwanzig Jahren schon vieles besser und einfacher geworden. Und als Optimist glaube ich daran, so lange zu leben, dass ich noch erleben werde, dass es noch besser wird.

Habt ihr ähnliche Erfahrungen gemacht? Wie seht ihr die Situation heute?

Mit den Jahren hat sich die Situation bei der Bürosoftware geändert. So gab es zwischenzeitlich für private Nutzung und für Schüler/Studenten eine proprietäre Office-Suite für 99 EUR, welche gleichzeitig auf bis zu drei Geräten installiert und genutzt werden durfte. Hier stimmt für meinen Geschmack das Preis-Leistungsverhältnis. Nur war diese Software nicht für mein Betriebssystem erhältlich und kam somit nicht in Frage. Ich glaube jedoch bis heute, dass es entsprechende Angebote nicht gegeben hätte, ohne dass freie Alternativen verfügbar gewesen wären und es heute noch sind.

Ein weiteres Beispiel für FLOSS ist die in diesem Beitrag schon für einige Links verwendete Wikipedia. Früher hatte man vielleicht ein Lexikon oder den Brockhaus daheim. Wobei letzterer sogar eine echte Geldanlage war. Das Wissen in den Büchern verstaubte, wie die Bücher selbst auch. Heute haben dank Wikipedia sehr viele Menschen dieser Welt freien Zugang zu nahezu unbegrenztem Wissen. Ich finde dies großartig.

Ich schrieb eingangs, dass ich kein Freund der Geiz-ist-geil-Mentalität bin. Dies liegt in der Annahme begründet, dass gute Software nicht nur in der Freizeit von Entwicklern zwischen 22:00-23:50 Uhr entsteht. Wenn viele Entwickler gute Anwendungen programmieren, sollten sie dafür auch bezahlt werden. Doch scheint es wider der Natur des Menschen zu sein, für eine Leistung zu bezahlen, die er auch kostenlos erhalten kann. Dies missfällt mir und ich habe beschlossen, da nicht mitzumachen.

Ich selbst bin mittleren Alters, habe Familie, stehe mitten im Berufsleben und beziehe ein Einkommen, welches meiner Familie und mir ein gutes Auskommen ermöglicht. Und ich habe beschlossen, einen kleinen unbedeutenden Teil meines Einkommens für FLOSS-Projekte zu spenden.

Dabei überlege ich mir einmal im Jahr, welchen Betrag ich insgesamt spenden möchte und welche Anwendungen oder Projekte ich besonders häufig genutzt habe; bzw. welche Anwendungen/Projekte mir besonders wichtig waren. Anschließend entscheide ich, wie ich den von mir festgelegten Betrag aufteile und überweise die einzelnen Summen. Mir ist bewusst, dass der gespendete Betrag nichtmal einem Monatsgehalt eines professionellen Software-Entwicklers entspricht. Doch ich denke, Kleinvieh macht auch Mist und habe ein gutes Gefühl dabei.

Heute nutze ich fast ausschließlich freie Software. E-Mail-Client, Textverarbeitung, Editoren und Betriebssystem; alles FLOSS. Dabei bin ich der Nutzung proprietärer Software gar nicht abgeneigt. So würde ich auch heute noch zu proprietären Anwendungen für die Steuererklärung oder das Online-Banking greifen, bevor ich mich mit den freien Alternativen abquäle.

Zwar existieren einige liebgewonnene Anwendungen heute nicht mehr, weil die Hersteller sie abgekündigt oder zur Unbenutzbarkeit weiterentwickelt haben. Doch habe ich das gleiche auch schon mit FLOSS-Anwendungen durchgemacht.

Wie ist das bei euch? Verwendet ihr freie bzw. quell-offene Software in eurem Alltag? Wenn ja, in welchem Umfang? Und wie zufrieden seid ihr damit? In welchen Bereichen fehlt es eurer Meinung nach an freien Alternativen? Nutzt gern die Kommentarfunktion oder schreibt mir per E-Mail, wenn ihr mögt.

Was tun, wenn’s klemmt?

FLOSS und proprietäre Software haben gemein, dass sie fehlerbehaftet sind. Ohne eine Gewährleistungspflicht auf Software wird sich dieser Umstand auch nie ändern. Doch was kann man als Privatanwender tun, wenn eine Anwendung mal nicht so will, wie sie soll? Oder man einfach nicht weiß, wie man sein gewünschtes Ziel erreicht?

In meinen Augen gehört zu jeder Anwendung auch ein Handbuch, eine Anleitung und eine Befehlsreferenz als Dokumentation. Je nach Hersteller, Projekt bzw. Anwendung schwankt die Qualität von Dokumentation von „nicht vorhanden“ über „beschissen ist geprahlt“ bis „erfreulich gut“. Hier lohnt sich ein erster Blick. Kommt man mit der vorhandenen Dokumentation nicht weiter, findet man häufig Hilfe in den unzähligen Internetforen, wo freiwillige, engagierte Nutzer anderen Nutzern bei Sorgen, Nöten und Problemen weiterhelfen.

Um nicht ständig die gleichen Fragen aufs neue zu beantworten, wird lediglich verlangt, das verdammte Handbuch (RTFM) gelesen und die Suchfunktion verwendet zu haben, bevor man ein neues Thema eröffnet. Wer sich an diese einfachen, grundlegenden Regeln hält und darüber hinaus stets freundlich bleibt, dem wird mit großer Wahrscheinlichkeit geholfen.

Wer hingegen rüpelhaft, in rauhem Ton sofortige Unterstützung und Lösungen für ein Problem mit einer Anwendung einfordert, für die man nichtmal einen Cent zu spenden/zahlen bereit war, darf sich nicht wundern, am langen Arm zu verhungern. Und das ist in meinen Augen vollkommen in Ordnung.

Neben der Dokumentation und den Internetforen gibt es natürlich noch die technisch begabten Verwandten. Diese reisen meist an Wochenenden und hohen Feiertagen an, um die IT-Probleme ihrer Familie und Nachbarn zu fixen. Doch bitte nutzt die Hilfe dieser edlen Ritter ohne Rüstung nicht schamlos aus. Sie kommen euch unter Umständen viel häufiger besuchen, wenn sie für den Kaffee nicht drei Laptops und zwei Handys neuinstallieren müssen.

Damit sind die Möglichkeiten eigentlich auch ausgeschöpft. Kommerzielle und finanziell interessante Support-Angebote für Privatanwender existieren meines Wissens nach so gut wie nicht.

FLOSS lebt vom Mitmachen, nicht vom Meckern

Freie Software wird meist unentgeltlich zur Nutzung angeboten. Diese wird nicht selten von Freiwilligen in deren Freizeit geschaffen. Auch Unternehmen, welche der Gemeinschaft etwas zurückgeben möchten, beschäftigen Entwickler, die einen Teil ihrer Arbeitszeit an Open Source Software arbeiten können.

Fehler werden höchstwahrscheinlich nicht mit Absicht eingebaut. Und nicht jeder erdenkliche Anwendungsfall wird von Beginn an in der Entwicklung berücksichtigt. Darüber zu meckern und Forderungen für etwas zu stellen, was man kostenlos nutzen darf, hat bisher in den seltensten Fällen geholfen.

Hat man Wünsche den Funktionsumfang einer Anwendung betreffend, kann man diese an das jeweilige Projekt richten. Liest man zuvor die sog. Contribution guidelines (zu Deutsch in etwa: Beitragsleitlinie), erhöht dies die Chancen, dass ein Beitrag Berücksichtigung findet.

Unterstützung und Hilfe ist an allen Ecken und Enden des FLOSS-Universums von Nöten und oft herzlich willkommen. Dabei muss man kein Software-Entwickler sein. Denn oft mangelt es an Dingen, die mit dem Code nicht viel zu tun haben. So kann man zum Beispiel:

  • Dokumentationen schreiben, erweitern und verbessern
  • Dokumentationen in andere Sprachen übersetzen
  • Nutzern in Internetforen und auf Maillinglisten bei der Lösung ihrer Probleme helfen
  • Fehlerbilder verifizieren und Patches testen

FLOSS ist Software von der Gemeinschaft für die Gemeinschaft. Bring dich ein, mach mit!

Ein (paar) Wort(e) an Entwickler und Paket-Betreuer

Ihr habt zum Teil großartige Anwendungen geschaffen und stellt sie der Gemeinschaft zur Verfügung. Ihr seid auf Hilfe angewiesen und braucht/sucht Nachwuchs, der bereit ist, zu lernen, wie man Software erstellt, pflegt, pakettiert und verteilt? Dann denkt bitte daran, dass jeder mal klein anfängt und man dem Nachwuchs aufs Pferd helfen muss, bevor dieser losreiten kann.

Zum Teil habt ihr rund um eure Software Ökosysteme aus Versionskontrollsystemen, Build-Umgebungen, CI/CD und Kommunikationskanäle geschaffen, die für Anfänger und technisch interessierte Laien nur schwer zu durchdringen sind. Wer sich bei der Beantwortung der Frage, wie man ein Distributions-Paket betreuen kann, tagelang durch verschiedenste Wiki-Seiten und gefühlt das halbe Internet gewühlt hat, gibt danach oft frustriert auf.

Ich habe kein Patentrezept, wie man es optimal gestalten kann. Doch klafft IMHO zwischen Tutorials wie „Wie baut man ein {DEB,RPM}-Paket“ und „So baut und betreut man Pakete für Distribution XY“ eine große Lücke, durch welche potenzieller Nachwuchs durchfällt.

Hier ist eventuell eine Diskussion innerhalb der einzelnen Communities notwendig, wie der Prozess der Nachwuchsgewinnung verbessert werden kann.

Oder habe ich hier ein falsches Bild von der FLOSS-Welt und es gibt kein Nachwuchsproblem, weil man sich vor neuen Paketbetreuern kaum retten kann?

Was haben Unternehmen und Behörden von FLOSS?

TL;DR: Mehr Souveränität. Keine starke Abhängigkeit von einem einzelnen Anbieter. Und Freiheit.

Ich habe in vorstehendem Absatz ganz bewusst auf Begriffe wie „kostenlos“, „unentgeltlich“ und „Kostenreduzierung“ verzichtet. In meinen Augen greift die Reduzierung von FLOSS auf vermeintliche Kostenvorteile zu kurz und ist nicht selten mit ein Grund für das Scheitern von Migrationsprojekten hin zu FLOSS. Statt dessen möchte ich in diesem Beitrag Aspekte hervorheben, die IMHO häufig zu kurz kommen.

Dazu beginne ich mit einem Beispiel aus der Closed Source Welt. Es wird ein Produkt wie zum Beispiel ein Betriebssystem oder eine Anwendung eines proprietären Herstellers erworben und in die eigenen Geschäftsprozesse integriert. Nicht selten zahlt man einmal für die Lizenz, um das Produkt überhaupt nutzen zu dürfen und darüber hinaus für ein Abonnement, über welches man Updates, Sicherheits-Patches und Unterstützung durch den Hersteller-Support bekommt. Der Hersteller kann beliebig darüber entscheiden, wie lange er ein Produkt unterstützt und wann er es abkündigt, so dass der Kunde ggf. ein Nachfolgeprodukt erneut kaufen muss. Wenn es ganz dumm läuft, stellt der Anbieter ein Produkt komplett ein, ohne dass es ein Nachfolgeprodukt gibt. Als Kunde guckt man dann halt in die Röhre und kann sich erneut auf die Suche nach einem Produkt machen, das man ggf. unter Anpassung der eigenen Prozesse integriert. Damit einher geht häufig die Anpassung weiterer Systeme und Prozesse, sowie der Austausch von Client-Anwendungen und Anwenderschulungen.

Die schlechte Nachricht ist, dies alles kann beim Einsatz von FLOSS ebenfalls passieren. Doch gibt es bei FLOSS noch eine weitere Option, die sich als vorteilhaft erweisen kann. Auch dazu möchte ich euch ein Beispiel geben.

Angenommen es wird eine Software genutzt, die ein engagierter FLOSS-Entwickler als Hobby-Projekt in seiner Freizeit erstellt hat. Die Software besitzt ausschließlich Abhängigkeiten zu anderen FLOSS-Technologien und deckt alle Anforderung des Unternehmens bzw. der Behörde ab. Die Nutzung ist unbeschränkt und kostenlos möglich. Mittlerweile ist die Anwendung tief in die eigenen Prozesse integriert und elementarer Bestandteil der Wertschöpfungskette. Alle sind glücklich und alle sind froh.

Doch dann endet eines Jahres die Unterstützung für eine FLOSS-Technologie von der diese Anwendung abhängt. Es gibt ein Major-Release-Upgrade für diese Technologie. Die FLOSS-Anwendung muss jedoch angepasst werden, um weiterhin lauffähig zu sein.

Nun kann man den bzw. die Entwickler der Anwendung ganz lieb fragen, ob sie die notwendigen Anpassungen vornehmen mögen. Vielleicht hat man Glück und dies geschieht innerhalb weniger Tage. Vielleicht hat man auch Pech und sie haben einfach keine Lust.

Wenn es an der Motivation fehlt, kann man auf die verrückte Idee kommen und den Entwicklern anbieten, sie für die notwendigen Anpassungen zu bezahlen und einen Preis mit ihnen aushandeln. Für mich liegt dieser Gedanke nahe, würde man einen proprietären Hersteller doch auch bezahlen. Und das häufig sogar für Änderungen, die man gar nicht wollte/brauchte.

Nun kann es durchaus immer noch passieren, dass der/die Entwickler das Angebot ablehnen. Sie haben einfach keine Lust, sich weiterhin um ihre alte Anwendung zu kümmern. Was bleibt nun übrig, außer eine Markterkundung durchzuführen, eine Alternative zu eruieren und Himmel und Hölle in Bewegung zu setzen, um diese zu implementieren?

Halt! Stopp! Es gibt noch eine weitere Alternative. Die Anwendung ist quell-offen und der Quelltext liegt euch vor. Die Anwendung kann jederzeit aus diesem neu erstellt werden und ihr habt das Recht, beliebige Anpassungen am Quelltext vorzunehmen. Wenn euch die Anwendung wichtig genug ist, hindert euch nichts und niemand daran, eigene Entwickler einzustellen, welche den Quelltext studieren und notwendige Anpassungen vornehmen. Und da ihr diese Entwickler selbst bezahlt, könnt ihr sie auch mit Priorität an euren Wunsch-Funktionen arbeiten lassen.

Jetzt wurde auch schon deutlich, warum ich das Argument, FLOSS sei kostenlos bzw. günstig, doof finde. Es trifft nicht zu. Spätestens wenn ich eigene Entwickler beschäftige und hoffentlich auch bezahle, kostet dies ebenfalls Geld; nur investiert man das Geld hierbei in eigene Ressourcen. Ähnlich ist es, wenn man sich Funktionen im Auftrag entwickeln lässt. Nur behält man hierbei die Souveränität über die Software, im Gegensatz zum Produkt eines proprietären Anbieters.

Selbstverständlich mag dies nicht in jedem Fall möglich sein. Doch in vielen Fällen ist dies ein gangbarer Weg und einer der großen Vorteile des FLOSS-Entwicklungsmodells. Ein weiterer Vorteil besteht darin, dass man nicht die gesamte Entwicklungsarbeit allein bewältigen muss. Die Last kann auf viele Schultern weltweit verteilt werden. So arbeiten Entwickler aus verschiedensten Branchen mit am Linux-Kernel. Gleiches gilt für den BSD-Kern und unzählige andere Projekte.

Wer hilft wenn’s klemmt?

Grundsätzlich stehen die gleichen Optionen zur Verfügung, die auch Privatnutzern offen stehen. Darüber hinaus bietet sich häufig die Möglichkeit, Support-Verträge mit Herstellern oder Systemhäusern abzuschließen.

So bieten z.B. Red Hat, SUSE, Canonical und Oracle verschiedene Support-Optionen für das jeweilige Portfolio an. Darüber hinaus haben sich auch im deutschsprachigen Raum einige Firmen etabliert, welche Support-Dienstleistungen für vielfältige FLOSS-Projekte/Produkte anbieten.

Diese Firmen verdienen nicht nur Geld mit Dienstleistungen rund um FLOSS. Sie beteiligen sich häufig mit eigenem Personal und/oder finanziell an der Weiterentwicklung diverser Projekte.

Die Qualität des Supports ist meiner Erfahrung nach mit dem proprietärer Anbieter vergleichbar. Das gilt sowohl im positiven wie negativen Sinne.

Nutzt einfach die Suchmaschine eures geringsten Misstrauens und ihr werdet bestimmt einen passenden Dienstleister finden.

Auch hier gilt, nicht meckern, mitmachen!

Ich möchte mich wiederholen: „FLOSS ist Software von der Gemeinschaft für die Gemeinschaft. Bring dich ein, mach mit!“

Dies sollte in meinen Augen besonders für Behörden und Organisationen gelten, die den Betrieb und die Entwicklung ihrer Anwendungen mit dem Steuergeld von Bürgerinnen und Bürgern finanzieren. Deshalb unterstütze ich die Kampagne „Public Money, Public Code“. Innovationen und Investitionen in Freie Software verschwinden nicht hinter verschlossenen Türen zum Nutzen Weniger; statt dessen können alle Nutzer davon profitieren. So z.B. auch Bürgerinnen und Bürger, die daheim evtl. die gleichen FLOSS-Anwendungen nutzen, die auch der Staat nutzt und mit weiterentwickelt.

Bisher ist vieles davon noch bloße Utopie. Scheitert es doch im öffentlichen Dienst schon oft genug daran, an Open Source Projekte zu spenden. Geld für Berater-Verträge auszugeben ist da schon einfacher möglich. Doch auch auf diesem Weg kann man ja FLOSS-Projekte unterstützen. Ich glaube da wo ein Wille ist, ist auch ein Weg.

Schlussworte

Freie Software und Open Source Software sind frei im Sinne von:

  • Der Quelltext liegt offen vor und kann von jedem Menschen eingesehen, studiert und weitergegeben werden.
  • Jeder Mensch hat das Recht den Quelltext zu verändern.
  • Die Verwendung der Software ist in keiner Weise beschränkt.

Wer einfach nur seine Arbeit erledigen möchte, mag dabei mit FLOSS-Software genau so viel Glück oder Pech wie mit proprietärer Software haben. FLOSS bietet hingegen Souveränität und Freiheit; mit allen Vor- und Nachteilen, die das mit sich bringen mag. Technisch interessierte Menschen können sich mit ihr vertraut machen, dazulernen und Teil einer Gemeinschaft werden.

Ich mag FLOSS und glaube Open Source Entwicklungsmodelle sind auch in Zukunft nicht mehr aus unserer Welt wegzudenken.

My 2 Cents on CentOS

In den letzten Tagen dreht sich meine Internet-Blase zu einem großen Teil rund um das nahende Ende von CentOS-Linux. Um nicht in mehreren Blogs die gleichen oder ähnliche Kommentare zu hinterlassen, habe ich mich entschieden, in diesem einen Artikel meinen Senf dazu zugeben.

Aus Transparenzgründen weise ich ausdrücklich darauf hin, dass ich Mitglied der Red Hat Accelerators bin (siehe „Zu meiner Person„). Meine Meinung ist jedoch grundsätzlich meine eigene. Diese kann mit den Ansichten von Red Hat übereinstimmen, muss es aber nicht und tut es auch nicht immer.

Die Meldungen, dass das Ende von CentOS Linux naht, haben mich weder überrascht, noch enttäuscht. Überrascht bin ich nicht, da ich mich schon bei der Ankündigung von CentOS Stream gefragt habe, wie lange beide Projekte parallel existieren können bzw. wann Red Hat die Unterstützung des einen Projekts zu Gunsten des anderen beendet. Und um enttäuscht zu sein, habe ich CentOS nicht lange genug verwendet und mich nicht in der Gemeinschaft organisiert. Freuen tut es mich allerdings auch nicht, wenn ein so robustes Projekt ein Ende hat.

Ziemlich unglücklich fand ich die Kommunikation durch Red Hat. Dabei ist denke ich mehr Porzellan kaputt gegangen, als nötig war. So ist es wenig verwunderlich, dass die CentOS-Community aufgebracht ist und sich in den Kommentarspalten in Rage schreibt.

So ist die Rede davon, dass Red Hat auf Weisung von IBM bei CentOS das Licht ausgemacht hat; dass CentOS auf dem Altar der Kommerzialisierung geopfert wird, um die Profite von Red Hat und IBM zu steigern. Ja, man könnte fast meinen, die Welt geht unter. Ich finde, das ist alles Quatsch.

Zumal ich nicht glaube, dass Nutzer des kostenlosen CentOS jetzt alle RHEL-Subkriptionen kaufen werden. Für viel wahrscheinlicher halte ich es, dass sich diese Nutzer den zukünftigen kostenlosen RHEL-Klonen zuwenden oder zu anderen kostenlosen Linux-Distributionen wechseln werden.

In meiner Wahrnehmung hat Red Hat bisher seine Eigenständigkeit nach dem Kauf durch IBM behalten. Ob IBM etwas mit der Entscheidung zu tun hatte, weiß ich nicht. Glauben tue ich es jedenfalls nicht. Letztendlich hat das CentOS-Projekt im hauseigenen Blog bekannt gegeben, den Fokus auf CentOS Stream zu verlagern. Übrigens sind nur 4 von 10 Sitzen im CentOS Governing Board von Red Hattern besetzt.

Etliche Kommentare hinterlassen bei mir den Eindruck, die dazugehörigen Autoren würden glauben, sie hätten ein Recht auf kostenlose Software der Enterprise-Klasse. Dabei beinhaltet Open Source nach meinem Verständnis die Freiheit der Quelltexte und das Recht, aus diesen ausführbare Programme zu erstellen. Eine Pflicht, dass ein Unternehmen für den ganzen Spaß bezahlt und diese Aufgabe für mich übernimmt, kann ich hingegen nicht daraus ableiten. Von daher kann ich die Empörung nur teilweise nachvollziehen.

Dabei ist die Binärkompatibilität von CentOS, Oracle Linux, CloudLinux OS, Centos Stream und evtl. bald Rocky Linux zu RHEL doch auch ein Vorteil. Sollten sich Anwendungen, die auf einer dieser Distributionen laufen, sich doch selbst im Binärformat ohne großen Aufwand auf die jeweils anderen übertragen lassen. Ganz ehrlich, einen Wechsel von Debian zu OpenSUSE stelle ich mich schwieriger vor.

In meinen Augen stehen ausreichend gute und stabile Alternativen für Menschen zur Verfügung, die es sich nicht leisten können, für Enterprise-Software zu bezahlen.

Also CentOS, mach es gut. Es war schön mit dir. Mit deinen Nachfolgern wird es sicherlich auch wieder viel zu erleben geben.

Links und Quellen zum Thema

SSH ForwardAgent ja oder nein?

In diesem Artikel möchte ich diskutieren, ob die Nutzung der SSH-Client-Option ForwardAgent sicher und sinnvoll ist. Wie so häufig bei Themen der IT-Sicherheit geht es auch hier um die Abwägung zwischen Sicherheit und Bequemlichkeit.

Der SSH-Agent nimmt den privaten SSH-Schlüssel auf und stellt diesen für SSH-Verbindungen bereit, so dass nicht bei jeder neuen SSH-Verbindung die Passphrase eingegeben werden muss.

Dabei wird ein UNIX-Socket erstellt und in der Variablen SSH_AUTH_SOCK gespeichert. Der folgende Code zeigt dies beispielhaft:

$ echo $SSH_AUTH_SOCK
/run/user/1000/keyring/ssh

In der ssh_config(5) findet sich die Option ForwardAgent, mit deren Hilfe der SSH-Agent auf einen entfernten Rechner weitergeleitet werden kann. Ist diese Funktionalität aktiv, kann man sich mit dem im SSH-Agenten gespeicherten privaten SSH-Schlüssel zu einem entfernten Rechner verbinden und von dort aus unter Nutzung des gleichen Schlüssels Verbindungen zu weiteren Rechnern aufbauen.

In den meisten Linux-Distributionen ist diese Option standardmäßig deaktiviert, da sie ein potenzielles Sicherheitsrisiko darstellt. Gelingt es einem Benutzer, die Dateiberechtigungen auf dem entfernten Rechner zu umgehen, kann er den lokalen Agenten benutzen, um Operationen durchzuführen, die nur mit dem im SSH-Agenten gespeicherten SSH-Schlüssel möglich sind. Ich möchte dies im Folgenden an einem Beispiel veranschaulichen.

Die Umgebung

Für den Versuch kommen die drei Linux-Rechner host-a, host-b und host-c zum Einsatz. Auf allen drei Hosts existiert der User foo, welcher mittels sudo zum root werden kann. Darüber hinaus existiert auf host-b User bar, welcher ebenfalls mittels sudo zum root werden darf.

Gezeigt wird, wie bar durch Wechsel in den Kontext des Users root die Dateiberechtigungen für den Unix-Socket des SSH-Agenten von foo umgehen kann, um mit dessen Informationen eine SSH-Verbindung zu host-c herzustellen, was ihm sonst nicht gestattet ist.

Der Versuchsablauf

In diesem Abschnitt wird der Ablauf wiedergegeben, der dazu führt, dass bar Zugriff als foo auf host-c bekommt.

host-a

Auf host-a existiert ein Unix-Socket für den SSH-Agenten. Der User foo nutzt diesen, um eine Verbindung zu host-b aufzubauen. Dabei wird die Option ForwardAgent aktiviert:

foo@host-a:~$ echo $SSH_AUTH_SOCK
/run/user/1000/keyring/ssh
foo@host-a:~$ ssh -o ForwardAgent=yes  host-b
[...]
foo@host-b:~$

host-b

Wir sind jetzt via SSH als User foo auf dem host-b eingeloggt. Da wir die Weiterleitung des SSH-Agenten aktiviert haben, existiert jetzt auch hier ein entsprechender Unix-Socket. Die Dateiberechtigungen sind so gesetzt, dass nur foo darauf zugreifen darf. Der folgende Codeblock veranschaulicht dies.

foo@host-b:~$ echo $SSH_AUTH_SOCK
/tmp/ssh-fxwQXNlZrS/agent.32579
foo@host-b:~$ ls -ld /tmp/ssh-fxwQXNlZrS
drwx------ 2 foo foo 4096 Nov 24 14:47 /tmp/ssh-fxwQXNlZrS
foo@host-b:~$ ls -l /tmp/ssh-fxwQXNlZrS/agent.32579
srwxr-xr-x 1 foo foo 0 Nov 24 14:47 /tmp/ssh-fxwQXNlZrS/agent.32579

Neben foo ist auch User bar auf host-b eingeloggt. Die Variable SSH_AUTH_SOCK dieses Users ist leer und bar wird beim Versuch, sich mit host-c zu verbinden, zur Eingabe eines Passworts aufgefordert.

bar@host-b:~$ echo $SSH_AUTH_SOCK

bar@host-b:~$ ssh foo@host-c
foo@host-c's password:

bar@host-b:~$ ls -l /tmp
drwx------ 2 foo   foo   4096 Nov 24 14:56 ssh-fxwQXNlZrS
bar@host-b:~$ ls -l /tmp/ssh-fxwQXNlZrS
ls: cannot open directory '/tmp/ssh-fxwQXNlZrS/': Permission denied

Da bar das Kennwort von foo unbekannt ist, geht es auf diesem Weg nicht weiter. Jedoch kann bar erkennen, dass auf dem System ein Unix-Socket mit einem SSH-Agenten für foo existiert. Der nun folgende Codeblock zeigt, wie bar über einen Umweg den SSH-Agenten von foo nutzt, um sich mit host-c zu verbinden.

bar@host-b:~$ sudo -i
[sudo] password for bar:
root@host-b:~# ssh foo@host-c
foo@host-c's password:

root@host-b:~# SSH_AUTH_SOCK=/tmp/ssh-fxwQXNlZrS/agent.32579
root@host-b:~# export SSH_AUTH_SOCK
root@host-b:~# ssh foo@host-c
[...]
foo@host-c:~$

Der User bar hat es geschafft, sich als foo an host-c zu authentifizieren. Das ist foobar!

Schlussfolgerung

Der hier durchgeführte Versuch zeigt, dass die Option ForwardAgent ein Sicherheitsrisiko birgt, wenn es auf einem entfernten System Benutzer gibt, welche die Dateiberechtigungen, wie in diesem Artikel gezeigt, umgehen können.

Ich empfinde es daher als gut und sinnvoll, dass diese Option standardmäßig deaktiviert ist.

Kann man jedoch ausschließen, dass die Dateiberechtigungen umgangen werden, z. B. weil keine weiteren Nutzer auf dem entfernten Rechner existieren bzw. auf diesen zugreifen können, spricht in meinen Augen nichts dagegen, diese Option zu nutzen und sich den Alltag etwas komfortabler zu gestalten.

Let’s Encrypt: Nutzung des DNS-Alias-Modus mit dem acme.sh-Client

Dieses Tutorial erklärt, wie der Let’s Encrypt Client (LE-Client) acme.sh mit dem Plugin dns_nsupdate auf einem Linux-System installiert und zur Nutzung der „DNS-01 challenge“ im DNS-Alias-Modus konfiguriert werden kann.

Um dem Tutorial folgen zu können, sollte man den grundlegenden Umgang mit einem Terminal und einer weitgehend POSIX-kompatiblen Shell beherrschen. Grundlegende Kenntnisse der Funktion von DNS und Resource Records sind hilfreich, jedoch nicht erforderlich und können im Zweifel unter den verlinkten Seiten erworben werden.

In diesem Tutorial werden nicht die Vor- und Nachteile der „DNS-01 challenge“ diskutiert. Diese können unter vorstehendem Link nachgelesen werden.

Die Begriffe SSL-Zertifikat, TLS-Zertifikat oder nur Zertifikat werden in diesem Tutorial synonym verwendet. Der zum Zertifikat gehörende Schlüssel wird als Private-Key bezeichnet.

Umfeld

Im Folgenden werden die Begriffe Subdomain und Zone synonym verwendet. Es sei darauf hingewiesen, dass diese Begriffe in einem anderen Kontext eine unterschiedliche Bedeutung haben können.

Als Beispiel-Domain wird example.org mit den beiden Subdomains foo.example.org und bar.example.org verwendet. Um dem acme.sh-Client keinen direkten Zugriff auf diese DNS-Zonen geben zu müssen, hat der DNS-Anbieter die DNS-Zone acme.foo.example.org definiert, welche für den Zweck der DNS-Challenge verwendet wird.

Der Nameserver für alle in diesem Tutorial verwendeten Domains lautet ns.example.org.

Vom DNS-Anbieter wird ein sogenannter TSIG-Schlüssel bereitgestellt, welcher berechtigt ist TXT-Ressource-Records in der oben genannten DNS-Zone acme.foo.example.org anlegen zu können. In diesem Tutorial werden folgender Name und Wert für einen TSIG-Schlüssel angenommen:

  • TSIG-NAME: acme-key
  • TSIG-WERT: SuperGeheim1111elf==
  • TSIG-Algorithmus: hmac-sha512

Das Ziel soll nun sein, ein Zertifikat ausgestellt zu bekommen, welches für die beiden folgenden Hostnamen gültig ist:

  • host.foo.example.org
  • mein-schoener-host.bar.example.org

In diesem Tutorial wird als Webserver Apache in der Distribution RHEL 7 verwendet. Die entsprechende Service-Unit lautet demnach httpd.service. Wird ein anderer Webserver oder eine andere Distribution verwendet, ist der Name der Service-Unit entsprechend anzupassen (z.B. nginx.service).

Sinn und Zweck des DNS-Alias-Modus

Der DNS-Alias-Modus kann verwendet werden, wenn man einem Programm wie dem acme.sh-Client keinen direkten API-Zugriff auf die DNS-Zonen der eigenen Domain geben möchte. In diesem Abschnitt werde ich beispielhaft erklären, warum eine Nutzung dieses Modus angeraten erscheint.

Bevor Let’s Encrypt ein Zertifikat ausstellt, muss der anfragende Client beweisen, dass er die administrative Kontrolle über die Domain hat. Im Falle der DNS-Challenge geschieht dies indem ein TXT-Record in der jeweiligen DNS-Zone erstellt wird. Der LE-Client benötigt somit Zugriff mit Schreibberechtigung auf die entsprechenden DNS-Zonen. Dies kann jedoch verheerende Folgen haben, wenn der Host mit dem LE-Client kompromittiert wird. Ein Angreifer könnte hierdurch API-Zugang erlangen und die Informationen einer (oder mehrere) DNS-Zonen löschen. Oder noch schlimmer, die Einträge manipulieren bzw. eigene Einträge hinzufügen.

In unserer Organisation hat daher nur autorisiertes Personal mit personalisierten Zugangsdaten Zugriff auf ausgewählte Zonen.

Eine wichtige Eigenschaft von personalisierten Zugangsdaten ist, dass diese nur von der Person verwendet werden, an die sie ausgehändigt wurden. Diese sollten tunlichst nicht in einen LE-Client eingetragen werden, den man anschließend für Monate oder Jahre sich selbst überlässt.

Der DNS-Alias-Modus funktioniert so, dass der DNS-Anbieter eine Zone einrichtet, in der dynamische Updates durch LE-Clients gestattet sind. Die Authentifizierung und Autorisierung kann mittels sogenannter TSIG-Schlüssel durchgeführt werden.

Als Sysadmin erstellt man nun einen CNAME-Eintrag in der eigentlichen DNS-Zone, z.B. foo.example.org welcher auf die Zone acme.foo.example.org zeigt. Der LE-Client legt während der DNS-Alias-Challenge eine TXT-Record in der Zone acme.foo.example.org an. Let’s Encrypt kann nun über den CNAME den erstellten TXT-Record validieren. Anschließend wird das Zertifikat an den LE-Client ausgestellt.

Wird der LE-Client kompromittiert, sind damit zwar auch die auf ihm gespeicherten Private-Keys kompromittiert (was schon schlimm genug ist), es kann jedoch darüber hinaus nur die sogenannte DNS-Alias-Zone manipuliert werden. Eine Manipulation der eigentlichen Zone bleibt ausgeschlossen.

Installation des acme.sh-Clients

Um ein neu ausgestelltes bzw. erneuertes Zertifikat und den dazugehörigen Private-Key in einem Webserver zu verwenden, muss die Webserver-Konfiguration neu geladen werden. Dazu sind meist Root/sudoer-Rechte erforderlich. In diesem Tutorial wird der acme.sh-Client unter einem User mit sudo-Berechtigung installiert.

Hinweis: Das Projekt empfiehlt die Nutzung von sudo ausdrücklich nicht. Weitere Informationen finden sich unter dem Link: https://github.com/acmesh-official/acme.sh/wiki/sudo

Da ich glaube, zu wissen, was ich tue, werde ich hier optional die Einrichtung unter einem User mit sudo-Berechtigung dokumentieren. Befehle sind entsprechend mit oder ohne sudo auszuführen, je nachdem welcher User verwendet wird.

Zur Installation führt man folgende Kommandos aus (Quelle):

git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install

Durch die Installationsroutine werden folgende drei Aktionen durchgeführt:

  1. Erstellen und Kopieren von acme.sh in das HOME-Verzeichnis ($HOME): ~/.acme.sh/. Unterhalb dieses Verzeichnisses werden die ausgestellten Zertifikate abgelegt.
  2. Erstellen eines Alias für: acme.sh=~/.acme.sh/acme.sh.
  3. Erstellen eines täglichen cron job, welcher die Restlaufzeit der Zertifikate prüft und diese ggf. verlängert.

Beispiel eines Cron-Jobs:

1 0 * * * "/home/alice/.acme.sh"/acme.sh --cron --home "/home/alice/.acme.sh" > /dev/null

Nach der Installation meldet man sich am besten einmal neu an, damit der Alias für acme.sh genutzt werden kann.

Anlegen der CNAME-Records

Nun legen wir die CNAME-Records an, welche für den DNS-Alias-Mode benötigt werden (Quelle).

_acme-challenge.host.foo.example.org IN CNAME acme.foo.example.org
_acme-challenge.mein-schoener-host.bar.example.org IN CNAME acme.foo.example.org

Der vom DNS-Anbieter bereitgestellte TSIG-Schlüssel ist ausschließlich zum Erstellen von Records in der Zone acme.foo.example.org berechtigt. Mit dem CNAME-Record weisen wir quasi nach, dass wir die Kontrolle über die DNS-Zone haben und dort Ressource-Records erstellen dürfen, während der acme.sh-Client nur berechtigt ist Einträge in jenen Zonen zu erstellen, auf welche die CNAME-Records verweisen.

Dies geschieht aus Sicherheitsaspekten. Auf diese Art und Weise wird verhindert, dass ein kompromittierter Host mit acme.sh-Client die wichtigen Zonen überschreiben kann.

Beantragung, Ausstellen und Installation eines Zertifikats

Die Überschrift deutet bereits an, dass der folgende Prozess aus drei Schritten besteht. Ich beginne mit einem optionalen Schritt, in dem ich die notwendige Konfiguration für einen sudo-User erkläre.

Optional: Konfiguration für non-root-User

Wer den acme.sh-Client unter dem User root installiert hat, kann diesen Abschnitt überspringen. An dieser Stelle wird die Konfiguration eines sudo-Users beschrieben.

Verwendet wird dabei ein Useraccount, der bereits über sudo-Berechtigungen verfügt, sich bei der Verwendung von sudo jedoch mit einem Passwort authentisieren muss. Im Folgenden nennen wir diesen Useraccount Alice.

Anpassung der sudoers-Datei

Da für die Installation des Let’s Encrypt Zertifikats in einem Webserver ein Reload der Konfiguration des Webservers erforderlich ist und dies später automatisch (ohne Passworteingabe) geschehen soll, ist eine Anpassung der sudoers-Datei von Alice erforderlich.

Die sudoers-Datei von Alice (/etc/sudoers.d/alice) sieht zu Beginn wie folgt aus:

alice  ALL=(ALL) ALL

Um Alice nun das Recht zu geben, die Webserver-Konfiguration ohne Passworteingabe neu zu laden, wird folgende Zeile hinzugefügt. Dabei ist die Reihenfolge der Zeilen zu beachten (siehe sudoers(5)). Die Bearbeitung der Datei wird mit dem Kommando sudo visudo -f /etc/sudoers.d/alice durchgeführt.

alice  ALL=(ALL) ALL
alice  ALL=(ALL) NOPASSWD: /bin/systemctl reload httpd.service

Verzeichnis für TLS-Zertifikate und Private-Keys erstellen

Zur weiteren Nutzung durch den Webserver, sollen die Zertifikate und Private-Keys in einem Verzeichnis gespeichert werden, auf das Alice Schreibrechte hat. Der Verzeichnisname kann dabei abweichend vom folgenden Beispiel frei gewählt werden. Im Beispiel wird die Gruppe apache verwendet. Diese ist ggf. an das eigene System anzupassen:

sudo mkdir -m 750 /etc/letsencrypt
sudo chown alice.apache /etc/letsencrypt

Die folgenden Schritte sind für root und alice gleich. Nur muss alice einige Befehle halt mit sudo auführen. Diese sind entsprechend gekennzeichnet. root lässt ein führendes sudo einfach weg.

Zertifikat beantragen und austellen

Wie eingangs erwähnt, wird ein TSIG-Schlüssel und das Plugin dns_nsupdate genutzt, um dynamische Zonen-Updates auf dem Nameserver durchzuführen. Um den acme.sh-Client entsprechend zu konfigurieren, wird zuerst eine Key-Datei erstellt, welche den TSIG-Schlüssel in JSON-Notation enthält (vgl. Abschnitt Umfeld).

Wichtig: Der TSIG-Schlüssel erlaubt dynamische Zonen-Updates im DNS. Er ist geheim zu halten und bestmöglich zu schützen.

Der Name der Datei ist dabei frei wählbar. Ich empfehle die Datei im HOME-Verzeichnis des Users abzulegen, welcher den acme.sh-Client ausführt und die Datei nur für diesen User lesbar zu machen (chmod 0400 DATEINAME). Beispiel:

cat ~/.nsupdate.key
key "acme-key" {
  algorithm hmac-sha512;
  secret "SuperGeheim1111elf==";
};

chmod 0400 ~/.nsupdate.key

Als nächstes werden notwendige Informationen über den Nameserver und unseren TSIG-Key verfügbar gemacht. Die folgenden Kommandos sind einmalig auszuführen. Die Werte werden vom acme.sh-Client später automatisch in ~/.acme.sh/account.conf gespeichert (Quelle).

export NSUPDATE_SERVER="ns.example.org"
export NSUPDATE_KEY="/home/alice/.nsupdate.key"

Nun ist es endlich soweit, dass ein Zertifikat ausgestellt werden kann. Für die in diesem Tutorial verwendeten Werte lautet der Befehl dazu wie folgt:

acme.sh --issue --dns dns_nsupdate -d host.foo.example.org -d mein-schoener-host.bar.example.org --challenge-alias acme.foo.example.org

Der folgende Code-Block zeigt ein konkretes Beispiel unter Verwendung der Staging-Umgebung.

acme.sh --issue --staging --dns dns_nsupdate -d host.foo.example.org -d mein-schoener-host.bar.example.org --challenge-alias acme.foo.example.org

[Fr 4. Sep 09:52:41 CEST 2020] Using ACME_DIRECTORY: https://acme-staging-v02.api.letsencrypt.org/directory
[Fr 4. Sep 09:52:42 CEST 2020] Using CA: https://acme-staging-v02.api.letsencrypt.org/directory
[Fr 4. Sep 09:52:42 CEST 2020] Create account key ok.
[Fr 4. Sep 09:52:42 CEST 2020] Registering account: https://acme-staging-v02.api.letsencrypt.org/directory
[Fr 4. Sep 09:52:43 CEST 2020] Registered
[...]
[Fr 4. Sep 09:52:43 CEST 2020] Creating domain key
[Fr 4. Sep 09:52:44 CEST 2020] The domain key is here: /home/alice/.acme.sh/host.foo.example.org/host.foo.example.org
.key
[Fr 4. Sep 09:52:44 CEST 2020] Multi domain='DNS:host.foo.example.org,DNS:mein-schoener-host.bar.example.org'
[Fr 4. Sep 09:52:44 CEST 2020] Getting domain auth token for each domain
[Fr 4. Sep 09:52:46 CEST 2020] Getting webroot for domain='host.foo.example.org'
[Fr 4. Sep 09:52:46 CEST 2020] Getting webroot for domain='mein-schoener-host.bar.example.org'                                             
[Fr 4. Sep 09:52:46 CEST 2020] Adding txt value: 2tG2NCvV23KGUNCSGeVO3P_UVaOVAG4t0ehbv1cJbtY for domain:  _acme-challenge.acme.foo.example.org
[Fr 4. Sep 09:52:46 CEST 2020] adding _acme-challenge.acme.foo.example.org. 60 in txt "2tG2NCvV23KGUNCSGeVO3P_UVaOVAG4t0ehbv1cJbtY"
[Fr 4. Sep 09:52:46 CEST 2020] The txt record is added: Success.
[Fr 4. Sep 09:52:46 CEST 2020] Adding txt value: yFbL8EF6xQre3v3RfYiCYQ8X4KH0gykagD9_oRfIvgA for domain:  _acme-challenge.acme.foo.example.org
[Fr 4. Sep 09:52:46 CEST 2020] adding _acme-challenge.acme.foo.example.org. 60 in txt "yFbL8EF6xQre3v3RfYiCYQ8X4KH0gykagD9_oRfIvgA"
[Fr 4. Sep 09:52:46 CEST 2020] The txt record is added: Success.
[Fr 4. Sep 09:52:46 CEST 2020] Let's check each DNS record now. Sleep 20 seconds first.
[Fr 4. Sep 09:53:07 CEST 2020] Checking host.foo.example.org for _acme-challenge.acme.foo.example.org                       
[Fr 4. Sep 09:53:07 CEST 2020] Not valid yet, let's wait 10 seconds and check next one.
[Fr 4. Sep 09:53:20 CEST 2020] Checking mein-schoener-host.bar.example.org for _acme-challenge.acme.foo.example.org
[Fr 4. Sep 09:53:21 CEST 2020] Domain mein-schoener-host.bar.example.org '_acme-challenge.acme.foo.example.org' success.
[Fr 4. Sep 09:53:21 CEST 2020] Let's wait 10 seconds and check again.
[Fr 4. Sep 09:53:32 CEST 2020] Checking host.foo.example.org for _acme-challenge.acme.foo.example.org
[Fr 4. Sep 09:53:32 CEST 2020] Domain host.foo.example.org '_acme-challenge.acme.foo.example.org' success.
[Fr 4. Sep 09:53:32 CEST 2020] Checking mein-schoener-host.bar.example.org for _acme-challenge.acme.foo.example.org
[Fr 4. Sep 09:53:32 CEST 2020] Already success, continue next one.                                                                          
[Fr 4. Sep 09:53:32 CEST 2020] All success, let's return
[Fr 4. Sep 09:53:32 CEST 2020] Verifying: host.foo.example.org
[Fr 4. Sep 09:53:35 CEST 2020] Pending
[Fr 4. Sep 09:53:38 CEST 2020] Success
[Fr 4. Sep 09:53:38 CEST 2020] Verifying: mein-schoener-host.bar.example.org                                                               
[Fr 4. Sep 09:53:41 CEST 2020] Success
[Fr 4. Sep 09:53:41 CEST 2020] Removing DNS records.
[...]
[Fr 4. Sep 09:53:41 CEST 2020] Removed: Success
[Fr 4. Sep 09:53:41 CEST 2020] Verify finished, start to sign.
[Fr 4. Sep 09:53:41 CEST 2020] Lets finalize the order.
[Fr 4. Sep 09:53:41 CEST 2020] Le_OrderFinalize='https://acme-staging-v02.api.letsencrypt.org/acme/finalize/15482274/142528495'
[Fr 4. Sep 09:53:42 CEST 2020] Downloading cert.
[Fr 4. Sep 09:53:42 CEST 2020] Le_LinkCert='https://acme-staging-v02.api.letsencrypt.org/acme/cert/fa6e5a9922acccb49619cc8808f6f0916dc0'
[Fr 4. Sep 09:53:43 CEST 2020] Cert success.
[...]
[Fr 4. Sep 09:53:43 CEST 2020] Your cert is in  /home/alice/.acme.sh/host.foo.example.org/host.foo.example.org.cer
[Fr 4. Sep 09:53:43 CEST 2020] Your cert key is in  /home/alice/.acme.sh/host.foo.example.org/host.foo.example.org.key
[Fr 4. Sep 09:53:43 CEST 2020] The intermediate CA cert is in /home/alice/.acme.sh/host.foo.example.org/ca.cer
[Fr 4. Sep 09:53:43 CEST 2020] And the full chain certs is there:  /home/alice/.acme.sh/host.foo.example.org/fullchain.cer

Damit liegen alle benötigten Dateien auf unserem Host. Im nächsten Schritt werden diese in die korrekte Lokation installiert.

Installation der Zertifikate für Apache

Das folgende Beispiel gilt für den Webserver Apache und ist der Dokumentation des acme.sh-Clients entnommen. Auf der verlinkten Seite findet sich ein weiteres Beispiel für den Webserver NGINX.

Das Kommando im folgenden Beispiel enthält am Ende den Parameter --reloadcmd "sudo systemctl reload httpd.service. Das sudo ist notwendig, wenn man einen User mit sudo-Berechtigungen wie z.B. Alice nutzt (siehe oben). Als root lässt man sudo einfach weg. Ggf. muss der Name der Service-Unit an das eigene System angepasst werden.

acme.sh --install-cert -d host.foo.example.org --cert-file /etc/letsencrypt/host.foo.example.org.cer --key-file /etc/letsencrypt/host.foo.example.org.key --fullchain-file /etc/letsencrypt/host.foo.example.org.fullchain.cer --reloadcmd "sudo systemctl reload httpd.service"

Hat alles funktioniert, erzeugt obiger Befehl folgende Ausgabe:

[Fr 4. Sep 12:36:22 CEST 2020] Installing cert to:/etc/letsencrypt/host.foo.example.org.cer
[Fr 4. Sep 12:36:22 CEST 2020] Installing key to:/etc/letsencrypt/host.foo.example.org.key
[Fr 4. Sep 12:36:22 CEST 2020] Installing full chain to:/etc/letsencrypt/host.foo.example.org.fullchain.cer
[Fr 4. Sep 12:36:22 CEST 2020] Run reload cmd: sudo systemctl reload httpd.service
[Fr 4. Sep 12:36:22 CEST 2020] Reload success

Hinweis: Der Webserver bzw. VirtualHost des Webservers muss so konfiguriert werden, dass die Zertifikate aus dem entsprechenden Pfad auch verwendet werden. Diese Konfiguration ist nicht Gegenstand dieses Tutorials. Es wird hierzu auf die Dokumentation des genutzten Webservers verwiesen.

Die Dateiberechtigungen für oben installierte Dateien sind nach der Installation einmalig zu korrigieren und ggf. mit chown und chmod so zu konfigurieren, dass nur Alice und der Webserver-User Zugriff darauf haben. Wichtig: Der Private-Key darf unter keinen Umständen von allen Usern gelesen werden dürfen. Folgender Code-Block zeigt eine mögliche Berechtigung:

ls -l /etc/letsencrypt/
total 12
-rw-r-----. 1 alice apache 1964  4. Sep 15:09 host.foo.example.org.cer
-rw-r-----. 1 alice apache 3644  4. Sep 15:09 host.foo.example.org.fullchain.cer
-rw-r-----. 1 alice apache 1679  4. Sep 15:09 host.foo.example.org.key

Die Dateiberechtigungen bleiben bei einer Erneuerung des Zertifikats und beim Überschreiben der existierenden Dateien erhalten.

Die vorgenommenen Einstellungen und Befehle werden in der Datei ~/.acme.sh/account.conf gespeichert. In der Standard-Einstellung wird das Zertifikat automatisch alle 60 Tage erneuert, in den angegebenen Pfaden installiert und ein Neustart des Webservers durchgeführt.

Weiterführende Quellen und Links

  1. A Technical Deep Dive: Securing the Automation of ACME DNS Challenge Validation: https://www.eff.org/deeplinks/2018/02/technical-deep-dive-securing-automation-acme-dns-challenge-validation
  2. How-to install acme.sh: https://github.com/acmesh-official/acme.sh/wiki/How-to-install
  3. Install from Git: https://github.com/acmesh-official/acme.sh#2-or-install-from-git
  4. Set up Let’s Encrypt certificate using acme.sh as non-root user: https://gist.github.com/Greelan/28a46a33140b65c9a045573ca460f044

Datenträger unter Linux mit cryptsetup (LUKS) verschlüsseln

Ihr möchtet einen Datenträger wie z.B. eine Festplatte oder einen USB-Stick verschlüsseln, damit bei einem Verlust des Datenträgers nicht jeder ohne weiteres Zutun eure Daten lesen kann? Dann haben wir ein gemeinsames Ziel!

Im Folgenden beschreibe ich, wie unter Linux mit Hilfe des Programms cryptsetup eine Festplatte verschlüsselt werden kann. Dazu gehört die Beschreibung, wie das verschlüsselte Gerät anschließend zur Nutzung geöffnet wird und wie man es in die /etc/crypttab einträgt, um das Gerät automatisch beim Start des Rechners zu öffnen. Es handelt sich dabei um eine zusätzliche Festplatte. Wie die erste Festplatte eines Systems verschlüsselt werden kann ist hingegen nicht Gegenstand dieses Artikels. Hier hilft (hoffentlich) ein Blick in die Installationsanleitung der jeweiligen Linux-Distribution weiter.

Das Programm cryptsetup ist in allen gängigen Linux-Distributionen verfügbar. Falls nicht, kann es direkt von der Projekt-Homepage auf GitLab bezogen werden. Die hier gezeigten Kommandos funktionieren prinzipiell auf jedem Linux-System, auf dem cryptsetup verfügbar ist.

Bevor es losgeht noch ein Hinweis. Ich möchte ein verschlüsseltes Gerät erstellen, mit dem Ziel, dass nicht jeder die Daten einfach lesen kann, dem das Gerät in die Hände fällt. Dabei denke ich an Szenarien wie Verlust, Diebstahl, Verkauf etc. Die Daten vor dem Zugriff durch Sicherheitsbehörden, Geheimdiensten oder Schurkenstaaten zu schützen erfordert einen deutlichen höheren Aufwand und ist nicht Gegenstand dieses Artikels.

Ich verwende im Folgenden LUKS2. Wer stattdessen lieber dm-crypt verwendet, sei auf die Manpage cryptsetup(8) verwiesen.

Die zu verschlüsselnde Festplatte wird in diesem Tutorial als /dev/sdX bezeichnet. Dieser Bezeichner muss auf das tatsächlich zu nutzende Gerät angepasst werden. Wichtig: Wird versehentlich das falsche Gerät verschlüsselt gehen vorhandene Daten unwiederbringlich verloren!

LUKS-Container erstellen

Zur Erstellung eines LUKS-Containers wird der folgende Befehl ausgeführt und die Bildschirmanweisungen befolgt. Für die dabei abgefragte Passphrase gilt grundsätzlich: „Je länger und komplizierter, desto sicherer. Wobei es mehr auf die Länge als die Komplexität ankommt.“

Weitere Hinweise zur Passwort-Sicherheit bietet der Wikipedia-Artikel zu Passsatz.

$ sudo cryptsetup luksFormat --type luks2 /dev/sdX
WARNING: Device /dev/sdX already contains a 'dos' partition signature.

WARNING!
========
This will overwrite data on /dev/sdX irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/sdX: 
Verify passphrase:

Hinzufügen einer Backup-Passphrase

LUKS-Passphrasen werden in sogenannten Key-Slots gespeichert. Es stehen 8 Key-Slots zur Verfügung, welche von 0-7 nummeriert sind. Die Passphrase aus dem vorigen Abschnitt wurde in Slot 0 gespeichert.

Vergisst man die Passphrase, wird der Key-Slot oder der LUKS-Header beschädigt, ist ein Zugriff auf die Daten nicht mehr möglich. Um mich gegen die ersten beiden Fälle abzusichern, erstellte ich zwei Backup-Passphrasen, welche ich in den Key-Slots 3 und 8 speicherte. Wie man sich gegen den Verlust des LUKS-Headers schützt, beschreibe ich im folgenden Abschnitt.

$ # Hinzufügen der ersten Backup-Passphrase in Key-Slot 3
$ sudo cryptsetup luksAddKey /dev/sdX --key-slot 3
Enter any existing passphrase: 
Enter new passphrase for key slot: 
Verify passphrase:

$ # Hinzufügen der zweiten Backup-Passphrase in Key-Slot 8
$ sudo cryptsetup luksAddKey /dev/sdX --type luks2 --key-slot 8
Enter any existing passphrase: 
Enter new passphrase for key slot: 
Verify passphrase:

Für die Backup-Passphrasen gilt selbstverständlich das im vorangegangenen Abschnitt Beschriebene. Die einzelnen Passphrasen sollten von gleicher Güte sein.

Die Backup-Passphrasen sind an einem sicheren Ort aufzubewahren. Ich verwende dazu bspw. einen Passwort-Tresor (vgl. Sichere Passwörter und wie man sie verwaltet).

LUKS-Header sichern

Nun nützen alle Backup-Passphrasen nichts, wenn der LUKS-Header beschädigt wird. Daher empfehle ich diesen mit Hilfe des folgenden Kommandos in eine Datei zu sichern.

$ sudo cryptsetup luksHeaderBackup /dev/sdX --header-backup-file /var/tmp/luksHeaderBackup_2020-10-18

Die so erstellte Datei ist sicher — am besten offline und vom Datenträger getrennt — aufzubewahren. Mit Hilfe dieser Datei und einer Passphrase, welche bei Erstellung des Backups gültig war, kann ein defekter LUKS-Header und damit der Zugriff auf ein verschlüsseltes Gerät wiederhergestellt werden.

Ändert man im Laufe der Zeit in Key-Slots gespeicherte Passphrasen, löscht alte oder fügt neue hinzu, so ist eine erneute Sicherung des LUKS-Headers sinnvoll und angeraten.

LUKS-Container öffnen und nutzen

Bis jetzt haben wir auf dem Block-Gerät /dev/sdX einen verschlüsselten LUKS-Container erstellt, Backup-Passphrasen hinzugefügt und den LUKS-Header gesichert. Der folgende Code-Block zeigt, wie der LUKS-Container geöffnet und damit nutzbar gemacht wird. Dabei ist sdX_crypt ein Bezeichner, welcher für den Device Mapper verwendet wird. Dieser Bezeichner kann von euch frei gewählt werden.

$ sudo cryptsetup open /dev/sdX sdX_crypt
$ lsblk | grep sdX
sdX              8:16   0 111,8G  0 disk  
└─sdX_crypt    254:3    0 111,8G  0 crypt

Der so geöffnete LUKS-Container kann nun mit einem Dateisystem eurer Wahl formatiert werden. Folgender Code-Block zeigt ein Beispiel für ext4. Dies kann in Abhängigkeit zur Größe des LUKS-Containers einige Zeit dauern.

$ sudo mkfs.ext4 /dev/mapper/sdX_crypt
[...]
$ sudo blkid | grep sdX
/dev/sdX: UUID="Eine-furchbar-lange-UUID" TYPE="crypto_LUKS"
/dev/mapper/sdX_crypt: UUID="Eine-andere-furchbar-lange-UUID" TYPE="ext4"

Man beachte, dass der LUKS-Container direkt mit einem Dateisystem formatiert werden kann. Die Erstellung einer Partition ist nicht erforderlich.

Das erstellte Dateisystem kann anschließend wie gewohnt eingehängt werden.

$ sudo mount /dev/mapper/sdX_crypt /mnt

Mit den folgenden Kommandos wird das Dateisystem wieder ausgehängt und der LUKS-Container verschlossen. Damit liegen die Daten nur noch in verschlüsselter Form vor, bis sie das nächste Mal geöffnet werden.

$ sudo umount /dev/mapper/sdX_crypt
$ sudo cryptsetup close sdX_crypt
$ sudo cryptsetup status sdX_crypt
/dev/mapper/sdX_crypt is inactive.

Verschlüsseltes Dateisystem bei Startvorgang des Rechners öffnen und einhängen

Soll das erstellte Dateisystem /dev/mapper/sdX_crypt bereits beim Start des Rechners eingehängt werden, ist folgende Reihenfolge einzuhalten:

  1. LUKS-Container sdX_crypt mit Hilfe der /etc/crypttab öffnen
  2. Dateisysteme aus /etc/fstab einhängen

Die Einträge in den entsprechenden Dateien sehen in meinem Fall wie folgt aus. In /etc/crypttab wird dabei die UUID des Gerätes /dev/sdX verwendet.

$ sudo cat /etc/crypttab
sdX_crypt UUID="Eine-furchbar-lange-UUID" none luks,discard

$sudo cat /etc/fstab
/dev/mapper/sdX_crypt /mnt ext4 defaults 0 0

Man beachte, dass der Rechner nun den Startvorgang unterbricht und zur Eingabe der Passphrase zum Öffnen des LUKS-Containers auffordert. Anschließend wird der Startvorgang fortgesetzt.

Damit sind wir eigentlich am Ende des Tutorials angelangt. Wie fandet ihr es? War es verständlich genug, um folgen zu können und Eingangs erwähnte Ziele zu erreichen?

Wer noch Zeit und Lust hat, kann gerne weiterlesen. In den folgenden Abschnitten werde ich als Zugabe noch beschreiben, wie man Key-Slots löscht und einen LUKS-Header aus einem Backup wiederherstellt.

Key-Slots löschen

Mit dem folgenden Befehl werden sämtliche Key-Slots des LUKS-Containers gelöscht. Ein Zugriff auf den LUKS-Container ist anschließend nicht mehr möglich. Die Angabe eines Passworts ist nicht erforderlich.

Warnung: Dieser Vorgang kann nicht rückgängig gemacht werden.

$ sudo cryptsetup erase /dev/sdX

WARNING!
========
This operation will erase all keyslots on device /dev/sdb.
Device will become unusable after this operation.

Are you sure? (Type uppercase yes):

Das war’s.

LUKS-Header aus Sicherung wiederherstellen

Und nun halte ich noch fest, wie man einen LUKS-Header und die darin enthaltenen Key-Slots aus einer Datensicherung wiederherstellen kann.

$ sudo cryptsetup luksHeaderRestore /dev/sdX --header-backup-file /tmp/luksHeaderBackup_2020-10-18 

WARNING!
========
Device /dev/sdX already contains LUKS2 header. Replacing header will destroy existing keyslots.

Are you sure? (Type uppercase yes):

Uns schon kann man wieder auf seinen LUKS-Container zugreifen.

Reflexion

Um meine Festplatte zu verschlüsseln und diesen Beitrag zu schreiben, habe ich einige Zeit in der Manpage cryptsetup(8) und auf der Projekt-Seite gelesen. Nur mit der Manpage allein wäre mir eine Nutzung vermutlich erst nach deutlich längerer Zeit gelungen.

Dabei vermisse ich in der Manpage vor allem eine Inhaltsübersicht und Beispiele, mit denen man sich gängige Aufgaben schnell ins Gedächtnis rufen kann. Zwar existiert seit ca. zwei Jahren ein Ticket zur Überarbeitung der Manpage, doch fehlt es bei den am Projekt beteiligten Personen wie so oft an der Zeit. Diese wird primär zur Arbeit am Code genutzt. Für die Dokumentation bleibt keine Zeit übrig.

Aktuell erkundige ich mich auf der Mailling-Liste, wie man am besten dazu beitragen kann, die Manpage zumindest in Teilen zu verbessern. Mal sehen, was sich da machen lässt.

Da man vermutlich nicht täglich mit cryptsetup arbeitet, ist eine gute Dokumentation um so wichtiger, da vermutlich niemand alle notwendigen Befehle dauerhaft im Gedächtnis behält.

Mit diesem Tutorial habe ich mir meine eigene Dokumentation für meine häufigsten Anwendungsfälle geschaffen. Wenn ihr sie ebenfalls nützlich findet, freut mich dies umso mehr.

Social-Red_Hat-Accelerators-Personal_Post-Twitter-RGB

Das Red Hat Accelerator Programm ist offiziell gestartet

In seiner heutigen Presseerklärung gibt Red Hat den offiziellen Start des Red Hat Accelerators Programms bekannt.

Die Red Hat Accelerators sind eine einzigartige Gemeinschaft, bestehend aus Open Source IT-Enthusiasten. Ich bin stolz Mitglied dieser Gemeinschaft zu sein. Dadurch habe dich die Möglichkeit:

  • Mich mit Gleichgesinnten zu vernetzen
  • Frühzeitig einen Blick auf neue Technologien und Entwicklungen von Red Hat zu werfen
  • Mit Experten von Red Hat zu interagieren
  • Die Produktentwicklung mit zu beeinflussen

Wir sind Kunden und Partner, welche gern über IT-Themen diskutieren, uns austauschen und Red Hat ehrliches Feedback zur ihren Produkten zukommen lassen.

Und diese Möglichkeiten stehen auch Dir offen!

Wenn auch Du ein Teil dieser außergewöhnlichen Gemeinschaft werden möchtest, erfährst du alles notwendige für deine Bewerbung unter diesem Link.

Vielen Dank an Andi, Grace und Jeff, welche alles dafür geben, damit wir uns in diesem Programm wohlfühlen.