--- created: 2026-08-11 completed: 2026-08-11 title: Verschluesselungsschluessel — fest eingebauter Vorgabewert in der Basis-Compose, Dokumentation in der Beispiel-Umgebung fehlt vermutlich area: infra severity: minor trigger: vor der ersten Installation bei einem externen Kunden; intern folgenlos, solange die Variable gesetzt ist files: - docker-compose.yml:46 - docker-compose.prod.yml:44 - .env.example - .env.prod.example resolution_commits: - 7bda56d - f574884 - 379606e --- ## Problem Seit 4f687ea sind alle Zugangsdaten in der Datenbank verschluesselt — Kalender, SMTP, DKV-Postfach, Ausschreibungs-Postfach und nun auch das LDAP-Bind-Passwort. Der Schluessel dafuer kommt aus der Umgebungsvariable `CALENDAR_ENCRYPTION_KEY` und liegt bewusst NICHT in der Datenbank. Zwei Luecken drumherum: **1. Vorgabewert in der Basis-Compose.** `docker-compose.yml:46` lautete ``` CALENDAR_ENCRYPTION_KEY: ${CALENDAR_ENCRYPTION_KEY:-5dbbaba2...} ``` Ist die Variable nicht gesetzt, startet die Anwendung also trotzdem — mit einem Schluessel, der im Repository steht und damit jedem bekannt ist, der den Code hat. Die Verschluesselung sieht dann vorhanden aus, traegt aber nicht. Ein stiller Fehlstart ist hier schlechter als ein lauter: `docker-compose.prod.yml:44` macht es bereits richtig und verlangt die Variable ohne Vorgabe. **2. Dokumentation.** Ob `.env.example` / `.env.prod.example` den Schluessel erklaeren, konnte ich am 2026-08-11 nicht pruefen (Dateizugriff in der Sitzung blockiert) — beim Umsetzen also zuerst nachsehen. Falls er dort fehlt oder ohne Erklaerung steht, weiss bei einer Neuinstallation niemand, dass ein eigener Wert erzeugt werden muss und was passiert, wenn er spaeter verloren geht. Aufgefallen am 2026-08-11, als der User fragte, wo der Schluessel liegt und ob man ihn exportieren kann. ## Solution 1. Den Vorgabewert aus `docker-compose.yml:46` entfernen, sodass ein Start ohne gesetzten Schluessel scheitert statt still einen bekannten zu verwenden. Vorher pruefen, was das fuer die lokale Entwicklung heisst — moeglicherweise braucht `docker-compose.dev.yml` dann einen eigenen, klar als Wegwerf-Wert gekennzeichneten Eintrag, damit ein frisch geklontes Repo weiterhin hochfaehrt. 2. In beiden Beispiel-Umgebungsdateien den Schluessel dokumentieren: wie er erzeugt wird (`openssl rand -hex 32`), dass er zu jedem Datenbank-Backup gehoert und getrennt davon aufbewahrt werden sollte, und dass ein verlorener Schluessel bedeutet, dass alle gespeicherten Zugangsdaten neu eingetragen werden muessen. Optional im selben Zug, weil es beim Lesen sonst jedes Mal irritiert: der Name `CALENDAR_ENCRYPTION_KEY` stammt daher, dass das Kalender-Modul die Verschluesselung zuerst brauchte. Er gilt laengst fuer alle Zugangsdaten der Plattform. Eine Umbenennung muesste den alten Namen als Rueckfallebene lesen, sonst faellt jede bestehende Installation beim naechsten Start auf die Nase — siehe auch die Notiz dazu im LdapModule. ## Resolution (2026-08-11) **Punkt 1 war beim Abschluss dieses Zettels bereits erledigt** — der Zettel war insoweit veraltet. `7bda56d` hat den Vorgabewert aus beiden Compose-Dateien entfernt; statt eines Ersatzwerts steht dort jetzt ein `:?`-Abbruch, der Stack startet ohne gesetzten Schluessel gar nicht. Der befuerchtete Nebeneffekt fuer die lokale Entwicklung trat nicht ein: es gibt keine `docker-compose.dev.yml`, und die Basis-Compose verlangt den Wert seither aus der lokalen `.env`. **Auch die optionale Umbenennung ist erledigt** — `f574884` hat die Variable in `TESSERA_ENCRYPTION_KEY` umbenannt und liest `CALENDAR_ENCRYPTION_KEY` weiterhin als Rueckfallebene, damit bestehende Installationen nicht beim naechsten Start stehenbleiben. Im selben Zug ist der CryptoService aus dem CalendarModule in ein eigenes globales Modul gewandert. **Punkt 2 schliesst `379606e`.** `.env.example` hatte gar keinen Eintrag, `.env.prod.example` nannte noch den alten Namen ohne Erklaerung. Beide Vorlagen tragen jetzt denselben Block: aktueller Variablenname, Erzeugungsbefehl `openssl rand -hex 32`, der Hinweis dass ein verlorener Wert alle gespeicherten Zugangsdaten unwiederbringlich macht, und dass der Schluessel zu jedem Datenbank-Backup gehoert, aber getrennt davon aufbewahrt werden muss. **Reibung beim Umsetzen, fuer das naechste Mal:** Die Deny-Regeln `Read(.env)` und `Read(.env.*)` in `~/.claude/settings.json` sperren auch die beiden eingecheckten Vorlagendateien, die keinerlei Geheimnisse enthalten. Read und Edit sind darauf nicht benutzbar; die Aenderung lief ueber die Shell. Wer hier wieder arbeitet, faengt sich dieselbe Sperre ein — sie enger zu fassen (echte Geheimnisdateien einzeln statt `.env.*`) wuerde das aufloesen.