Merge branch 'phase/release-0.1.0'

This commit is contained in:
2026-10-12 10:02:46 +02:00
4 changed files with 124 additions and 9 deletions
+30 -2
View File
@@ -8,5 +8,33 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
## [Unveröffentlicht] ## [Unveröffentlicht]
### Hinzugefügt ### Hinzugefügt
- Projektplan mit Architektur, Datenmodell, Sicherheitskonzept und Phasenplan (`docs/PLAN.md`)
- Issues für alle fünf Phasen in `docs/issues/` ### Geändert
### Behoben
### Sicherheit
## [0.1.0] – 2026-10-12
Phase 1: Grundgerüst, Anmeldung, Hosts und Bootstrap.
### Hinzugefügt
- Projektplan mit Architektur, Datenmodell, Sicherheitskonzept und Phasenplan (`docs/PLAN.md`), Issues für alle fünf Phasen
- Projektgerüst: Next.js 16, Prisma 7, PostgreSQL 17, pnpm, ESLint/Prettier/Vitest/Playwright, commitlint (#001)
- Design „Tresor“: Design-Tokens, selbst gehostete Schriften, Logo, Favicon und PWA-Icons, Seitenleiste, Grundkomponenten (#002)
- Datenmodell für alle Phasen mit Datenbankregeln (CHECK, partielle Indizes, Trigger) und Seed mit Notfall-Admin und eingebauten Vorlagen „Deployment-Zugang“, „Backup-Zugang“, „Admin-Zugang Person“ (#003)
- Anmeldung per OIDC (authentik) mit Rollen Admin/Operator/Leser aus Gruppen; Notfall-Admin mit Passwort und Pflicht-TOTP; erneute Bestätigung vor kritischen Aktionen (#004)
- Revisionssicheres Protokoll: append-only, Hash-Kette, Prüffunktion (#005)
- OpenSSH-Schlüssel (ed25519, RSA-4096) erzeugen, lesen und schreiben ohne `ssh-keygen`, kompatibel geprüft; Envelope Encryption mit Master-Key; Deployment-Key der Instanz (#006)
- Worker mit pg-boss und Vorgängen mit Fortschrittsanzeige; Dockerfile und Docker Compose für den Betrieb (#007)
- Hosts und Hostgruppen anlegen, bearbeiten, archivieren, filtern; Demo-Daten mit `SSHKV_SEED_DEMO=1` (#008)
- Host-Key-Prüfung nach TOFU: abrufen, Fingerprint bestätigen oder ablehnen, strikte Prüfung bei jeder Verbindung, Warnung bei Änderung (#009)
- Bootstrap neuer Hosts per Einmal-Passwort oder Befehl zum Kopieren, mit Dienstbenutzer `sshkv` und sudoers; sshd-Hinweise (#010)
- SSH-Testziele (Debian 13) und Mock-OIDC für Integrations- und E2E-Tests (#011)
### Sicherheit
- Private Keys nur verschlüsselt in der Datenbank; nur der Worker kann sie öffnen – strukturell getestet
- Ein `pg_dump` enthält keinen Klartext-Schlüssel (Test)
- Bootstrap-Passwort wird nirgends gespeichert (Test); Dateien im Home des Zielbenutzers werden als dieser Benutzer geschrieben (Symlink-Test gegen `/etc/shadow`)
- Content-Security-Policy mit Nonce, Sicherheits-Kopfzeilen, Rate-Limit für den Notfallzugang
+33 -6
View File
@@ -2,29 +2,56 @@
## Zeitübersicht ## Zeitübersicht
**Gesamt:** 0 h 25 min **Gesamt (Wandzeit):** 1 h 25 min
**Summe der Issue-Zeiten:** 4 h 23 min – Sub-Agenten arbeiteten parallel an #002, #006, #008 und #011.
| Issue | Zeit | | Issue | Zeit |
|---|---| |---|---|
| Planung (Phase 0) | 0 h 25 min | | Planung (Phase 0) | 0 h 20 min |
| #001 Projekt-Setup | 0 h 15 min |
| #002 Design-System und Layout | 0 h 18 min |
| #003 Datenmodell und Migrationen | 0 h 10 min |
| #004 Anmeldung, Rollen und Bestätigung | 0 h 45 min |
| #005 Revisionssicheres Protokoll | 0 h 10 min |
| #006 Verschlüsselte Ablage und Schlüsselformat | 0 h 25 min |
| #007 Worker und Vorgänge | 0 h 15 min |
| #008 Hosts und Hostgruppen | 0 h 30 min |
| #009 Host-Key-Prüfung | 0 h 25 min |
| #010 Bootstrap | 0 h 35 min |
| #011 Test-SSH-Container | 0 h 15 min |
| Meilenstein | Zeit | | Meilenstein | Zeit |
|---|---| |---|---|
| v0.1.0 | 1 h 25 min (Wandzeit) | **abgeschlossen**
--- ---
## 2026-10-12 · Sitzung 1 · 08:45–laufend ## 2026-10-12 · Sitzung 1 · 08:45–10:10
**Issue:** Planung, #001 ff. **Issue:** Planung, #001–#011
**Version:** 0.0.0 **Version:** 0.1.0
### Erledigt ### Erledigt
- Auftrag gelesen, Architektur, Datenmodell, Sicherheitskonzept und Phasenplan entworfen (`docs/PLAN.md`) - Auftrag gelesen, Architektur, Datenmodell, Sicherheitskonzept und Phasenplan entworfen (`docs/PLAN.md`)
- Sieben Rückfragen gestellt und beantwortet, Plan freigegeben - Sieben Rückfragen gestellt und beantwortet, Plan freigegeben
- Konventionen aus Laufzettel und ProxLedger übernommen; Git-Repository mit lokaler Identität angelegt - Konventionen aus Laufzettel und ProxLedger übernommen; Git-Repository mit lokaler Identität angelegt
- 30 Issues für Phase 1–5 als Dateien angelegt (Gitea folgt mit #030) - 30 Issues für Phase 1–5 als Dateien angelegt (Gitea folgt mit #030)
- #001 Grundgerüst; #003 Datenmodell mit Datenbankregeln und Protokoll-Hash-Kette als Trigger
- #002, #006 (Kern), #011 parallel von Sub-Agenten in eigenen Worktrees, danach gemergt
- #004 Anmeldung: OIDC gegen Mock-Anbieter, Notfall-Admin mit TOTP, Re-Auth; E2E im gebauten Stand
- #005 Protokoll-Service mit Schwärzen; #006 Teil 2: Versiegeln im Worker, Deployment-Key
- #007 Worker mit pg-boss; vollständiger Docker-Stapel geprüft (migrate, web, worker gesund)
- #008 Hosts und Hostgruppen (Sub-Agent)
- #009 strikte Host-Key-Prüfung und TOFU, #010 Bootstrap – Integrationstests gegen echtes sshd, E2E im Browser bis zur Verbindungsprüfung
- Stand: 287 Unit-/DB-Tests, 17 Integrationstests, 5 E2E-Abläufe grün
### Entscheidungen ### Entscheidungen
- **Kein Auth.js:** v5 steht dauerhaft auf Beta – wie in Laufzettel `openid-client` + `jose`. - **Kein Auth.js:** v5 steht dauerhaft auf Beta – wie in Laufzettel `openid-client` + `jose`.
- **Web und Worker getrennt, Master-Key nur im Worker:** Eine Lücke im Webprozess allein entschlüsselt keine Private Keys. - **Web und Worker getrennt, Master-Key nur im Worker:** Eine Lücke im Webprozess allein entschlüsselt keine Private Keys.
- **Schlüsselerzeugung ohne `ssh-keygen`:** Node `crypto` + eigener OpenSSH-Serializer, damit Private Keys nie als Datei auf dem sshkv-Server liegen. `ssh-keygen` prüft in den Tests die Kompatibilität. - **Schlüsselerzeugung ohne `ssh-keygen`:** Node `crypto` + eigener OpenSSH-Serializer, damit Private Keys nie als Datei auf dem sshkv-Server liegen. `ssh-keygen` prüft in den Tests die Kompatibilität.
- **Dienstbenutzer `sshkv` mit sudo als Standard,** `root` pro Host wählbar: Im auth.log der Hosts ist eindeutig zu sehen, was von sshkv kam, und root-Login kann ganz aus bleiben. - **Dienstbenutzer `sshkv` mit sudo als Standard,** `root` pro Host wählbar.
- **Ohne sshkv-Gruppe kein Zugang** – auch kein Lesezugriff.
- **Protokoll-Hash im Datenbank-Trigger:** Niemand kann eine Zeile ohne Verkettung einfügen; die ID wird erst unter Sperre vergeben, damit die Kette auch bei parallelem Schreiben hält.
- **Ein Bootstrap-Skript für Passwort- und Copy-Paste-Weg,** Dateizugriffe als Zielbenutzer (Symlink-Schutz).
- **Eigene Testdatenbank je parallelem Lauf:** Sub-Agent und Hauptsitzung leerten sich gegenseitig die Tabellen (in `CLAUDE.md` festgehalten).
- **Prisma-Client in den Worker gebündelt:** Im Standalone-Abbild fand die ESM-Auflösung `@prisma/client` sonst nicht (erst im echten Docker-Stapel aufgefallen).
- **Worker-Benutzer in Compose wählbar** (`SSHKV_WORKER_USER`), weil Docker-Secrets ohne Swarm die Rechte der Host-Datei tragen.
+60
View File
@@ -0,0 +1,60 @@
# SSHKeyVault (`sshkv`)
Selbst gehostete Weboberfläche zur zentralen Verwaltung von SSH-Schlüsseln: Schlüsselpaare
erzeugen und verwalten, Public Keys in `authorized_keys` verteilen, Private Keys gezielt auf
andere Hosts ausrollen, Ist- und Soll-Zustand abgleichen – alles revisionssicher protokolliert.
> **Stand 0.1.0 (Phase 1):** Grundgerüst, Anmeldung (OIDC + Notfall-Admin mit TOTP), Hosts und
> Hostgruppen, Host-Key-Prüfung (TOFU) und Bootstrap neuer Hosts. Schlüsselverwaltung und
> Ausrollen folgen in Phase 2 und 3 – siehe [`docs/PLAN.md`](docs/PLAN.md).
## Sicherheit vorab
SSHKeyVault hat **root-Zugriff auf alle verwalteten Hosts**. Deshalb:
- nur im internen Netz bzw. VPN betreiben, mit Reverse Proxy und TLS davor;
- den **Master-Key** (`SSHKV_MASTER_KEY_FILE`) getrennt von der Datenbank sichern – ohne ihn
ist ein Datenbank-Backup wertlos, und mit ihm zusammen gibt es alle Private Keys preis;
- Private Keys liegen nur verschlüsselt in der Datenbank (AES-256-GCM, Envelope Encryption);
entschlüsseln kann ausschließlich der Worker, der Webprozess hat den Master-Key nicht.
Mehr dazu in [`docs/PLAN.md`](docs/PLAN.md), Abschnitt 3.
## Installation (Docker Compose)
```bash
cp .env.example .env # Werte eintragen, siehe Kommentare
openssl rand -base64 48 # → SSHKV_APP_SECRET
mkdir -p geheim && head -c 32 /dev/urandom > geheim/master.key
sudo chown 1001:1001 geheim/master.key && sudo chmod 400 geheim/master.key
docker compose up -d
```
Die Oberfläche lauscht auf `127.0.0.1:3400` (`SSHKV_BIND`, `SSHKV_PORT`). Beim ersten Start legt
der Dienst `migrate` das Schema, den Notfall-Admin (`SSHKV_BREAKGLASS_*`) und die eingebauten
Vorlagen an; der Worker erzeugt den Deployment-Key der Instanz.
**Anmeldung:** über authentik (`SSHKV_OIDC_*`, Rollen über die Gruppen `sshkv-admins`,
`sshkv-operators`, `sshkv-readers`). Der Notfall-Admin richtet bei der ersten Anmeldung TOTP ein.
## Einen Host aufnehmen
1. **Hosts → + Host anlegen** (Name, Adresse, Port; Anmeldung als Dienstbenutzer `sshkv` mit sudo
– empfohlen – oder direkt als root).
2. **Host-Key abrufen**, den Fingerprint am Host vergleichen
(`ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub`) und vertrauen.
3. **Bootstrap:** einmalig per Passwort (wird nicht gespeichert) oder den angezeigten Befehl auf
dem Host einfügen und „Einrichtung prüfen“.
## Entwicklung
Node 24, pnpm 12, Docker. Konventionen und Befehle: [`CLAUDE.md`](CLAUDE.md).
```bash
pnpm install
pnpm db:up && pnpm db:migrate && pnpm db:seed
pnpm test:ziele:up # SSH-Testziele + Mock-OIDC
pnpm build && pnpm start # http://localhost:3400
pnpm worker # zweites Terminal
pnpm test && pnpm test:integration && pnpm test:e2e
```
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "sshkv", "name": "sshkv",
"version": "0.0.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module", "type": "module",
"description": "SSHKeyVault – zentrale Verwaltung von SSH-Schluesseln mit Weboberflaeche", "description": "SSHKeyVault – zentrale Verwaltung von SSH-Schluesseln mit Weboberflaeche",