From cd83542482ac01165afb03c2d93d66c35eb5655a Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Mon, 12 Oct 2026 09:02:42 +0200 Subject: [PATCH] docs: Projektplan, Issues und Konventionen anlegen Plan nach Freigabe vom 12.10.2026 mit den Antworten auf die sieben Rueckfragen. Issues fuer alle fuenf Phasen als Dateien, bis der Gitea-Zugang da ist (#030). Co-Authored-By: Claude Opus 5.5 --- .gitignore | 32 ++ .nvmrc | 1 + CHANGELOG.md | 12 + DEVLOG.md | 30 ++ docs/PLAN.md | 389 ++++++++++++++++++ docs/issues/001-projekt-setup.md | 24 ++ docs/issues/002-design-layout.md | 25 ++ docs/issues/003-datenmodell.md | 23 ++ docs/issues/004-anmeldung-rollen.md | 28 ++ docs/issues/005-protokoll.md | 23 ++ .../006-geheimnisse-schluesselformat.md | 26 ++ docs/issues/007-worker-vorgaenge.md | 22 + docs/issues/008-hosts-gruppen.md | 22 + docs/issues/009-host-key-pruefung.md | 23 ++ docs/issues/010-bootstrap.md | 24 ++ docs/issues/011-test-ssh-container.md | 21 + docs/issues/012-schluessel-verwalten.md | 23 ++ docs/issues/013-personen-selbstauskunft.md | 20 + docs/issues/014-private-key-anzeigen.md | 20 + docs/issues/015-authorized-keys-parser.md | 22 + docs/issues/016-public-key-ausrollen.md | 21 + docs/issues/017-private-key-ausrollen.md | 21 + docs/issues/018-benutzer-sudoers.md | 20 + docs/issues/019-vorlagen.md | 19 + docs/issues/020-host-zu-host-assistent.md | 21 + docs/issues/021-abgleich-drift.md | 20 + docs/issues/022-import.md | 19 + docs/issues/023-rotation.md | 19 + docs/issues/024-widerruf.md | 19 + docs/issues/025-zuweisungsmatrix.md | 19 + docs/issues/026-uebersicht.md | 19 + docs/issues/027-protokoll-ansicht.md | 19 + docs/issues/028-benachrichtigungen.md | 19 + docs/issues/029-handbuch.md | 19 + docs/issues/030-gitea-abgleich.md | 19 + 35 files changed, 1103 insertions(+) create mode 100644 .gitignore create mode 100644 .nvmrc create mode 100644 CHANGELOG.md create mode 100644 DEVLOG.md create mode 100644 docs/PLAN.md create mode 100644 docs/issues/001-projekt-setup.md create mode 100644 docs/issues/002-design-layout.md create mode 100644 docs/issues/003-datenmodell.md create mode 100644 docs/issues/004-anmeldung-rollen.md create mode 100644 docs/issues/005-protokoll.md create mode 100644 docs/issues/006-geheimnisse-schluesselformat.md create mode 100644 docs/issues/007-worker-vorgaenge.md create mode 100644 docs/issues/008-hosts-gruppen.md create mode 100644 docs/issues/009-host-key-pruefung.md create mode 100644 docs/issues/010-bootstrap.md create mode 100644 docs/issues/011-test-ssh-container.md create mode 100644 docs/issues/012-schluessel-verwalten.md create mode 100644 docs/issues/013-personen-selbstauskunft.md create mode 100644 docs/issues/014-private-key-anzeigen.md create mode 100644 docs/issues/015-authorized-keys-parser.md create mode 100644 docs/issues/016-public-key-ausrollen.md create mode 100644 docs/issues/017-private-key-ausrollen.md create mode 100644 docs/issues/018-benutzer-sudoers.md create mode 100644 docs/issues/019-vorlagen.md create mode 100644 docs/issues/020-host-zu-host-assistent.md create mode 100644 docs/issues/021-abgleich-drift.md create mode 100644 docs/issues/022-import.md create mode 100644 docs/issues/023-rotation.md create mode 100644 docs/issues/024-widerruf.md create mode 100644 docs/issues/025-zuweisungsmatrix.md create mode 100644 docs/issues/026-uebersicht.md create mode 100644 docs/issues/027-protokoll-ansicht.md create mode 100644 docs/issues/028-benachrichtigungen.md create mode 100644 docs/issues/029-handbuch.md create mode 100644 docs/issues/030-gitea-abgleich.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d2191d --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Abhaengigkeiten +node_modules/ +.pnpm-store/ + +# Next.js / Build +.next/ +out/ +build/ +dist/ +*.tsbuildinfo +next-env.d.ts + +# Prisma-Client (wird erzeugt) +src/generated/ + +# Umgebung und Geheimnisse – niemals einchecken +.env +.env.* +!.env.example +geheim/ +*.key +master.key + +# Tests +coverage/ +test-results/ +playwright-report/ + +# Editor / System +.DS_Store +.vscode/* +!.vscode/extensions.json diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3b22adb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +Alle nennenswerten Änderungen an SSHKeyVault werden in dieser Datei dokumentiert. + +Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), +und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/). + +## [Unveröffentlicht] + +### Hinzugefügt +- Projektplan mit Architektur, Datenmodell, Sicherheitskonzept und Phasenplan (`docs/PLAN.md`) +- Issues für alle fünf Phasen in `docs/issues/` diff --git a/DEVLOG.md b/DEVLOG.md new file mode 100644 index 0000000..23ba281 --- /dev/null +++ b/DEVLOG.md @@ -0,0 +1,30 @@ +# DEVLOG – SSHKeyVault + +## Zeitübersicht + +**Gesamt:** 0 h 25 min + +| Issue | Zeit | +|---|---| +| Planung (Phase 0) | 0 h 25 min | + +| Meilenstein | Zeit | +|---|---| + +--- + +## 2026-10-12 · Sitzung 1 · 08:45–laufend +**Issue:** Planung, #001 ff. +**Version:** 0.0.0 + +### Erledigt +- Auftrag gelesen, Architektur, Datenmodell, Sicherheitskonzept und Phasenplan entworfen (`docs/PLAN.md`) +- Sieben Rückfragen gestellt und beantwortet, Plan freigegeben +- 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) + +### Entscheidungen +- **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. +- **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. diff --git a/docs/PLAN.md b/docs/PLAN.md new file mode 100644 index 0000000..e69f5be --- /dev/null +++ b/docs/PLAN.md @@ -0,0 +1,389 @@ +# SSHKeyVault (`sshkv`) – Planung + +Stand: 12.10.2026 · Status: **freigegeben** (12.10.2026) + +--- + +## 1. Architektur + +``` + internes Netz / VPN + │ HTTPS (Reverse Proxy, z. B. Traefik/Caddy – nicht Teil von sshkv) +┌───────────────────────▼────────────────────────┐ +│ sshkv-web (Next.js 16, App Router, Node 24) │ +│ - UI (Server Components, Tailwind v4) │ +│ - OIDC authentik + Notfall-Admin │ +│ - Server Actions / Route Handlers │ +│ - schreibt Jobs in pg-boss, liest Ergebnisse │ +│ - KEINE SSH-Verbindungen, KEIN Master-Key* │ +└───────────────┬────────────────────────────────┘ + │ PostgreSQL 16 (Prisma + pg-boss-Schema) +┌───────────────▼────────────────────────────────┐ +│ sshkv-worker (gleiches Image, anderer Start) │ +│ - pg-boss-Consumer │ +│ - Master-Key (Datei, read-only gemountet) │ +│ - ssh2: Verbindungen zu Hosts, strikt geprüft │ +│ - Schlüsselerzeugung, Ver-/Entschlüsselung │ +│ - Scheduler: Abgleich, Ablaufwarnungen │ +└───────────────┬────────────────────────────────┘ + │ SSH (nur Deployment-Key) + ┌──────▼──────┐ ┌─────────────┐ + │ srv-pve01 │ │ srv-app01 … │ + └─────────────┘ └─────────────┘ +``` + +\* Ausnahme „Private Key anzeigen/exportieren“: Der Web-Prozess fordert die Entschlüsselung als Job an und reicht das Ergebnis genau einmal an den Browser durch (kein Caching, kein Logging). Alternative: Master-Key auch im Web-Container – bewusst **nicht** vorgesehen, damit eine Lücke im Webprozess allein nicht reicht, um alle Private Keys zu entschlüsseln. + +**Begründung Trennung Web/Worker:** Lange SSH-Vorgänge laufen nie im Request; nur der Worker hält Master-Key und Deployment-Key. Der Web-Container hat keinen Netzwerkpfad zu den Hosts nötig (per Docker-Netz trennbar). + +### Bausteine + +| Bereich | Wahl | Bemerkung | +|---|---|---| +| Framework | Next.js 16 (App Router), TypeScript strict | `output: "standalone"` für schlankes Image | +| DB | PostgreSQL 17 + Prisma 7 (adapter-pg) | Migrationen versioniert im Repo | +| Jobs | pg-boss 10 | kein Redis nötig; Cron-Schedules eingebaut | +| Auth | `openid-client` (OIDC, PKCE) + `jose` (Sitzungs-Cookie); Notfall-Admin mit argon2id + TOTP | kein Auth.js: v5 ist dauerhaft Beta (wie in Laufzettel/ProxLedger) | +| SSH | `ssh2` | `hostVerifier` gegen gespeicherten Host-Key | +| Schlüssel | Node `crypto` (ed25519, RSA-4096) + eigener OpenSSH-Serializer | kein `ssh-keygen` mit Temp-Dateien; Tests prüfen Kompatibilität gegen `ssh-keygen -y` | +| Krypto at rest | AES-256-GCM (Node `crypto`), Envelope Encryption | keine nativen Zusatz-Abhängigkeiten | +| UI | Tailwind v4 mit Design-Tokens als CSS-Variablen, `@fontsource` | keine externen CDNs | +| Validierung | zod | an allen Server-Grenzen | +| Tests | Vitest (Unit), Testcontainers + Debian-sshd-Image (Integration), Playwright (E2E + Screenshots) | | +| Betrieb | Docker Compose: `sshkv-web`, `sshkv-worker`, `sshkv-db` | `.env.example` mit Präfix `SSHKV_` | + +### Remote-Operationen + +Alle Änderungen auf Hosts laufen über **ein kleines POSIX-sh-Skript**, das per `exec` mit Inhalt über stdin gestartet wird (keine Dateien auf dem sshkv-Server, keine Abhängigkeit von SFTP-Rechten): + +1. Ziel-Benutzer prüfen (`getent passwd`), Home ermitteln, `AuthorizedKeysFile` per `sshd -T` auslesen (abweichende Pfade werden erkannt). +2. Dateioperationen in fremden Home-Verzeichnissen **als Zielbenutzer** (`runuser -u --`), damit Symlink-Tricks im Home eines Benutzers keine Root-Schreibrechte erlangen. +3. Backup `authorized_keys.sshkv.bak.` (Aufbewahrung: letzte N, konfigurierbar). +4. Schreiben in `…/.sshkv.tmp.` + `chmod 0600` + `mv` (atomar), `~/.ssh` 0700. +5. Rückgabe von Vorher-Hash und Nachher-Inhalt → Audit-Diff. +6. Optimistische Sperre: Der Dry-Run merkt sich den SHA-256 der Datei; hat sich die Datei bis zur Ausführung geändert, bricht der Job ab und fordert einen neuen Dry-Run. + +Private Keys werden ausschließlich über stdin derselben SSH-Verbindung übertragen. + +### authorized_keys – verwalteter Block + +``` +# BEGIN SSHKV MANAGED – nicht manuell bearbeiten +restrict,from="10.0.0.12" ssh-ed25519 AAAA… sshkv:k_8f3a backup-srv-backup01 +# END SSHKV MANAGED +``` + +- Parser (reine Funktion, umfangreich getestet): Optionen mit Anführungszeichen/Escapes/Kommas, Kommentare, Leerzeilen, CRLF, BOM, doppelte Keys, `cert-authority`, unbekannte Schlüsseltypen, kaputte Zeilen, fehlendes END-Marker, mehrere Blöcke. +- Merge: Alles außerhalb des Blocks bleibt bytegenau erhalten (inkl. Zeilenenden). Innerhalb wird der Block deterministisch aus dem Soll-Zustand neu erzeugt. +- Erkennung eines verwalteten Keys über Fingerprint (nicht über Kommentar); Kommentar `sshkv: ` dient nur der Lesbarkeit. +- Duplikat außerhalb des Blocks → Hinweis im Dry-Run, keine automatische Löschung. +- **Aussperrschutz:** Der Deployment-Key ist in jedem `root`- bzw. Dienstbenutzer-Block unabänderlich enthalten; Aktionen, die ihn entfernen würden, werden serverseitig verweigert. + +--- + +## 2. Datenmodell (Prisma, verkürzt) + +> Umgesetzt mit **deutschen Bezeichnern** (Konvention, siehe `CLAUDE.md`); die Zuordnung steht dort. Die Skizze unten zeigt die Struktur. + +```prisma +enum Role { ADMIN OPERATOR READER } +enum KeyType { ED25519 RSA4096 } +enum KeyCategory { PERSON SERVICE DEPLOYMENT } +enum KeyStatus { ACTIVE ROTATING REVOKED EXPIRED } +enum HostKeyStatus { PENDING TRUSTED CHANGED REJECTED } +enum UsageState { PLANNED DEPLOYED DRIFT REMOVING REMOVED FAILED } + +model User { // Anmeldekonten der Oberfläche + id String @id @default(cuid()) + oidcSub String? @unique + email String? @unique + name String + role Role + isBreakGlass Boolean @default(false) // lokaler Notfall-Admin + passwordHash String? // nur Notfall-Admin (argon2id) + totpSecretEnc Bytes? // nur Notfall-Admin, verschlüsselt + lastLoginAt DateTime? + person Person? +} + +model Person { // Menschen, denen Personen-Keys gehören + id String @id @default(cuid()) + name String + email String? + organisation String? // z. B. Dienstleister + userId String? @unique // Verknüpfung für Self-Service + keys SshKey[] +} + +model Customer { id String @id @default(cuid()); name String @unique; hosts Host[] } + +model Host { + id String @id @default(cuid()) + name String @unique // srv-pve01 + address String // Hostname/IP + port Int @default(22) + description String? + tags String[] + customerId String? + connectUser String @default("root") // siehe offene Frage 1 + deploymentKeyId String? // null = Instanz-Default + bootstrapState String // NEW | BOOTSTRAPPED + reachable Boolean? + lastCheckAt DateTime? + lastSyncAt DateTime? + driftState String? // IN_SYNC | DRIFT | UNKNOWN + sshdFacts Json? // PermitRootLogin, PasswordAuthentication, AuthorizedKeysFile + groups HostGroupMember[] + hostKeys HostKey[] +} + +model HostGroup { + id String @id @default(cuid()) + name String @unique + description String? + deploymentKeyId String? // optional Deployment-Key je Gruppe (später) + members HostGroupMember[] +} +model HostGroupMember { hostId String; groupId String; @@id([hostId, groupId]) } + +model HostKey { // TOFU + id String @id @default(cuid()) + hostId String + algorithm String // ssh-ed25519, ecdsa-…, rsa-sha2-512 + publicKey String + fingerprint String // SHA256:… + status HostKeyStatus + firstSeenAt DateTime + confirmedById String? + confirmedAt DateTime? +} + +model SshKey { + id String @id @default(cuid()) + name String // Zweck, z. B. „Backup srv-backup01 → srv-pve01“ + slug String @unique // für Dateinamen: backup-srv-backup01-srv-pve01 + type KeyType + category KeyCategory + status KeyStatus + publicKey String // OpenSSH-Zeile ohne Optionen + fingerprint String @unique // SHA256 + comment String // Default: sshkv: + hasPrivateKey Boolean + privateKeyEnc Bytes? // Envelope-Ciphertext, nie in Listen-Queries selektiert + dekEnc Bytes? // verschlüsselter Data-Key + masterKeyVersion Int? + passphraseProtected Boolean @default(false) + personId String? + templateId String? + tags String[] + expiresAt DateTime? + createdAt DateTime @default(now()) + lastUsedAt DateTime? // aus Verbindungstests/Abgleich + rotatedFromId String? // Rotationskette + publicUsages PublicKeyUsage[] + privateUsages PrivateKeyUsage[] +} + +model PublicKeyUsage { // Soll: Public Key in authorized_keys + id String @id @default(cuid()) + keyId String + hostId String? // genau eins von hostId / groupId + groupId String? + targetUser String // root, deploy … + options String? // restrict,from="…",command="…" + state UsageState + lastAppliedAt DateTime? +} + +model PrivateKeyUsage { // Soll: Private-Key-Datei auf Host + id String @id @default(cuid()) + keyId String + hostId String + targetUser String + path String // ~/.ssh/id_ed25519_ + sshConfigBlock Json? // { alias, hostName, user, port, identitiesOnly } + knownHostsFor String[] // Host-IDs, deren Host-Key in known_hosts gepflegt wird + state UsageState + onRevoke String @default("DELETE") // DELETE | RENAME + lastAppliedAt DateTime? +} + +model ManagedAccount { // vom sshkv angelegte Benutzer (deploy) und sudoers-Dateien + id String @id @default(cuid()) + hostId String + username String + createdBySshkv Boolean + sudoersFile String? // /etc/sudoers.d/sshkv-deploy + sudoersContent String? +} + +model Template { // „Deployment-Zugang“, „Backup-Zugang“, … + id String @id @default(cuid()) + name String @unique + builtin Boolean @default(false) + keyType KeyType + category KeyCategory + defaultTtlDays Int? + targetUser String + createTargetUser Boolean + sudoMode String? // NONE | COMMANDS | FULL + sudoCommands String[] + authorizedKeysOptions String? // mit Platzhaltern, z. B. from="{{sourceHostIp}}" + sshConfigAlias String? // z. B. deploy-{{targetHost}} +} + +model Observation { // Ist-Zustand aus dem letzten Abgleich + id String @id @default(cuid()) + hostId String + targetUser String + file String + sha256 String + entries Json // geparste Zeilen inkl. inBlock/fingerprint/options + observedAt DateTime +} + +model DriftFinding { + id String @id @default(cuid()) + hostId String + targetUser String + kind String // UNKNOWN_KEY | MISSING_KEY | EXTRA_IN_BLOCK | FILE_CHANGED | PERMS | HOSTKEY_CHANGED + fingerprint String? + detail Json + openedAt DateTime + resolvedAt DateTime? +} + +model Operation { // fachliche Klammer um pg-boss-Jobs (für UI-Status) + id String @id @default(cuid()) + kind String // DRY_RUN | APPLY | BOOTSTRAP | SYNC | TEST | ROTATE | REVOKE … + status String // QUEUED | RUNNING | AWAITING_CONFIRM | DONE | FAILED + requestedById String + plan Json? // Dry-Run-Ergebnis inkl. Datei-Hashes + result Json? + createdAt DateTime @default(now()) + finishedAt DateTime? +} + +model AuditEvent { // append-only (DB-Trigger verbietet UPDATE/DELETE) + id BigInt @id @default(autoincrement()) + at DateTime @default(now()) + actorId String? // null = System/Scheduler + actorName String + action String // key.create, key.private.view, usage.apply, host.hostkey.confirm … + hostName String? + targetUser String? + fingerprint String? + diff String? // Vorher/Nachher (nur Public-Daten) + result String // SUCCESS | FAILURE | DENIED + detail Json? + prevHash String // Hash-Kette → Manipulation erkennbar + hash String +} + +model Setting { key String @id; value Json } +``` + +Hinweise: +- `privateKeyEnc`/`dekEnc` liegen technisch in `SshKey`, werden aber nur über ein einziges Modul (`lib/secrets`) im Worker gelesen; Prisma-Queries nutzen sonst feste `select`-Listen ohne diese Felder (per Lint-Regel/Test abgesichert). +- Der Deployment-Key ist ein `SshKey` mit `category = DEPLOYMENT`. + +--- + +## 3. Sicherheitskonzept (Kurzfassung, Langfassung in `docs/security.md`) + +- **Envelope Encryption:** pro Private Key ein zufälliger 256-bit-DEK, Key mit AES-256-GCM verschlüsselt (AAD = Key-ID + Fingerprint), DEK mit Master-Key (AES-256-GCM) verschlüsselt. Master-Key: 32 Byte aus Datei `SSHKV_MASTER_KEY_FILE` (Docker Secret), nur im Worker. `masterKeyVersion` erlaubt Master-Key-Rotation (CLI-Kommando `sshkv rewrap`). +- **Re-Auth** für kritische Aktionen: OIDC mit `prompt=login` + `max_age=0`, Prüfung von `auth_time` (≤ 5 min); Notfall-Admin: Passwort + TOTP erneut. +- **Web:** Sitzungs-Cookie (jose, HS256) (httpOnly, Secure, SameSite=Lax), CSRF: Server Actions (Origin-Prüfung) + Token für Route Handler, Rate-Limit (Postgres-basiert) auf Login/Re-Auth/kritische Aktionen, CSP mit Nonce, HSTS, `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer`. +- **Logging:** strukturierter Logger mit Redaction (`privateKey`, `password`, `passphrase`, `-----BEGIN`); Test, der bewusst einen Key durch Fehlerpfade schickt und Logs prüft. +- **Bootstrap-Passwort:** nur im Arbeitsspeicher des Workers für genau einen Job, nie in DB/Job-Payload im Klartext → wird vom Web-Prozess mit einem kurzlebigen Job-Schlüssel verschlüsselt, Job-Datensatz wird nach Abschluss gelöscht. +- **Bedrohungsanalyse** (Doku): Kompromittierung sshkv = root auf allen Hosts. Gegenmaßnahmen: nur intern/VPN, Worker ohne eingehende Ports, Master-Key getrennt, Audit mit Hash-Kette + Syslog-Export, Notfallplan „Deployment-Key auf allen Hosts tauschen“ (Rotation des Deployment-Keys als eigener Ablauf), optional `from=""` am Deployment-Key. +- **Backup:** `pg_dump` + Master-Key getrennt sichern; Doku mit Restore-Test. + +--- + +## 4. Rollen & Rechte + +| Aktion | Admin | Operator | Leser | +|---|:-:|:-:|:-:| +| Ansehen (Hosts, Keys, Matrix, Audit) | ✓ | ✓ | ✓ | +| Hosts anlegen/bootstrappen, Host-Key bestätigen | ✓ | ✓ | – | +| Public Key zuweisen/ausrollen/entfernen | ✓ | ✓ | – | +| Schlüssel erzeugen/importieren | ✓ | ✓ | – | +| Private Key ausrollen | ✓ | – | – | +| Private Key anzeigen/exportieren (Re-Auth) | ✓ | – | – | +| Deployment-Key ändern/rotieren (Re-Auth) | ✓ | – | – | +| Massenwiderruf (Re-Auth) | ✓ | – | – | +| Benutzer anlegen + sudoers (Re-Auth) | ✓ | – | – | +| Einstellungen, Vorlagen | ✓ | – | – | +| Eigene Public Keys hinterlegen (Self-Service) | ✓ | ✓ | ✓ | + +Rollenzuordnung über authentik-Gruppen: `sshkv-admins`, `sshkv-operators`, `sshkv-readers` (Namen konfigurierbar per `SSHKV_OIDC_GROUP_*`). + +--- + +## 5. Phasenplan + +Versionierung: `0.N.0` am Ende jeder Phase, `1.0.0` nach Phase 5 + Abnahme. + +### Phase 0 – Planung (jetzt) +Plan, Freigabe, Repo + Gitea-Issues. + +### Phase 1 – Grundgerüst → `v0.1.0` +- Repo, Next.js, Prisma-Schema, Docker Compose (web/worker/db), `.env.example`, CI-Basis (Lint, Typecheck, Tests). +- Design-System: Tokens, Schriften, Logo-Dateien + Favicon/PWA-Icons, Layout mit Seitenleiste. +- Auth: OIDC authentik, Notfall-Admin (argon2id + TOTP), Rollen, Re-Auth-Mechanik. +- Audit-Grundlage (append-only, Hash-Kette). +- Master-Key-Handling + `lib/secrets`. +- Deployment-Key erzeugen. +- Hosts/Hostgruppen CRUD, TOFU-Host-Key-Verifizierung, Erreichbarkeitsprüfung. +- Bootstrap: Einmal-Passwort-Login **oder** Copy-Paste-Befehl; sshd-Check (PermitRootLogin, PasswordAuthentication). +- Test-SSH-Container (Debian 12/13) für Integrationstests. + +### Phase 2 – Schlüssel & Public-Key-Ausrollen → `v0.2.0` +- Erzeugen (ed25519, rsa-4096, optional Passphrase), Import (Public/Paar), Export. +- Personen, Kategorien, Tags, Ablaufdatum. +- Private Key anzeigen/Download mit Re-Auth + Audit. +- `authorized_keys`-Parser/Merger (Unit-Tests mit allen Randfällen). +- Zuweisungen (Host/Gruppe × Benutzer, Optionen), Dry-Run-Diff, Backup, atomares Schreiben, Aussperrschutz. + +### Phase 3 – Private-Key-Ausrollen & Host-zu-Host → `v0.3.0` +- Private Key auf Host A, `~/.ssh/config`-Block (verwaltet, eigener Marker), `known_hosts`-Pflege. +- Benutzer anlegen + sudoers (Vorschau, `visudo -c`). +- Vorlagen (eingebaut „Deployment-Zugang“, „Backup-Zugang“, „Admin-Zugang Person“; eigene in Einstellungen). +- Host-zu-Host-Assistent inkl. Verbindungstest A → B. +- Option „Private Key nach Ausrollen löschen“. + +### Phase 4 – Abgleich, Import, Rotation, Widerruf → `v0.4.0` +- Periodischer + manueller Abgleich, Drift-Befunde, Host-Status. +- Import bestehender `authorized_keys` (Zuordnung zu Person/Zweck). +- Geführte Rotation, Ein-Klick-Widerruf (inkl. Benutzer/sudoers entfernen), Massenwiderruf. +- Zuweisungsmatrix. + +### Phase 5 – Dashboard & Feinschliff → `v0.5.0` / `v1.0.0` +- Übersicht laut Design, Audit-Ansicht + CSV-Export, Syslog-Weiterleitung. +- Benachrichtigungen (SMTP M365 / Webhook) bei Drift und Ablauf. +- Anleitung mit Screenshots (Installation, Bootstrap, Kern-Anwendungsfall), Bedrohungsanalyse, Backup-Konzept. + +### Später (eigene Phasen) +- Heller Modus, SSH-CA (Zertifikate), Deployment-Key je Hostgruppe (Datenmodell ist vorbereitet). + +--- + +## 6. Arbeitsweise + +- Gitea: pro Phase ein Milestone, Issues je Arbeitspaket, Commits referenzieren Issues (`refs #12`), Conventional Commits. +- `devlog.md` (Datum, Dauer, Tätigkeit), `CHANGELOG.md` (Keep a Changelog), SemVer-Tags. +- Sub-Agenten parallel für Tests, Doku, UI-Komponenten. +- Nach jeder Phase Screenshots (Playwright) und kurzer Bericht, Freigabe der nächsten Phase. + +--- + +## 7. Entscheidungen aus der Freigabe (12.10.2026) + +1. Anmeldung auf den Hosts nur per Schlüssel. Standard: Dienstbenutzer `sshkv` mit `/etc/sudoers.d/sshkv` (NOPASSWD); `root` direkt ist pro Host wählbar. +2. Gitea-Zugang folgt; bis dahin liegen die Issues als Dateien in `docs/issues/` (#030). +3. Private Keys ausrollen: **nur Admin**. +4. Self-Service: Jeder angemeldete Benutzer darf eigene Public Keys hinterlegen; wirksam erst nach Zuweisung durch Operator/Admin. +5. Notfall-Admin: Passwort + verpflichtendes TOTP. +6. Passphrasen werden nie gespeichert; beim Ausrollen eines passphrasengeschützten Keys erscheint eine Warnung. +7. authentik vorerst über einen Mock-OIDC-Anbieter; echte Anbindung später. diff --git a/docs/issues/001-projekt-setup.md b/docs/issues/001-projekt-setup.md new file mode 100644 index 0000000..6715883 --- /dev/null +++ b/docs/issues/001-projekt-setup.md @@ -0,0 +1,24 @@ +# #001 Projekt-Setup +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** infrastruktur +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Lauffähiges Grundgerüst: Next.js 16, TypeScript strict, Prisma 7, pnpm, ESLint/Prettier/Vitest, Commit-Prüfung, Docker-Compose für die Entwicklungsdatenbank. + +## Aufgaben +- [ ] Next.js 16 (App Router), React 19, TypeScript strict +- [ ] pnpm 12, gepinnte Versionen, `.nvmrc` +- [ ] ESLint 9, Prettier, Vitest, commitlint + husky +- [ ] `docker-compose.dev.yml` mit PostgreSQL +- [ ] `.env.example` mit Präfix `SSHKV_` +- [ ] `CLAUDE.md` mit Konventionen + +## Akzeptanzkriterien +- [ ] `pnpm lint && pnpm typecheck && pnpm test && pnpm build` grün + +## Abhängigkeiten +– + +## Notizen / Entscheidungen diff --git a/docs/issues/002-design-layout.md b/docs/issues/002-design-layout.md new file mode 100644 index 0000000..7768dfe --- /dev/null +++ b/docs/issues/002-design-layout.md @@ -0,0 +1,25 @@ +# #002 Design-System und Layout +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** ui +**Geschätzt:** 4 h **Tatsächlich:** – + +## Ziel +Design laut Vorgabe (Tresor, Graphit + Messing): Tokens, Schriften, Logo, Favicon/PWA-Icons, Grundlayout mit Seitenleiste. + +## Aufgaben +- [ ] Design-Tokens als CSS-Variablen + Tailwind-Theme +- [ ] Space Grotesk, IBM Plex Sans, JetBrains Mono selbst gehostet (`@fontsource`) +- [ ] `public/brand/` mit `logo.svg`, `logo-light.svg`, `logo-mono.svg`, `favicon.svg`; PNG-Icons + Manifest per Skript +- [ ] Seitenleiste (Logo, Navigation, Benutzer unten), einklappbar auf schmalen Bildschirmen +- [ ] Grundkomponenten: Karte, Button (primär/sekundär/Gefahr), Badge, Statuspunkt, Tabelle, Diff-Box, Kennzahl-Kachel + +## Akzeptanzkriterien +- [ ] Kontrast ≥ 4,5:1, Fokusrahmen in Messing +- [ ] Kein horizontales Scrollen bei 390 px +- [ ] Klickflächen ≥ 44 px + +## Abhängigkeiten +#001 + +## Notizen / Entscheidungen diff --git a/docs/issues/003-datenmodell.md b/docs/issues/003-datenmodell.md new file mode 100644 index 0000000..a771480 --- /dev/null +++ b/docs/issues/003-datenmodell.md @@ -0,0 +1,23 @@ +# #003 Datenmodell und Migrationen +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** datenbank +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Prisma-Schema für alle Phasen (Hosts, Gruppen, Personen, Schlüssel, Verwendungen, Host-Keys, Vorgänge, Protokoll), erste Migration, Seed. + +## Aufgaben +- [ ] Schema mit deutschen Bezeichnern (siehe `CLAUDE.md`) +- [ ] Datenbankregeln (CHECK, Trigger) für das, was auch an der Service-Schicht vorbei gelten muss +- [ ] Seed: Notfall-Admin, eingebaute Vorlagen, optional Demo-Daten +- [ ] Test-Datenbank-Setup für Vitest + +## Akzeptanzkriterien +- [ ] Migration läuft auf leerer Datenbank +- [ ] Tests für die Datenbankregeln + +## Abhängigkeiten +#001 + +## Notizen / Entscheidungen diff --git a/docs/issues/004-anmeldung-rollen.md b/docs/issues/004-anmeldung-rollen.md new file mode 100644 index 0000000..2b05521 --- /dev/null +++ b/docs/issues/004-anmeldung-rollen.md @@ -0,0 +1,28 @@ +# #004 Anmeldung, Rollen und erneute Bestätigung +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** sicherheit +**Geschätzt:** 5 h **Tatsächlich:** – + +## Ziel +Anmeldung per OIDC (authentik, in der Entwicklung Mock-Anbieter) und lokaler Notfall-Admin mit Passwort + TOTP. Rollen Admin/Operator/Leser, Re-Auth für kritische Aktionen. + +## Aufgaben +- [ ] Sitzungs-Cookie (`jose`), OIDC mit PKCE (`openid-client`) +- [ ] Rollen aus authentik-Gruppen, bei jeder Anmeldung neu +- [ ] Notfall-Admin: argon2id + TOTP (Pflicht), Einrichtung beim ersten Login +- [ ] Re-Auth: OIDC `prompt=login`/`max_age=0` + `auth_time`, Notfall-Admin Passwort + TOTP; Gültigkeit 5 min +- [ ] Rechtetabelle `darf()`, jede Seite/Route prüft selbst; struktureller Test +- [ ] Rate-Limit für Anmeldung und Re-Auth +- [ ] Mock-OIDC-Anbieter in `docker-compose.dev.yml` +- [ ] Proxy mit CSP-Nonce und Sicherheits-Kopfzeilen + +## Akzeptanzkriterien +- [ ] Ohne Anmeldung keine Seite erreichbar +- [ ] Operator sieht keine Private-Key-Funktionen +- [ ] Re-Auth älter als 5 min wird abgelehnt + +## Abhängigkeiten +#003 + +## Notizen / Entscheidungen diff --git a/docs/issues/005-protokoll.md b/docs/issues/005-protokoll.md new file mode 100644 index 0000000..c29388e --- /dev/null +++ b/docs/issues/005-protokoll.md @@ -0,0 +1,23 @@ +# #005 Revisionssicheres Protokoll +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** sicherheit +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Jede Aktion landet im Protokoll; Einträge sind unveränderlich und über eine Hash-Kette verbunden. + +## Aufgaben +- [ ] Modell `Protokoll` mit Akteur, Aktion, Host, Zielbenutzer, Fingerprint, Diff, Ergebnis +- [ ] DB-Trigger verbietet UPDATE/DELETE +- [ ] Hash-Kette (SHA-256 über Vorgänger + Inhalt), Prüffunktion +- [ ] Redaction: Private Keys/Passwörter können nicht hineingeraten + +## Akzeptanzkriterien +- [ ] Test: UPDATE/DELETE schlägt fehl +- [ ] Test: manipulierte Zeile wird von der Prüfung gefunden + +## Abhängigkeiten +#003 + +## Notizen / Entscheidungen diff --git a/docs/issues/006-geheimnisse-schluesselformat.md b/docs/issues/006-geheimnisse-schluesselformat.md new file mode 100644 index 0000000..b8b83ca --- /dev/null +++ b/docs/issues/006-geheimnisse-schluesselformat.md @@ -0,0 +1,26 @@ +# #006 Verschlüsselte Ablage und OpenSSH-Schlüsselformat +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** sicherheit +**Geschätzt:** 4 h **Tatsächlich:** – + +## Ziel +Envelope Encryption für Private Keys mit Master-Key aus Datei; eigene Erzeugung und Serialisierung von OpenSSH-Schlüsseln ohne temporäre Dateien; Deployment-Key der Instanz. + +## Aufgaben +- [ ] Master-Key aus `SSHKV_MASTER_KEY_FILE` (32 Byte), nur im Worker +- [ ] AES-256-GCM, DEK je Schlüssel, AAD = Schlüssel-ID + Fingerprint, Master-Key-Version +- [ ] ed25519 und RSA-4096 erzeugen (Node `crypto`), OpenSSH-Format schreiben und lesen (optional Passphrase: bcrypt-pbkdf + aes256-ctr) +- [ ] Fingerprint SHA256 wie `ssh-keygen -l` +- [ ] Deployment-Key beim ersten Start erzeugen +- [ ] Log-Redaction + +## Akzeptanzkriterien +- [ ] `ssh-keygen -y -f` liest erzeugte Schlüssel (mit und ohne Passphrase) +- [ ] Fingerprints identisch zu `ssh-keygen -lf` +- [ ] Datenbank-Dump enthält keinen Klartext-Schlüssel + +## Abhängigkeiten +#003 + +## Notizen / Entscheidungen diff --git a/docs/issues/007-worker-vorgaenge.md b/docs/issues/007-worker-vorgaenge.md new file mode 100644 index 0000000..bf8e513 --- /dev/null +++ b/docs/issues/007-worker-vorgaenge.md @@ -0,0 +1,22 @@ +# #007 Worker und Vorgänge +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** backend +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Eigener Worker-Prozess mit pg-boss; die Oberfläche legt Vorgänge an und zeigt deren Fortschritt. + +## Aufgaben +- [ ] Worker-Einstieg, gebündelt per esbuild +- [ ] pg-boss-Queues je Vorgangsart, Zeitpläne +- [ ] Modell `Vorgang` für Status/Ergebnis in der Oberfläche +- [ ] Docker-Compose produktiv: web, worker, db, migrate; Master-Key nur im Worker + +## Akzeptanzkriterien +- [ ] Vorgang wird im Web angelegt, im Worker ausgeführt, Ergebnis sichtbar + +## Abhängigkeiten +#003 + +## Notizen / Entscheidungen diff --git a/docs/issues/008-hosts-gruppen.md b/docs/issues/008-hosts-gruppen.md new file mode 100644 index 0000000..4fcc785 --- /dev/null +++ b/docs/issues/008-hosts-gruppen.md @@ -0,0 +1,22 @@ +# #008 Hosts und Hostgruppen +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** backend, ui +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Hosts und Hostgruppen anlegen, bearbeiten, archivieren; Kunden und Tags. + +## Aufgaben +- [ ] Liste mit Status (erreichbar, letzter Abgleich, Drift) +- [ ] Formular: Name, Adresse, Port, Gruppen, Tags, Beschreibung, Kunde, Anmeldebenutzer (`sshkv`/`root`) +- [ ] Hostgruppen mit Mitgliedern +- [ ] Hostdetail-Gerüst + +## Akzeptanzkriterien +- [ ] Leser sieht, Operator/Admin bearbeiten + +## Abhängigkeiten +#004 + +## Notizen / Entscheidungen diff --git a/docs/issues/009-host-key-pruefung.md b/docs/issues/009-host-key-pruefung.md new file mode 100644 index 0000000..db19b93 --- /dev/null +++ b/docs/issues/009-host-key-pruefung.md @@ -0,0 +1,23 @@ +# #009 Host-Key-Prüfung (TOFU) und Erreichbarkeit +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** sicherheit +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Beim ersten Kontakt wird der Host-Key angezeigt und bestätigt; danach strikte Prüfung, Änderung = Warnung. + +## Aufgaben +- [ ] Vorgang „Host-Key abrufen“ (ohne Anmeldung) +- [ ] Bestätigen/Ablehnen mit Protokolleintrag +- [ ] `hostVerifier` in ssh2 gegen gespeicherten Key +- [ ] Geänderter Host-Key: Status GEAENDERT, keine Verbindung, deutliche Warnung +- [ ] Erreichbarkeitsprüfung + +## Akzeptanzkriterien +- [ ] Integrationstest: geänderter Host-Key wird abgelehnt + +## Abhängigkeiten +#006, #007 + +## Notizen / Entscheidungen diff --git a/docs/issues/010-bootstrap.md b/docs/issues/010-bootstrap.md new file mode 100644 index 0000000..8542aa1 --- /dev/null +++ b/docs/issues/010-bootstrap.md @@ -0,0 +1,24 @@ +# #010 Bootstrap neuer Hosts +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** backend, ui +**Geschätzt:** 4 h **Tatsächlich:** – + +## Ziel +Deployment-Key auf neuen Host bringen: einmalige Passwortanmeldung (nie gespeichert) oder Copy-Paste-Befehl. Dienstbenutzer `sshkv` + sudoers. + +## Aufgaben +- [ ] Einmal-Passwort nur im Arbeitsspeicher des Workers, verschlüsselt im Job, danach gelöscht +- [ ] Bootstrap-Skript: Benutzer `sshkv`, `/etc/sudoers.d/sshkv` (geprüft mit `visudo -c`), Deployment-Key in verwaltetem Block +- [ ] Alternative: Befehl zum Kopieren +- [ ] Prüfung nach Bootstrap: Anmeldung per Schlüssel, sudo ohne Passwort +- [ ] sshd-Check: `PermitRootLogin`, `PasswordAuthentication` (nur Anzeige) + +## Akzeptanzkriterien +- [ ] Integrationstest gegen Test-Container: Bootstrap per Passwort und per Befehl +- [ ] Passwort taucht nirgends in DB, Logs oder Protokoll auf + +## Abhängigkeiten +#009 + +## Notizen / Entscheidungen diff --git a/docs/issues/011-test-ssh-container.md b/docs/issues/011-test-ssh-container.md new file mode 100644 index 0000000..3e38869 --- /dev/null +++ b/docs/issues/011-test-ssh-container.md @@ -0,0 +1,21 @@ +# #011 Test-SSH-Container und Integrationstests +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** test +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Debian-Container mit sshd für Integrationstests (Bootstrap, Host-Key, später Ausrollen). + +## Aufgaben +- [ ] Dockerfile `tests/ssh-ziel/` (Debian 13, sshd, sudo, Passwort für root/test) +- [ ] Compose-Datei für Tests, zwei Ziele (für Host-zu-Host) +- [ ] Vitest-Projekt `integration` + +## Akzeptanzkriterien +- [ ] `pnpm test:integration` läuft lokal + +## Abhängigkeiten +#001 + +## Notizen / Entscheidungen diff --git a/docs/issues/012-schluessel-verwalten.md b/docs/issues/012-schluessel-verwalten.md new file mode 100644 index 0000000..08f0082 --- /dev/null +++ b/docs/issues/012-schluessel-verwalten.md @@ -0,0 +1,23 @@ +# #012 Schlüssel erzeugen, importieren, exportieren +**Status:** offen +**Meilenstein:** v0.2.0 +**Labels:** backend, ui +**Geschätzt:** 4 h **Tatsächlich:** – + +## Ziel +Schlüsselliste und -formular: erzeugen (ed25519, rsa-4096), importieren (Public oder Paar), exportieren; Kategorien, Tags, Ablaufdatum. + +## Aufgaben +- [ ] Liste mit Filter +- [ ] Erzeugen-Dialog +- [ ] Import mit Prüfung +- [ ] Export Public Key +- [ ] Schlüsseldetail-Gerüst mit Verwendungen + +## Akzeptanzkriterien +- [ ] Private Keys erscheinen nie in Listen-Antworten + +## Abhängigkeiten +#006 + +## Notizen / Entscheidungen diff --git a/docs/issues/013-personen-selbstauskunft.md b/docs/issues/013-personen-selbstauskunft.md new file mode 100644 index 0000000..e814e0e --- /dev/null +++ b/docs/issues/013-personen-selbstauskunft.md @@ -0,0 +1,20 @@ +# #013 Personen und Self-Service +**Status:** offen +**Meilenstein:** v0.2.0 +**Labels:** backend, ui +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Personen verwalten; jeder angemeldete Benutzer kann eigene Public Keys hinterlegen, wirksam erst nach Zuweisung. + +## Aufgaben +- [ ] Personenliste/-detail +- [ ] Seite „Meine Schlüssel“ + +## Akzeptanzkriterien +- [ ] Leser kann nur eigene Keys hinterlegen, nicht zuweisen + +## Abhängigkeiten +#012 + +## Notizen / Entscheidungen diff --git a/docs/issues/014-private-key-anzeigen.md b/docs/issues/014-private-key-anzeigen.md new file mode 100644 index 0000000..f2c6ffa --- /dev/null +++ b/docs/issues/014-private-key-anzeigen.md @@ -0,0 +1,20 @@ +# #014 Private Key anzeigen und herunterladen +**Status:** offen +**Meilenstein:** v0.2.0 +**Labels:** sicherheit +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Nur Admin, nur nach Re-Auth, immer protokolliert; Entschlüsselung im Worker. + +## Aufgaben +- [ ] Einmal-Abholung +- [ ] Option „Private Key nach Ausrollen löschen“ + +## Akzeptanzkriterien +- [ ] Kein Caching, kein Logging + +## Abhängigkeiten +#012, #004 + +## Notizen / Entscheidungen diff --git a/docs/issues/015-authorized-keys-parser.md b/docs/issues/015-authorized-keys-parser.md new file mode 100644 index 0000000..b1e072c --- /dev/null +++ b/docs/issues/015-authorized-keys-parser.md @@ -0,0 +1,22 @@ +# #015 authorized_keys: Parser und Merge +**Status:** offen +**Meilenstein:** v0.2.0 +**Labels:** backend, test +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Reine Funktionen zum Lesen und Schreiben von `authorized_keys` mit verwaltetem Block. + +## Aufgaben +- [ ] Optionen mit Anführungszeichen +- [ ] CRLF, BOM, Kommentare, Duplikate +- [ ] Block erzeugen/ersetzen, Rest bytegenau +- [ ] Aussperrschutz Deployment-Key + +## Akzeptanzkriterien +- [ ] Umfangreiche Unit-Tests der Randfälle + +## Abhängigkeiten +#001 + +## Notizen / Entscheidungen diff --git a/docs/issues/016-public-key-ausrollen.md b/docs/issues/016-public-key-ausrollen.md new file mode 100644 index 0000000..c66cbfc --- /dev/null +++ b/docs/issues/016-public-key-ausrollen.md @@ -0,0 +1,21 @@ +# #016 Public Key ausrollen mit Dry-Run und Backup +**Status:** offen +**Meilenstein:** v0.2.0 +**Labels:** backend, ui +**Geschätzt:** 4 h **Tatsächlich:** – + +## Ziel +Zuweisungen Schlüssel × Host/Gruppe × Benutzer; Dry-Run-Diff, Backup, atomares Schreiben, Rechte. + +## Aufgaben +- [ ] Remote-Skript +- [ ] Optimistische Sperre über Datei-Hash +- [ ] Diff-Box im Schlüsseldetail + +## Akzeptanzkriterien +- [ ] Integrationstest Ausrollen/Entfernen + +## Abhängigkeiten +#015, #010 + +## Notizen / Entscheidungen diff --git a/docs/issues/017-private-key-ausrollen.md b/docs/issues/017-private-key-ausrollen.md new file mode 100644 index 0000000..3f7847d --- /dev/null +++ b/docs/issues/017-private-key-ausrollen.md @@ -0,0 +1,21 @@ +# #017 Private Key ausrollen und ~/.ssh/config +**Status:** offen +**Meilenstein:** v0.3.0 +**Labels:** backend, ui +**Geschätzt:** 4 h **Tatsächlich:** – + +## Ziel +Private Key auf Zielhost/-benutzer/-pfad, verwalteter Block in `~/.ssh/config`, `known_hosts`. + +## Aufgaben +- [ ] Übertragung nur über stdin +- [ ] Warnung bei Passphrase +- [ ] nur Admin + +## Akzeptanzkriterien +- [ ] Integrationstest + +## Abhängigkeiten +#016 + +## Notizen / Entscheidungen diff --git a/docs/issues/018-benutzer-sudoers.md b/docs/issues/018-benutzer-sudoers.md new file mode 100644 index 0000000..b8bada8 --- /dev/null +++ b/docs/issues/018-benutzer-sudoers.md @@ -0,0 +1,20 @@ +# #018 Zielbenutzer anlegen und sudoers +**Status:** offen +**Meilenstein:** v0.3.0 +**Labels:** backend, sicherheit +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Benutzer (z. B. `deploy`) anlegen, optional sudoers mit Vorschau und `visudo -c`. + +## Aufgaben +- [ ] Auswahl keine/bestimmte Befehle/voll +- [ ] Entfernen beim Widerruf + +## Akzeptanzkriterien +- [ ] Integrationstest + +## Abhängigkeiten +#016 + +## Notizen / Entscheidungen diff --git a/docs/issues/019-vorlagen.md b/docs/issues/019-vorlagen.md new file mode 100644 index 0000000..1b3f521 --- /dev/null +++ b/docs/issues/019-vorlagen.md @@ -0,0 +1,19 @@ +# #019 Vorlagen +**Status:** offen +**Meilenstein:** v0.3.0 +**Labels:** backend, ui +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Eingebaute Vorlagen „Deployment-Zugang“, „Backup-Zugang“, „Admin-Zugang Person“; eigene in den Einstellungen. + +## Aufgaben +- [ ] Platzhalter `{{quellHostIp}}` usw. + +## Akzeptanzkriterien +- [ ] Vorlage füllt Assistent vor + +## Abhängigkeiten +#017, #018 + +## Notizen / Entscheidungen diff --git a/docs/issues/020-host-zu-host-assistent.md b/docs/issues/020-host-zu-host-assistent.md new file mode 100644 index 0000000..c7e7362 --- /dev/null +++ b/docs/issues/020-host-zu-host-assistent.md @@ -0,0 +1,21 @@ +# #020 Host-zu-Host-Assistent und Verbindungstest +**Status:** offen +**Meilenstein:** v0.3.0 +**Labels:** ui, backend +**Geschätzt:** 4 h **Tatsächlich:** – + +## Ziel +Geführter Ablauf: Schlüssel erzeugen, Private auf A, Public auf B, Host-Key von B in known_hosts von A, Verbindungstest. + +## Aufgaben +- [ ] Schrittanzeige +- [ ] Zusammenfassung + Dry-Run +- [ ] `ssh -o BatchMode=yes … true` auf A + +## Akzeptanzkriterien +- [ ] Kern-Anwendungsfall Ende-zu-Ende gegen zwei Test-Container + +## Abhängigkeiten +#019 + +## Notizen / Entscheidungen diff --git a/docs/issues/021-abgleich-drift.md b/docs/issues/021-abgleich-drift.md new file mode 100644 index 0000000..b75aae0 --- /dev/null +++ b/docs/issues/021-abgleich-drift.md @@ -0,0 +1,20 @@ +# #021 Abgleich und Drift-Erkennung +**Status:** offen +**Meilenstein:** v0.4.0 +**Labels:** backend +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Periodischer und manueller Abgleich, Befunde je Host/Benutzer. + +## Aufgaben +- [ ] Zeitplan konfigurierbar +- [ ] Befundarten + +## Akzeptanzkriterien +- [ ] Integrationstest + +## Abhängigkeiten +#016 + +## Notizen / Entscheidungen diff --git a/docs/issues/022-import.md b/docs/issues/022-import.md new file mode 100644 index 0000000..4b60741 --- /dev/null +++ b/docs/issues/022-import.md @@ -0,0 +1,19 @@ +# #022 Import bestehender Schlüssel +**Status:** offen +**Meilenstein:** v0.4.0 +**Labels:** backend, ui +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Unbekannte Schlüssel eines Hosts übernehmen und zuordnen. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +#021 + +## Notizen / Entscheidungen diff --git a/docs/issues/023-rotation.md b/docs/issues/023-rotation.md new file mode 100644 index 0000000..6abca80 --- /dev/null +++ b/docs/issues/023-rotation.md @@ -0,0 +1,19 @@ +# #023 Geführte Rotation +**Status:** offen +**Meilenstein:** v0.4.0 +**Labels:** backend, ui +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Neuer Schlüssel an allen Verwendungen, Test, alter entfernt. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +#020 + +## Notizen / Entscheidungen diff --git a/docs/issues/024-widerruf.md b/docs/issues/024-widerruf.md new file mode 100644 index 0000000..be624da --- /dev/null +++ b/docs/issues/024-widerruf.md @@ -0,0 +1,19 @@ +# #024 Widerruf und Massenwiderruf +**Status:** offen +**Meilenstein:** v0.4.0 +**Labels:** backend, ui, sicherheit +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Ein-Klick-Widerruf inkl. Benutzer/sudoers; Massenwiderruf mit Re-Auth. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +#018 + +## Notizen / Entscheidungen diff --git a/docs/issues/025-zuweisungsmatrix.md b/docs/issues/025-zuweisungsmatrix.md new file mode 100644 index 0000000..ca824e8 --- /dev/null +++ b/docs/issues/025-zuweisungsmatrix.md @@ -0,0 +1,19 @@ +# #025 Zuweisungsmatrix +**Status:** offen +**Meilenstein:** v0.4.0 +**Labels:** ui +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Matrix Schlüssel × Host. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +#016 + +## Notizen / Entscheidungen diff --git a/docs/issues/026-uebersicht.md b/docs/issues/026-uebersicht.md new file mode 100644 index 0000000..806a701 --- /dev/null +++ b/docs/issues/026-uebersicht.md @@ -0,0 +1,19 @@ +# #026 Übersicht (Dashboard) +**Status:** offen +**Meilenstein:** v0.5.0 +**Labels:** ui +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Kennzahlen, Hosts mit Abweichung, bald ablaufend, letzte Aktionen. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +#021 + +## Notizen / Entscheidungen diff --git a/docs/issues/027-protokoll-ansicht.md b/docs/issues/027-protokoll-ansicht.md new file mode 100644 index 0000000..815ee77 --- /dev/null +++ b/docs/issues/027-protokoll-ansicht.md @@ -0,0 +1,19 @@ +# #027 Protokollansicht, CSV-Export, Syslog +**Status:** offen +**Meilenstein:** v0.5.0 +**Labels:** ui, backend +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +Filterbare Ansicht, CSV, optionale Weiterleitung an Syslog. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +#005 + +## Notizen / Entscheidungen diff --git a/docs/issues/028-benachrichtigungen.md b/docs/issues/028-benachrichtigungen.md new file mode 100644 index 0000000..e2aa695 --- /dev/null +++ b/docs/issues/028-benachrichtigungen.md @@ -0,0 +1,19 @@ +# #028 Benachrichtigungen +**Status:** offen +**Meilenstein:** v0.5.0 +**Labels:** backend +**Geschätzt:** 2 h **Tatsächlich:** – + +## Ziel +E-Mail (M365 SMTP) und Webhook bei Drift und Ablauf. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +#021 + +## Notizen / Entscheidungen diff --git a/docs/issues/029-handbuch.md b/docs/issues/029-handbuch.md new file mode 100644 index 0000000..97adcb5 --- /dev/null +++ b/docs/issues/029-handbuch.md @@ -0,0 +1,19 @@ +# #029 Handbuch und Betriebsdokumentation +**Status:** offen +**Meilenstein:** v0.5.0 +**Labels:** docs +**Geschätzt:** 3 h **Tatsächlich:** – + +## Ziel +Installation, Bootstrap, Kern-Anwendungsfall mit Bildern; Bedrohungsanalyse, Backup-Konzept. + +## Aufgaben +- [ ] wird zu Beginn der Phase verfeinert + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +– + +## Notizen / Entscheidungen diff --git a/docs/issues/030-gitea-abgleich.md b/docs/issues/030-gitea-abgleich.md new file mode 100644 index 0000000..73bcf2d --- /dev/null +++ b/docs/issues/030-gitea-abgleich.md @@ -0,0 +1,19 @@ +# #030 Issues nach Gitea übertragen +**Status:** offen +**Meilenstein:** v0.1.0 +**Labels:** chore +**Geschätzt:** 1 h **Tatsächlich:** – + +## Ziel +Issue-Dateien, Labels und Meilensteine in Gitea anlegen (wartet auf Zugangsdaten). + +## Aufgaben +- [ ] `scripts/gitea-issues.ts` + +## Akzeptanzkriterien +- [ ] wird zu Beginn der Phase verfeinert + +## Abhängigkeiten +– + +## Notizen / Entscheidungen