Featured image of post Podman Pods mit Authentik

Podman Pods mit Authentik

Vorstellung von Podman Pods am Beispiel von Authentik

Hinweis
Für diese Beschreibung ist mindestens die Podman Version 5.0 erforderlich, da erst dort die Quadlets um die “Pod Units” ergänzt worden sind. Die aktuell installierte Version kann mit dem Befehl podman version geprüft werden.

Einleitung

In den bisherigen Artikeln haben wir Podman unter Alma Linux installiert (Einführung in Podman) und die Quadlets genutzt, um einen einzelnen Container zu starten (Podman Quadlets). Eine weitere Funktion, die Podman von Docker abhebt, ist die namensgebende Funktion “Pod”. In einem Pod können mehrere Container zusammengefasst werden, die sich die gleichen Ressourcen teilen. Dies ist immer dann praktisch, wenn eine Anwendung aus mehreren Komponenten besteht, wie zum Beispiel einer Weboberfläche und einer dazugehörigen Datenbank. Sie sind vergleichbar mit den Pods aus Kubernetes. Unter anderem deshalb lassen sich aus Podman Pods auch mit einem einfachen Befehl Konfigurationsdateien für Kubernetes generieren.

Wie funktionieren Pods?

Springen wir zunächst einmal zu Docker und schauen uns an, wie man dort mit Anwendungen umgeht, die aus mehreren Containern bestehen. Unter Docker würde man alle Container einer Anwendung in einer docker-compose zusammen angeben, damit Docker dann ein internes Netzwerk für diese Container aufbaut, über das sich die einzelnen Komponenten dann gegenseitig erreichen können.

Ein Pod dagegen verhält sich etwas anders. Alle Container, die dem gleichen Pod zugewiesen sind, teilen sich die Ressourcen untereinander. Das bedeutet zum Beispiel, dass eine Anwendung seine Datenbank, wenn sie im gleichen Pod liegt, über “localhost:PORT” erreichen kann. Es gibt also kein separates Netzwerk, über dass die Erreichbarkeit erst zustande kommt. Außerdem steht alles, was für den Pod definiert wird, für alle Container zur Verfügung. Wird auf Pod-Ebene ein Volume definiert, kann jeder Container im Pod darauf zugreifen.

Nachfolgend die Skizze eines Podman Pods für Authentik zur besseren Verständlichkeit:

Skizzierung eines Pods für den Betrieb von Authentik

Der zusätzlich eingezeichnete “Infra”-Container kann vorerst vernachlässigt werden. Bei jedem Pod startet immer automatisch ein kleiner Infra-Container mit, der für die Verwaltung des Pods zuständig ist.

Da sich die Container im Pod quasi wie installierte Anwendungen verhalten gilt darin auch die gleiche Einschränkung in Bezug auf Ports: Es darf keine zwei Container geben, die den gleichen Port innerhalb eines Pods nutzen wollen. In dem Fall wird einer der beiden Container nicht starten können, weil der benötigte Port bereits in Verwendung ist (“already in use”). In den meisten Fällen sollte dies kein Problem sein, da die einzelnen Komponenten einer Anwendung selten die gleichen Ports nutzen, sonst hätten sie auch auf einem normalen System ein Problem.

Pods mit Quadlets

Was ist also zu beachten, wenn man jetzt mehrere Container in einem Pod zusammenfassen möchte? Im Endeffekt gilt alles, was wir bislang mit den Quadlets gemacht haben, weiterhin. Es muss nur neben den .container-Quadlets eine weitere Datei mit der Endung “.pod” erstellt werden und zusätzlich muss in den Container Quadlets mit Pod=dateiname.pod eine Referenz auf den Pod eingetragen werden. Das reicht schon aus damit systemd weiß, dass dieser Container zu dem Pod gehört.

Wichtig: Wir haben in der Anleitung “Podman Quadlets” in der .container-Datei den Hostnamen des Containers explizit festgelegt:

1
HostName=flame

Diese Angabe muss entfallen, wenn der Container einem Pod zugeordnet wird. Innerhalb eines Pods kann ein Container keinen spezifischen Hostnamen mehr haben.

Was ist überhaupt Authentik?

Mehrfach wurde jetzt erwähnt, dass hier die Pod-Mechanik anhand von Authentik erläutert wird, aber was ist das überhaupt?

Authentik ist ein Identity Management System. Konkret bedeutet dies, dass es eine zentrale Stelle ist, an der man seine Benutzer verwalten kann. Es soll dann die zentrale Stelle zum Login in verschiedenste Anwendungen sein, man spricht hier unter anderem auch von Single-Sign-On (SSO). Im besten Fall loggt man sich also nur noch einmal über Authentik ein und kann dann auf alle seine Anwendungen zugreifen, ohne sich überall separat einloggen zu müssen.

Authentik bietet verschiedenste Optionen, um es in andere Anwendungen einzubinden, unter anderem OAuth2, LDAP oder Forward Auth für einen Reverse Proxy. Viele Anwendungen unterstützen eines oder mehrere dieser Verfahren. Einmal eingerichtet, wird die Authentifizierung nicht mehr bei der Einzelanwendung selbst ausgeführt, sondern an Authentik weitergegeben.

Quadlets anlegen

Es gibt keine Notwendigkeit, Authentik unter root laufen zu lassen, deswegen wird das Ganze unter einem ganz normalen Benutzer laufen. Wenn ihr der Anleitung zu (Podman Quadlets) gefolgt seid, so habt ihr in eurem Benutzerverzeichnis bereits einen symbolischen Link mit dem Namen “containers” der direkt zu dem Ort führt, an dem die Quadlets abgelegt werden müssen.

Dennoch benötigen wir dort einen neuen Unterordner, in den wir dann anschließend auch direkt reinwechseln:

1
mkdir ~/containers/authentik && cd $_

Hinweis: $_ enthält das Argument des vorangegangenen Befehls, also “~/containers/authentik”

Hier nochmal der Befehl mit dem vollen Pfad, falls ihr den symbolischen Link nicht habt:

1
mkdir -p ~/.config/containers/systemd/authentik && cd $_

Übersicht

Authentik besteht aus 4 Containern, die wir jetzt nach und nach definieren werden:

  • PostgreSQL Datenbank
  • Redis
  • Authentik Server
  • Authentik Worker

Secrets anlegen

Zunächst benötigen wir aber noch zwei Secrets, die wir später in den Containern nutzen. Dabei handelt es sich zum einen um ein Passwort für PostgreSQL und zum anderen um einen Secret Key, der vom Authentik Server und Worker benötigt wird. Da wir die Werte nicht kennen müssen und diese nur in den Containern selbst relevant sind, erzeugen wir zufällige Werte und speichern diese direkt als Secrets, ohne dass wir sie jemals gesehen haben:

1
2
printf $(openssl rand -base64 36 | tr -d '\n') | podman secret create authentik-postgresql-password -
printf $(openssl rand -base64 60 | tr -d '\n') | podman secret create authentik-secret-key -

Container: PostgreSQL

Es folgen jetzt mehrere Schritte, bei denen nur Dateien angelegt und der danach angezeigte Inhalt übertragen wird. Es handelt sich um die Definitionen der einzelnen Container, alle referenzieren auf den Pod “authentik”.

Neue Datei anlegen:

1
nano authentik-postgresql.container

Und folgenden Inhalt einsetzen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[Unit]
Description=PostgreSQL Podman Container (Authentik)
Wants=network-online.target
After=network-online.target

[Container]
AutoUpdate=registry

# Pod
Pod=authentik.pod

# Container
Image=docker.io/library/postgres:16-alpine
ContainerName=authentik-postgresql

# Health Check
HealthCmd=pg_isready -d authentik -U authentik
HealthStartPeriod=20s
HealthInterval=30s
HealthRetries=5
HealthTimeout=5s

# Volume
Volume=authentik-postgresql-data.volume:/var/lib/postgresql/data

# Environment
Environment=POSTGRES_DB=authentik
Environment=POSTGRES_USER=authentik

# Secrets
Secret=authentik-postgresql-password,type=env,target=POSTGRES_PASSWORD

[Service]
Restart=always

[Install]
WantedBy=default.target

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

Container: Redis

Neue Datei anlegen:

1
nano authentik-redis.container

Und folgenden Inhalt einsetzen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[Unit]
Description=Redis Podman Container (Authentik)
Wants=network-online.target
After=network-online.target

[Container]
AutoUpdate=registry

# Pod
Pod=authentik.pod

# Container
Image=docker.io/library/redis:alpine
ContainerName=authentik-redis

# Health Check
HealthCmd=redis-cli ping | grep PONG
HealthStartPeriod=20s
HealthInterval=30s
HealthRetries=5
HealthTimeout=3s

# Volume
Volume=authentik-redis-data.volume:/data

# Start Command
Exec=--save 60 1 --loglevel warning

[Service]
Restart=always

[Install]
WantedBy=default.target

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

Container: Authentik Server

Neue Datei anlegen:

1
nano authentik-server.container

Und folgenden Inhalt einsetzen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[Unit]
Description=Server Podman Container (Authentik)
Wants=network-online.target
After=network-online.target authentik-postgresql.service authentik-redis.service

[Container]
AutoUpdate=registry

# Pod
Pod=authentik.pod

# Container
Image=ghcr.io/goauthentik/server:latest
ContainerName=authentik-server

# Volume
Volume=authentik-server-media.volume:/media
Volume=authentik-server-custom-templates.volume:/templates

# Environment
Environment=AUTHENTIK_REDIS__HOST=localhost
Environment=AUTHENTIK_POSTGRESQL__HOST=localhost
Environment=AUTHENTIK_POSTGRESQL__USER=authentik
Environment=AUTHENTIK_POSTGRESQL__NAME=authentik

# Secrets
Secret=authentik-postgresql-password,type=env,target=AUTHENTIK_POSTGRESQL__PASSWORD
Secret=authentik-secret-key,type=env,target=AUTHENTIK_SECRET_KEY

# Start Command
Exec=server

[Service]
Restart=always

[Install]
WantedBy=default.target

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

Container: Authentik Worker

Neue Datei anlegen:

1
nano authentik-worker.container

Und folgenden Inhalt einsetzen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[Unit]
Description=Worker Podman Container (Authentik)
Wants=network-online.target
After=network-online.target authentik-postgresql.service authentik-redis.service

[Container]
AutoUpdate=registry

# Pod
Pod=authentik.pod

# Container
Image=ghcr.io/goauthentik/server:latest
ContainerName=authentik-worker

# Volume
Volume=authentik-server-media.volume:/media
Volume=authentik-server-custom-templates.volume:/templates
Volume=authentik-worker-certs.volume:/certs

# Environment
Environment=AUTHENTIK_REDIS__HOST=localhost
Environment=AUTHENTIK_POSTGRESQL__HOST=localhost
Environment=AUTHENTIK_POSTGRESQL__USER=authentik
Environment=AUTHENTIK_POSTGRESQL__NAME=authentik

# Secrets
Secret=authentik-postgresql-password,type=env,target=AUTHENTIK_POSTGRESQL__PASSWORD
Secret=authentik-secret-key,type=env,target=AUTHENTIK_SECRET_KEY

# Start Command
Exec=worker

[Service]
Restart=always

[Install]
WantedBy=default.target

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

Volumes definieren

In den Container-Quadlets wurden einige Volumes definiert, die genutzt werden sollen. Deswegen folgen jetzt die Definitionen für diese Volumes.

1
nano authentik-postgresql-data.volume

Inhalt:

1
2
3
4
5
[Unit]
Description=PostgreSQL Volume (Authentik)

[Volume]
VolumeName=authentik-postgresql-data

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

1
nano authentik-redis-data.volume

Inhalt:

1
2
3
4
5
[Unit]
Description=Redis Volume (Authentik)

[Volume]
VolumeName=authentik-redis-data

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

1
nano authentik-server-media.volume

Inhalt:

1
2
3
4
5
[Unit]
Description=Server Media Volume (Authentik)

[Volume]
VolumeName=authentik-server-media

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

1
nano authentik-server-custom-templates.volume

Inhalt:

1
2
3
4
5
[Unit]
Description=Server Custom Templates Volume (Authentik)

[Volume]
VolumeName=authentik-server-custom-templates

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

1
nano authentik-worker-certs.volume

Inhalt:

1
2
3
4
5
[Unit]
Description=Worker Certs Volume (Authentik)

[Volume]
VolumeName=authentik-worker-certs

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

Pod

Jetzt fehlt nur noch die Definition des Pods, auf den alle Container referenzieren. Hier definieren wir auch den Port, über den dann die Authentik Weboberfläche erreichbar ist. 9000/tcp ist dabei der HTTP-Port und 9443/tcp der HTTPS-Port.

1
nano authentik.pod

Inhalt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=Authentik Pod

[Pod]
PodName=authentik

# Ports
PublishPort=9000:9000/tcp
#PublishPort=9443:9443/tcp

[Install]
WantedBy=default.target

Speichern und Schließen mit STRG+O -> ENTER STRG+X (Mac: control statt STRG)

Authentik ausführen

Damit systemd die von uns angelegten Quadlets einliest und daraus seine Service-Dateien generiert, führen wir jetzt zunächst einen Reload aus:

1
systemctl --user daemon-reload

Damit können wir den Pod jetzt auch schon direkt starten. Der Name, den man für systemctl nutzen muss, ist der Dateiname der “.pod”-Datei ohne Dateiendung, aber mit der Ergänzung “-pod”. Aus unserem “authentik.pod” entsteht also der Name: authentik-pod

1
systemctl --user start authentik-pod.service

Aufgrund der Abhängigkeiten und weil alle Images heruntergeladen werden müssen, kann es beim ersten Start etwas länger dauern, bis alle Container zu sehen sind. Falls auch nach einigen Minuten noch nicht alle Container gestartet sind, hilft ein Blick ins journalctl, um ggf. vorhandene Fehler zu finden.

Pod Status prüfen

1
podman ps

Mit diesem Befehl sieht man wie gewohnt alle Container, die aktuell laufen. Darunter sollten jetzt auch alle Container zu sehen sein, die wir jetzt neu definiert haben. Zusätzlich sollte auch ein neuer Container mit einem eher kryptischen Namen gestartet worden sein. Dabei handelt es sich um den eingangs erwähnten Infra-Container. Den haben wir nicht explizit definiert, er wird automatisch generiert und gehört zum Pod.

Nicht zu erkennen ist hier, dass es irgendeine Beziehung zu einem Pod gibt. Dafür kann dann der nachfolgende Befehl genutzt werden:

1
podman pod ps

Jetzt sehen wir alle aktuell laufenden Pods und die Anzahl der Container, die der Pod enthält. Dies sollten 5 Container sein, die vier die wir definiert haben plus der Infra-Container:

1
2
POD ID        NAME        STATUS      CREATED     INFRA ID      # OF CONTAINERS
cfgedc87c951  authentik   Running     3 min ago   3dff5c251308  5

Mit dem Zusatz --ctr-names können dann auch noch die Namen der enthaltenen Container ausgegeben werden:

1
podman pod ps --ctr-names

Ausgabe:

1
2
POD ID        NAME        STATUS      CREATED     INFRA ID      NAMES
cecedc84c951  authentik   Running     4 min ago   3dff5c251308  cfgedc87c951-infra,authentik-redis,authentik-postgresql,authentik-server,authentik-worker

Port freigeben

Da wir das Setup unter einem normalen Benutzer laufen lassen, kann Podman nicht automatisch den Port in der Firewall freigeben. Dies muss also manuell zusätzlich erfolgen, wenn der Dienst von außen erreichbar sein soll. Falls es sich um einen öffentlichen Server im Internet handelt, solltet ihr nur den verschlüsselten Zugang freigeben, am besten über einen Reverse Proxy.

Wenn es sich um eine Installation im lokalen Netzwerk handelt, kann auch einfach der Port 9000 (HTTP / unverschlüsselt) freigegeben werden, hier am Beispiel mit firewalld:

1
2
sudo firewall-cmd --permanent --add-port=9000/tcp
sudo firewall-cmd --reload

Initiale Einrichtung

Um die initiale Einrichtung von Authentik zu starten, ruft ihr in eurem Browser die nachfolgende URL (entsprechend angepasst) auf:

http://IP_EURES_SERVERS:9000/if/flow/initial-setup/

Dort müsst ihr dann eine Email-Adresse und ein Passwort für den Standardbenutzer mit dem Namen akadmin definieren.

Abschluss

Damit ist Authentik einsatzbereit und kann nach und nach in andere Dienste für den Login integriert werden. Weitere Beiträge zu Authentik selbst und Beispiele zur Integration in andere Dienste werden folgen.

Erstellt mit Hugo
Theme Stack gestaltet von Jimmy