# Sicherheit und Bedrohungsanalyse SSHKeyVault (`sshkv`) verwaltet die SSH-Zugänge zu Servern – und hat dafür selbst **root-gleichen Zugriff auf alle verwalteten Hosts**. Wer sshkv übernimmt, übernimmt die Infrastruktur. Diese Seite beschreibt, wie sshkv aufgebaut ist, um das zu erschweren, was trotzdem passieren kann und was im Ernstfall zu tun ist. ## Was sshkv besitzt | Wert | Wo | Schutz | |---|---|---| | **Deployment-Key** (Private Key) | Datenbank, verschlüsselt | Envelope Encryption mit Master-Key; nur der Worker entschlüsselt | | **Private Keys** von Service-Schlüsseln | Datenbank, verschlüsselt | wie oben; Anzeige nur für Admins mit frischer Bestätigung, protokolliert | | **Master-Key** | Datei auf dem Docker-Host, nur im Worker eingebunden | Dateirechte 0400, nicht in der Datenbank, nicht im Image, nicht im Repo | | `SSHKV_APP_SECRET` | `.env` | signiert Sitzungen, verschlüsselt TOTP-Geheimnis, SMTP-Passwort, Webhook-Geheimnis und Job-Transporte | | Host-Keys der Hosts | Datenbank | öffentlich; schützen vor Man-in-the-Middle (strikte Prüfung) | | Protokoll | Datenbank | append-only per Trigger, Hash-Kette, optional Syslog | **Personen-Keys** speichert sshkv nur als Public Key – ein Datenbankregel verhindert, dass dort je ein Private Key steht. Bootstrap-Passwörter werden nie gespeichert. ## Aufbau, der Angriffe erschwert - **Web und Worker getrennt.** Der Webprozess kennt den Master-Key nicht und spricht kein SSH. Eine Lücke in der Weboberfläche allein entschlüsselt keine Private Keys; ein Test hält strukturell fest, dass der Webprozess nichts aus `src/worker` importiert und nur eine einzige Datei die verschlüsselten Felder liest. - **Envelope Encryption** (AES-256-GCM): je Private Key ein eigener Datenschlüssel, gebunden an ID und Fingerprint des Schlüssels. Ein Datenbank-Dump allein gibt keinen Schlüssel preis (Test). - **Strikte Host-Key-Prüfung** bei jeder Verbindung. Ein geänderter Host-Key bricht die Verbindung vor dem Senden von Zugangsdaten ab; Ersetzen nur mit Häkchen und frischer Bestätigung. - **Dateien auf den Hosts** schreibt sshkv als jeweiliger Zielbenutzer (`runuser`): Ein Symlink im Home eines Benutzers kann root nicht dazu bringen, `/etc/shadow` zu lesen oder zu überschreiben (Integrationstest). - **Dry-Run mit optimistischer Sperre:** Ausgerollt wird nur, was ein Mensch gesehen hat. Hat sich die Datei oder die Zuweisung seitdem geändert, bricht der Vorgang ab. - **Rollen:** Private Keys anzeigen, ausrollen und löschen, Deployment-Key, Zielkonten mit sudo und Massenwiderruf nur für Admins; kritische Aktionen zusätzlich mit erneuter Anmeldung (5 Minuten). Ohne passende authentik-Gruppe gibt es keinen Zugang, auch keinen lesenden. - **Notfall-Admin und lokale Konten** nur mit Passwort **und** TOTP; Rate-Limit gegen Raten (5 Fehlversuche je Anmeldename in 15 Minuten). Lokale Konten bekommen ein vom System erzeugtes Einmal-Passwort, das bei der ersten Anmeldung ersetzt werden muss – Admins kennen die Passwörter anderer nie. Sperren beendet Sitzungen sofort; `SSHKV_LOKALE_KONTEN=0` schaltet lokale Konten ganz ab. - **Weboberfläche:** Content-Security-Policy mit Nonce, `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer`, HttpOnly-/SameSite-Cookies, Secure hinter HTTPS; Server Actions mit Origin-Prüfung. ## Was passiert, wenn sshkv kompromittiert wird? | Angreifer hat … | Folge | Gegenmaßnahme | |---|---|---| | nur ein **Datenbank-Backup** | Hosts, Zuweisungen, Public Keys, Protokoll sichtbar; **keine** Private Keys | Backup trotzdem vertraulich behandeln (verrät die Struktur der Infrastruktur) | | **Datenbank + Master-Key** | alle Private Keys inkl. Deployment-Key → root auf allen Hosts | Master-Key getrennt aufbewahren; Notfallplan unten | | die **Weboberfläche** als Admin (gestohlene Sitzung) | kann Schlüssel zuweisen und ausrollen; Private Keys nur nach erneuter Anmeldung | kurze Sitzungen (8 h), Re-Auth, Protokoll + Syslog zeigen jede Aktion | | den **Webprozess** (Codeausführung) | Datenbankzugriff, aber kein Master-Key | Worker getrennt; Master-Key nur im Worker-Container | | den **Worker** oder Docker-Host | alles | Host härten, nur internes Netz, Updates, Zugriff auf den Docker-Host streng begrenzen | | einen **verwalteten Host** | kann sshkv nicht angreifen; sieht nur den Public Key des Deployment-Keys | — | ### Notfallplan „sshkv kompromittiert“ 1. **Sofort:** Netzwerkzugang zu sshkv sperren (Firewall/VPN), Container anhalten (`docker compose stop`). Die Hosts bleiben erreichbar – sshkv ist kein Teil des Logins. 2. **Deployment-Key auf allen Hosts entfernen:** Auf jedem Host in `~sshkv/.ssh/authorized_keys` (bzw. `/root/.ssh/authorized_keys` bei Anmeldeart root) die Zeile mit `sshkv-deployment@…` löschen und `/etc/sudoers.d/sshkv` entfernen. Mit einem Konfigurationsmanagement oder einer Schleife über die Hostliste (CSV-Export der Zuweisungen bzw. Hostliste). 3. **Ausgerollte Service-Keys tauschen:** Jeder Private Key, den sshkv kannte, gilt als bekannt. Nach dem Wiederaufbau alle Service-Schlüssel rotieren (geführte Rotation) und alte widerrufen. 4. **Neu aufsetzen:** neuer Master-Key, neues `SSHKV_APP_SECRET`, frische Datenbank oder geprüftes Backup; der Worker erzeugt einen neuen Deployment-Key; Hosts per Bootstrap neu aufnehmen. 5. **Protokoll auswerten:** Syslog-Kopie (unveränderlich außerhalb von sshkv) mit dem Protokoll vergleichen; `protokoll_kette_pruefen()` zeigt manipulierte Zeilen. > **Rotation des Deployment-Keys** (Einstellungen → Instanz → „Deployment-Key rotieren“, nur Admin mit > frischer Bestätigung): regelmäßig, oder wenn nur der Key abgeflossen sein kann – etwa ein verlorenes > Backup samt Master-Key. Der neue Key kommt erst neben den alten, wird auf jedem Host geprüft und löst > den alten dann ab; zuletzt wird der alte aus allen Blöcken entfernt und sein Private Key verworfen. > Ist sshkv **selbst** kompromittiert, hilft das nicht – der Instanz ist dann nicht mehr zu trauen, > Schritt 2 bleibt Handarbeit. ## Betriebsregeln - **Nur intern betreiben.** Die Weboberfläche gehört ins interne Netz oder hinter ein VPN, nie ins Internet. Compose bindet standardmäßig nur an `127.0.0.1`; ein Reverse Proxy mit TLS steht davor (siehe [installation.md](installation.md)). - **Docker-Host wie einen Bastion-Host behandeln:** wenige Admins, SSH nur mit Schlüssel, Updates, keine anderen Dienste. - **Master-Key** getrennt sichern, Rechte 0400, nur für den Worker-Benutzer lesbar. - **Syslog** einschalten (`SSHKV_SYSLOG_*`), damit eine Kopie des Protokolls außerhalb von sshkv liegt. - **Hosts:** `PermitRootLogin prohibit-password` (sshkv zeigt das als Hinweis an), Anmeldeart „Dienstbenutzer“ bevorzugen – dann kann root-Login ganz aus bleiben. - **Benachrichtigungen** für Drift und geänderte Host-Keys aktivieren, damit Abweichungen auffallen.