Commit Graph
15 Commits
Author SHA1 Message Date
florian.wildeandClaude Opus 5.5 4584669962 feat(hosts): Host-Key und Bootstrap in der Oberflaeche (#009, #010)
Auf der Hostseite: Host-Key abrufen, Fingerprint vergleichen und
vertrauen oder ablehnen. Weicht ein Key vom vertrauten ab, erscheint
eine deutliche Warnung, und Annehmen geht nur mit Haekchen und frischer
Bestaetigung. Danach Bootstrap per Einmal-Passwort oder per Befehl und
Verbindungspruefung, jeweils mit Fortschrittsanzeige.

Der sshd-Befund empfiehlt prohibit-password, wenn root sich per
Passwort anmelden darf - nur als Hinweis, geaendert wird nichts.

E2E im gebauten Stand mit Worker gegen ein echtes sshd.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:00:43 +02:00
florian.wilde c87c7b4e43 Merge branch 'phase/010-bootstrap' 2026-10-12 09:52:00 +02:00
florian.wilde 941b38b4ea Merge branch 'phase/008-hosts-gruppen' 2026-10-12 09:51:58 +02:00
florian.wildeandClaude Opus 5.5 4f9d5d9680 feat(hosts): Services, Lesemodelle und Aktionen fuer Hosts (#008)
Hosts und Hostgruppen brauchen eine Schreibschicht, bevor Host-Key-Pruefung
(#009) und Bootstrap (#010) darauf aufsetzen. Die Regeln (Name, Adresse wie
der DB-CHECK, Port, Tags, Anmeldeart) liegen framework-frei in src/lib/hosts.ts,
damit Action (Feldmeldungen) und Service (Schutz fuer Seed und Assistenten)
dieselben pruefen.

Archivieren statt Loeschen haelt Protokollbezuege lesbar; dabei fallen die
Gruppenmitgliedschaften weg, damit Zuweisungen an eine Gruppe nie einen
archivierten Host treffen. Jede Aenderung landet mit den geaenderten Feldern
im Protokoll, Eindeutigkeitsverletzungen kommen als deutsche HostFehler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:42:56 +02:00
florian.wildeandClaude Opus 5.5 17879f4c80 feat(bootstrap): Deployment-Key per Einmal-Passwort oder Befehl (#010)
Ein einziges POSIX-Skript fuer beide Wege: der Worker fuehrt es nach
einer einmaligen Passwortanmeldung aus, und genau dasselbe Skript
steht als Befehl zum Kopieren bereit. So gibt es keine zwei Wahrheiten
darueber, was ein Bootstrap auf dem Host aendert.

Das Passwort reist nur verschluesselt im Job und steht nie in Vorgang,
Protokoll oder Log. Dateien im Home des Zielbenutzers liest und
schreibt das Skript als dieser Benutzer: ein Symlink auf /etc/shadow
fuehrt zum Abbruch statt zur Kopie (Integrationstest). sudoers wird
erst nach visudo -c aktiv. Am Ende beweist eine Anmeldung per
Deployment-Key, dass sshkv ohne Passwort weiterarbeiten kann.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:42:45 +02:00
florian.wildeandClaude Opus 5.5 364c514b04 feat(protokoll): Eintraege vor dem Schreiben schwaerzen (#005)
Details und Diff laufen durch schwaerzen(), damit kein Private Key und
kein Passwort ins unveraenderliche Protokoll geraet - was dort einmal
steht, laesst sich per Trigger nicht mehr entfernen.

Der Wortstamm privat ist dafuer zu breit: er schwaerzte auch Pfade und
Verwendungszaehler, die das Protokoll gerade zeigen soll. Jetzt greifen
nur Felder, die den Schluessel selbst tragen, und jeder PEM-Block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:30:51 +02:00
florian.wilde 4d0044b380 Merge branch 'phase/002-design-layout' 2026-10-12 09:21:46 +02:00
florian.wilde 63a378df8f Merge branch 'phase/006-geheimnisse-schluesselformat' (Kern) 2026-10-12 09:21:41 +02:00
florian.wildeandClaude Opus 5.5 88b5697cc9 feat(ui): Design-Tokens, Schriften und Datumsformate (#002)
Die Farben des Tresor-Designs stehen als CSS-Variablen in :root und
werden per @theme inline an Tailwind gereicht, damit Inline-SVGs und
Utility-Klassen dieselbe Quelle nutzen. Schriften kommen selbst gehostet
aus @fontsource, nur in den benutzten Gewichten und im Latin-Ausschnitt.

src/lib/format.ts rechnet fest in Europe/Berlin: der Server laeuft im
Container auf UTC, die Anzeige soll aber ueberall dieselbe Ortszeit
zeigen. relativ() nimmt den Bezugspunkt als Parameter, damit statische
Seiten und Tests deterministisch bleiben.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:20:22 +02:00
florian.wildeandClaude Opus 5.5 5ef90310e1 feat(auth): Anmeldung per OIDC und Notfall-Admin mit TOTP (#004)
Sitzung, OIDC-Fluss und Rueckruf aus Laufzettel uebernommen und auf
sshkv angepasst. Abweichungen mit Absicht:

- Ohne passende sshkv-Gruppe gibt es keinen Zugang, auch keinen
  Lesezugriff: schon die Hostliste verraet, wer wohin darf.
- Notfall-Admin nur mit TOTP (Freigabe Frage 5). Ein richtiges
  Passwort fuehrt beim ersten Mal nur zur Einrichtung. Jeder Code gilt
  einmal, fuenf Fehlversuche sperren 15 Minuten.
- Kritische Rechte verlangen eine Bestaetigung, die hoechstens fuenf
  Minuten alt ist. Bei OIDC mit prompt=login, max_age=0 und Pruefung
  von auth_time, sonst zaehlte eine laufende SSO-Sitzung als Beweis.
- Sitzungen laufen acht statt zwoelf Stunden.

Die Seiten folgen, sobald das Design-System (#002) steht.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:17:44 +02:00
florian.wildeandClaude Opus 5.5 d359b7d4c3 feat: Geheimnisse vor Log und Protokoll schwaerzen (#006)
Private Keys, Passwoerter und Tokens duerfen nie in Log oder Protokoll
landen (CLAUDE.md). Ein Netz ueber Feldnamen allein reicht nicht: Ein Key
kann in einer Fehlermeldung oder einem Stacktrace stecken, ohne dass das
Feld danach heisst. Deshalb schwaerzt `schwaerzen` sowohl Felder mit
verdaechtigen Namen (irgendwo im Namen, nicht nur am Anfang) als auch
jeden PEM-Private-Key in beliebigen Zeichenketten, auch abgeschnittene.

Der Helfer ist framework-frei und wird mit dem Logger und dem Protokoll
eingebaut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:17:34 +02:00
florian.wildeandClaude Opus 5.5 893b840f06 feat: Envelope Encryption und Master-Key aus Datei (#006)
Private Keys liegen spaeter verschluesselt in PostgreSQL. Ein eigener DEK
je Geheimnis erlaubt eine Master-Key-Rotation, ohne die Chiffrate selbst
anzufassen; das AAD (Schluessel-ID und Fingerprint) bindet jedes Chiffrat
an seinen Datensatz, sodass vertauschte Zeilen beim Oeffnen auffallen
statt einen fremden Key zu liefern. Die Master-Key-Version steckt im AAD
des DEK.

masterKeyLaden akzeptiert roh, Base64 und Hex, weil `openssl rand` der
naheliegende Weg zum Anlegen ist, und bricht ab, wenn Gruppe oder Welt
die Datei lesen duerfen – ein lesbarer Master-Key oeffnet alle Private
Keys. SSHKV_MASTER_KEY_UNSICHER_ERLAUBT=1 erlaubt das bewusst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:16:56 +02:00
florian.wildeandClaude Opus 5.5 6b20566676 feat: OpenSSH-Schluessel erzeugen, schreiben und lesen (#006)
Private Keys sollen nie als Datei auf dem sshkv-Server liegen, auch nicht
kurz. ssh-keygen braucht aber genau das. Deshalb erzeugt Node crypto die
Schluessel (ed25519, RSA-4096), und ein eigener Serializer schreibt das
Format openssh-key-v1 – unverschluesselt oder mit aes256-ctr und
bcrypt_pbkdf (16 Runden, 16 Byte Salt), Checkint und Padding wie OpenSSH.

Der Parser liest auch von ssh-keygen erzeugte Dateien, prueft, dass
Private und Public Key zusammengehoeren, und meldet fehlende oder falsche
Passphrasen ueber eigene Codes. Fehlermeldungen sind feste Saetze ohne
Eingabe darin, damit kein Schluesselmaterial in Logs geraet. RSA unter
2048 Bit wird beim Import abgelehnt; wer nur beobachtet, kann die Grenze
ausschalten.

Die Tests pruefen beide Richtungen gegen das echte ssh-keygen (-y, -lf).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:16:42 +02:00
florian.wildeandClaude Opus 5.5 03c7c45576 feat(datenbank): Datenmodell mit Datenbankregeln und Seed (#003)
Alle Modelle fuer die fuenf Phasen in einer Migration, damit spaetere
Phasen keine Umbauten an Kerntabellen brauchen. Was auch an der
Service-Schicht vorbei gelten muss, steht als CHECK, partieller Index
oder Trigger in der Datenbank: Personen-Keys ohne Private Key, genau
ein Instanz-Deployment-Key, ein vertrauter Host-Key je Algorithmus,
shell-sichere Zielbenutzer und Pfade.

Das Protokoll ist append-only und per Hash-Kette verbunden; den Hash
berechnet ein Trigger, damit keine Zeile ohne Verkettung entstehen
kann (#005 baut darauf auf).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:12:06 +02:00
florian.wildeandClaude Opus 5.5 cef9510f18 chore: Projektgeruest mit Next.js 16, Prisma 7 und pnpm (#001)
Konfiguration aus Laufzettel uebernommen, damit alle Projekte gleich
gebaut und geprueft werden. Entwicklungsdatenbank auf 5436, weil 5433
und 5434 von ProxLedger und Laufzettel belegt sind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:05:15 +02:00