Zum Inhalt

Secrets im Repo

Diese Seite erklärt, wo Passwörter, Tokens und andere vertrauliche Angaben im Repo liegen, wer sie lesen kann und wo wir bewusst Ausnahmen machen.

Wo die Secrets liegen

Secrets liegen nur verschlüsselt im Repo. Verschlüsselt werden sie mit SOPS, und zwar in eigenen Dateien je Anwendung neben den Stack-Dateien des Hosts: syslet/<host>/creds-<name>.enc.yaml.

SOPS verschlüsselt nur die Werte, nicht die Namen. Wer eine solche Datei öffnet, sieht also, welche Secrets es gibt, aber nicht ihren Inhalt.

Die Stack-Dateien verweisen auf die Secrets nur über ihren Namen. Wie sie von dort in die Container kommen, steht in Podman Secrets.

Secrets liegen im Repo, direkt neben den Stack-Dateien.

  • Die Secrets einer Anwendung liegen im selben Ordner wie ihre Stack-Datei und werden mit ihr zusammen ausgerollt. Wer eine Anwendung ändert, findet alles an einer Stelle, und ein neues oder geändertes Secret kommt mit demselben apply auf den Server wie die Änderung, die es braucht.
  • Jede Änderung an einem Secret ist ein Commit: Man sieht, wann es geändert wurde, von wem und warum, und kann zu einem früheren Stand zurück.

Secrets liegen nicht in einem Passwortmanager oder in HashiCorp Vault. Die Secrets könnten auch in einem Passwortmanager wie Bitwarden oder in einem Dienst wie HashiCorp Vault liegen. Dann lägen Konfiguration und Secrets aber an zwei Stellen:

  • Dort sieht man einem Eintrag nicht an, ob und wo er gerade benutzt wird. Ob man ein Secret dort ändern oder löschen darf, wüsste man erst nach einer Suche im Repo.
  • Änderungen an einem Secret stünden nicht in der Git-Historie. Alte Fassungen werden zwar aufgehoben, aber man sieht nicht, zu welcher Änderung an der Konfiguration sie gehören.
  • Beim Ausrollen müssten die Secrets erst von dort abgeholt und mit der Konfiguration verbunden werden; der Dienst müsste dafür erreichbar sein. Mit SOPS braucht das reine Ausrollen keinen Zugriff auf die Secrets: Die verschlüsselten Dateien gehen so, wie sie sind, an den Host und werden erst dort entschlüsselt (siehe Wer entschlüsseln kann). SOPS braucht man auf dem eigenen Rechner nur, um Secrets anzulegen, zu ändern oder nachzusehen.

Secrets liegen nur verschlüsselt im Repo.

  • Ein Repo kann in falsche Hände geraten: Ein Laptop mit einer Kopie geht verloren, beim Git-Hoster ist etwas falsch eingestellt, oder das Repo wird versehentlich öffentlich. Verschlüsselte Secrets sind dann trotzdem geschützt.
  • Die Git-Historie vergisst nichts: Ein Secret, das einmal unverschlüsselt committet wurde, steht für immer in der Historie, auch wenn man es im nächsten Commit wieder löscht. Es gilt deshalb als bekannt und muss ausgetauscht werden.

Wie die Verschlüsselung funktioniert

SOPS verschlüsselt mit age, und age arbeitet mit Schlüsselpaaren: Was mit dem öffentlichen Teil eines Paars verschlüsselt wurde, lässt sich nur mit dem passenden privaten Teil wieder lesen. Der öffentliche Teil darf deshalb offen im Repo stehen; den privaten behält jede Person bzw. jeder Host für sich.

Damit mehrere Personen und Hosts dieselbe Datei lesen können, verschlüsselt SOPS die Werte mit einem zufälligen Datenschlüssel. Diesen legt SOPS dann mehrfach mit in die Datei (Abschnitt sops: am Ende), jeweils verschlüsselt für einen der Empfänger. Jeder Empfänger entschlüsselt seine Kopie und kommt damit an die Werte.

flowchart LR
    werte["Secrets<br>(Klartext)"]
    dk["Datenschlüssel<br>(zufällig)"]

    subgraph datei ["creds-*.enc.yaml"]
        enc["verschlüsselte Werte"]
        k1["Datenschlüssel<br>für Admin"]
        k2["Datenschlüssel<br>für werner"]
    end

    werte --> enc
    dk --> enc
    dk -->|verschlüsselt für Admin| k1
    dk -->|verschlüsselt für werner| k2

Wer entschlüsseln kann

Empfänger sind:

  • die Admins, also die Personen, die Secrets bearbeiten, mit je einem eigenen Paar auf ihrem Rechner
  • jeder Host; sein Paar leitet syslet aus dem SSH-Schlüssel des Hosts ab, es muss also nicht eigens verteilt werden

Welche Empfänger für welche Dateien gelten, steht in syslet/.sops.yaml, mit einer Regel je Host-Ordner: Die Dateien in syslet/werner/ können die Admins und werner lesen, die in syslet/containerhost/ die Admins und containerhost. Entschlüsselt wird erst auf dem Host, während syslet die Änderungen ausrollt.

Warum je Host getrennt: Wird ein Host kompromittiert, sind nur die Secrets dieses Hosts betroffen, nicht an die des anderen.

Wer neu dazukommt und Secrets bearbeiten soll, trägt den öffentlichen Teil seines Paars in syslet/.sops.yaml ein. Jemand, der die Dateien schon lesen kann, legt den Datenschlüssel dann in allen Dateien zusätzlich für die neue Person ab. Wie das geht, steht in Arbeitsrechner einrichten und Neue Person freischalten; wie jemand wieder ausgetragen wird, in Person austragen.

Mehr zur Verschlüsselung: Secret encryption in der syslet-Doku

Ausnahmen

Terraform-State

Terraform speichert in seinem State unter anderem Secrets, die es in Authentik anlegt. Der State liegt unverschlüsselt im Repo, zum Beispiel apps/authentik/tf-core/terraform.tfstate. Das ist eine bewusste Abwägung, die in Terraform begründet ist.

Zugangsdaten interner Dienste

Die Zugangsdaten von Diensten, die nur innerhalb eines Stacks gebraucht werden, stehen im Klartext in den Stack-Dateien. Das sind vor allem die Datenbanken, zum Beispiel POSTGRES_PASSWORD in syslet/werner/stack-hedgedoc.cue, aber auch Hilfsdienste wie Cache oder Suche, etwa in syslet/werner/stack-nextcloud-aio.cue.

Warum das in Ordnung ist: Jeder solche Stack hat ein eigenes internes Netzwerk (Internal: "true", in der Stack-Datei ganz oben). Die internen Dienste hängen nur an diesem Netzwerk, und daran hängen nur die Container desselben Stacks. Von außen und aus anderen Stacks sind sie nicht erreichbar.

Erreichbar sind sie sonst nur noch direkt auf dem Host, etwa per podman exec … psql. Wer so weit kommt, hat ohnehin Zugriff auf alles und braucht das Passwort nicht mehr. Ein verschlüsseltes Passwort würde hier also keinen zusätzlichen Schutz bringen, aber jede Stack-Datei schwerer lesbar machen.

Startpasswörter

Manche Apps brauchen bei der ersten Einrichtung ein Passwort für ein Admin-Konto, zum Beispiel ADMIN_PASSWORD in der Nextcloud. Es steht im Klartext in der Stack-Datei, gilt aber nur für die Einrichtung und wird danach in der App geändert.

Warum das in Ordnung ist: Die App liest es nur beim allerersten Start; danach gilt das in der App geänderte Passwort, und der Wert in der Stack-Datei öffnet nichts mehr.

Weiterlesen