Worum es geht
Secrets werden meist nach Umgebung sortiert: ein Bereich für Entwicklung, einer für Produktion. Das passt zu großen Teams mit klaren Stufen. Bei einer kleinen Pipeline aus mehreren Komponenten ist eine andere Ordnung praktischer: nach Projekt. Alle Zugangsdaten, die zu einem Projekt gehören, liegen zusammen, und innerhalb des Projekts gibt es bei Bedarf Umgebungen.
Es gibt selbst gehostete Secrets-Manager, die nach diesem Ansatz arbeiten, ein Beispiel ist Project Vault, so wird es öffentlich beschrieben. Welche Funktionen ein bestimmtes Produkt hat, steht in dessen Dokumentation. Der Artikel erklärt das Ordnungsprinzip, unabhängig vom Werkzeug.
Das Problem verstreuter Secrets
Eine typische Pipeline besteht aus mehreren Komponenten: Collector, Klassifikation, Dispatcher und Heartbeat. Jede braucht eigene Zugangsdaten: einen Bot-Token, einen Modell-Endpunkt, ein Mail-Passwort, ein Webhook-Geheimnis. Liegen sie in verschiedenen Dateien und Verzeichnissen, entstehen Fragen, die im Ernstfall dringend werden:
- Welche Zugangsdaten gehören zusammen?
- Wo liegen alle Kopien eines Tokens?
- Wer hat wann etwas geändert?
Das Speichern selbst löst ein Passwortmanager. Schwierig wird die Rotation, wenn ein Zugang kompromittiert ist und du alle betroffenen Stellen finden musst.
Ordnung nach Projekt
Gruppiere Secrets nach dem Projekt, dem sie dienen, etwa watch-alert, backup oder monitoring. Die Vorteile:
- Zusammenhang: Was zusammen rotiert werden muss, liegt zusammen.
- Berechtigungen: Eine Komponente bekommt nur Zugriff auf das eigene Projekt.
- Übersicht: Im Ernstfall genügt ein Blick auf ein Projekt statt einer Suche über das ganze System.
Umgebungen (Test, Produktion) sind dann eine Unterebene des Projekts und nicht die oberste Ordnung.
Ein einfacher Loader
Auch ohne eigenen Secrets-Manager lässt sich das Prinzip mit Dateien umsetzen: ein Verzeichnis pro Projekt, darin je Secret eine Datei mit strengen Rechten.
from pathlib import Path
BASE = Path("/etc/secrets")
def load_project(project: str) -> dict[str, str]:
directory = BASE / project
result: dict[str, str] = {}
for path in sorted(directory.iterdir()):
if path.is_file():
result[path.name] = path.read_text(encoding="utf-8").strip()
return result
# Beispiel
# secrets = load_project("watch-alert")
# token = secrets["dispatcher_token"]
Die Verzeichnisse gehören dem jeweiligen Dienstbenutzer mit den Rechten 700, die Dateien 600. So sieht eine Komponente nur ihr Projekt.
Audit-Spur
Bei einem Vorfall willst du wissen, wann ein Secret zuletzt geändert wurde und welche Version aktiv war. Fertige Secrets-Manager protokollieren das. Mit Dateien kannst du eine einfache Variante bauen: ein Änderungsprotokoll, in das jedes Ändern einen Eintrag mit Zeit und Name des Secrets schreibt, ohne den Wert. Werte gehören nie ins Protokoll.
Kurzlebige Tokens und das Bootstrap-Problem
Systemdienste und Cron-Jobs können sich mit einem kurzlebigen Token beim Secrets-Manager anmelden und holen damit ihre Zugangsdaten. Das Token selbst muss irgendwo herkommen: das klassische Henne-Ei-Problem. Üblich ist ein einzelnes, eng begrenztes Start-Token, das beim Deployment auf den Rechner kommt und nur zum Abrufen der eigenen Secrets berechtigt. Alles andere wird von dort geladen.
Rotation eines ganzen Projekts
Ist ein Zugang kompromittiert, ist die Ordnung nach Projekt der Vorteil: Du rotierst alle Secrets des Projekts nacheinander, nach einer festen Checkliste. Wie du dabei Ausfälle vermeidest, beschreibt der Artikel zur Secret-Rotation ohne Downtime.
Wann sich das lohnt (und wann nicht)
Die Ordnung nach Projekt lohnt sich, sobald mehrere Komponenten oder mehrere Projekte auf einem System laufen und die Zugangsdaten sonst über viele Dateien verteilt wären. Sie hilft besonders im Notfall.
Für ein einzelnes Skript mit einem Token ist ein eigener Secrets-Manager zu viel. Eine Umgebungsdatei mit strengen Rechten reicht. Auch die Ordnung nach Umgebung ist völlig in Ordnung, solange du ohne Mühe herausfindest, welche Secrets zu welcher Komponente gehören.