Zum Inhalt

App hinzufügen

Zuerst DNS und Token-Policy, dann der Stack

Roll den DNS-Eintrag und die Zeile _acme-challenge.<subdomain> in der Token-Policy in einem eigenen Commit aus, bevor du die Stack-Datei anlegst (Schritte 2 bis 4). Fehlt die Zeile in der Token-Policy, wenn die App zum ersten Mal ausgerollt wird, bekommt sie kein Zertifikat, und Caddy versucht es auch nach dem Nachtragen erst nach langer Zeit erneut.

Ziel

Eine neue App läuft auf einem Host unter ihrer eigenen Adresse mit gültigem Zertifikat, wird gesichert, überwacht und ist, wo nötig, an Authentik angebunden.

Voraussetzungen

Diese Anleitung ist eine Checkliste: Jeder Schritt sagt, was dazugehört, und verlinkt für die Einzelheiten auf die passende Anleitung.

Schritte

1. Name und Hostnamen festlegen

Leg fest:

  • den Namen der App, klein und ohne Leerzeichen, etwa beispiel; danach heißen Stack-Datei, Container, Volumes und Secrets
  • den Hostnamen: auf werner <subdomain>.garage-lab.de, auf containerhost <subdomain>.garage-lab.net

Prüf in der Zonendatei, dass es die Subdomain noch nicht gibt:

Bash
grep -n '"beispiel"' desec/desec_garage_lab_de.cue

Erwartet: keine Ausgabe.

2. DNS-Eintrag anlegen

Nur für eine App auf werner. Trag in desec/desec_garage_lab_de.cue bei den anderen Apps einen A- und einen AAAA-Eintrag auf werner ein, wie bei md:

Text Only
{subname: "beispiel", type: "A", records: [ipam.netcup_vps.a]},
{subname: "beispiel", type: "AAAA", records: [ipam.netcup_vps.aaaa]},

Für eine App auf containerhost kommt kein Eintrag nach deSEC; den Namen löst der DNS-Server auf dem Gateway auf, der nicht im Repo steht (siehe Netzwerk). Bitte in Signal jemanden mit Zugang zum Gateway, den Namen dort einzutragen.

3. Token-Policy ergänzen

Trag am Ende derselben Zonendatei in der Token-Policy des Caddy auf dem Ziel-Host eine Zeile für die Subdomain ein, alphabetisch einsortiert:

Host Datei Block
werner desec/desec_garage_lab_de.cue desec: tokenPolicies: "2262403e-…"
containerhost desec/desec_garage_lab_net.cue desec: tokenPolicies: "2f198c18-…"
Text Only
{domain: "garage-lab.de", subname: "_acme-challenge.beispiel", type: "TXT", permWrite: true},

Auf containerhost steht garage-lab.net statt garage-lab.de.

4. DNS ausrollen, committen und pushen

Wie in Änderung ausrollen, im Ordner desec/. plan zeigt dabei für werner etwa:

Text Only
Token Policies:
  Token 2262403e-a7f8-41bf-b2f1-d32030cd26b5:
    + create policy {domain=garage-lab.de subname=_acme-challenge.beispiel type=TXT} permWrite=true
  (no changes) token 2f198c18-934a-4db4-872b-b1ab604c8728

RRsets:
  Domain "garage-lab.de":
    + create  beispiel                 A
              ttl=3600 records=[…]
    + create  beispiel                 AAAA
              ttl=3600 records=[…]

Für containerhost nur die Zeile + create policy. Commit und Push gehören zu diesem Schritt; die Stack-Datei kommt in einen eigenen Commit.

Prüfen (nur werner):

Bash
dig +short A beispiel.garage-lab.de @ns1.desec.io

Erwartet: die IPv4-Adresse von werner.

5. Stack-Datei anlegen

Kopier die Stack-Datei einer ähnlichen App und pass sie an:

Bash
cp syslet/werner/stack-hedgedoc.cue syslet/werner/stack-beispiel.cue

Geh die neue Datei von oben nach unten durch und ersetze überall den Namen der alten App durch den neuen. Achte dabei auf:

  • Image: Tag oben in der Datei, nach den Release Notes der App gewählt (siehe Image-Version anheben)
  • Netzwerke: ein eigenes internes Netzwerk für die App; auf werner zusätzlich caddy für den Container, der von außen erreichbar sein soll (siehe syslet in diesem Repo)
  • Volumes: :Z, wenn nur dieser Container das Volume nutzt, :z, wenn es auch gesichert wird oder ein anderer Container es nutzt (siehe SELinux)
  • Labels: auf werner #Labels mit partOf gleich dem Namen der App (siehe syslet in diesem Repo)
  • Ingress: Hostname aus Schritt 1, Port, auf dem die App im Container lauscht
  • Locks: jedes Volume mit Daten, die Container und das Netzwerk der App im Block tools.#SysdefLock
  • Secrets: Passwörter und Tokens nicht unter Environment:, sondern nach Secret anlegen oder ändern

Trag außerdem für jeden Container ein Speicherlimit in syslet/<host>/host.cue ein, wie für die anderen Container dort.

6. Backup einbinden

Nur für Apps auf werner, die Daten haben, die wir nicht verlieren wollen.

In der Stack-Datei:

  • backup: <app>: mit den Volumes und Pfaden, die gesichert werden sollen, wie am Ende von stack-hedgedoc.cue
  • bei einer Postgres-Datenbank beim Datenbank-Container configFiles: [restic.scripts.postgresBackup, restic.scripts.postgresRestore] und das Volume <app>-postgres-backups

In rootfs/werner/usr/bin/system-backup eine Zeile, die den Dump schreibt, wie bei HedgeDoc:

Bash
info "Backing up Beispiel database"
podman exec beispiel-postgres /bin/container-backup || record_error "beispiel backup failed"

und in rootfs/werner/etc/systemd/system/system-backup.service eine Zeile Requires=beispiel-postgres.service. Diese beiden Dateien kopierst du erst in Schritt 9 auf den Host.

7. Anmeldung über Authentik

Nur wenn sich die Leute in der App anmelden sollen. Leg die App in Authentik an, wie in App per OIDC anbinden, und trag Client-ID und Client-Secret nach Secret anlegen oder ändern als oidcclientid und oidcclientsecret in creds-<app>-server.enc.yaml ein.

8. Monitoring

  • Hat die App eine Postgres-Datenbank, bekommt sie einen eigenen Container <app>-postgres-exporter, wie in stack-hedgedoc.cue, mit dem nächsten freien Port unter PublishPort (die belegten zeigt grep -n PublishPort syslet/werner/*.cue). Dazu kommt ein Eintrag unter scrape_configs in syslet/containerhost/stack-prom.cue, wie bei hedgedoc-postgres.
  • Ist die App öffentlich erreichbar, trägt jemand mit Zugang ihre Adresse in der Web-Oberfläche von Uptime Kuma ein (siehe Uptime-Monitoring).

9. Ausrollen

Wie in Änderung ausrollen, im Ordner des Hosts; für einen neuen Exporter danach auch in syslet/containerhost.

Erwartet bei plan: unter Images to pull: die Images der App, unter Unit file changes: neue Units für Container, Volumes und Netzwerk der App, bei Secrets Secret changes:, und in der Zusammenfassung jede Unit der App mit created. Dazu caddy.container mit config updated, reloaded, weil das Caddyfile den neuen Hostnamen bekommt, und mit Backup-Block restic.container mit unit updated, restarted.

Hast du in Schritt 6 die Backup-Dateien geändert, kopier sie nach dem apply auf werner und lass systemd sie neu einlesen:

Bash
1
2
3
scp rootfs/werner/usr/bin/system-backup werner.garage-lab.de:/usr/bin/system-backup
scp rootfs/werner/etc/systemd/system/system-backup.service werner.garage-lab.de:/etc/systemd/system/system-backup.service
ssh werner.garage-lab.de systemctl daemon-reload

Erwartet: keine Ausgabe außer dem Fortschritt von scp.

Hat die App auf werner ein eigenes internes Netzwerk, trag dessen Subnetz nach dem ersten apply in firewalld ein, wie in Podman-Netzwerke reparieren; sonst erreichen sich ihre Container nicht.

Committe Stack-Datei, host.cue, Secrets und rootfs/-Dateien zusammen und push sofort.

10. App-Seite schreiben

Leg unter docs/explanation/selfhosting/apps/<app>.md eine Seite nach der Vorlage .work/docs/app-template.md an, trag sie in der Tabelle des Hosts in docs/explanation/general/apps.md und in nav in zensical.toml ein und prüf mit make docs.

Prüfen

  • cue cmd ingress im Ordner des Hosts listet den neuen Hostnamen.
  • curl -sI https://beispiel.garage-lab.de antwortet ohne Zertifikatsfehler; das erste Zertifikat kann wegen der DNS-Challenge ein paar Minuten dauern.
  • Die Anmeldung über Authentik klappt, falls angebunden.
  • Am Morgen nach dem ersten Backup zeigt cue cmd -t unit=system-backup.service status in syslet/werner status=0/SUCCESS.
  • cue cmd plan zeigt in jedem Ordner, in dem du ausgerollt hast, „No changes“.

Wenn etwas schiefgeht