Ohne authentik gab es nur den Notfall-Admin – ein geteiltes Konto ohne Rollen und ohne erkennbare Akteure im Audit-Log. Admins legen jetzt lokale Konten an; sshkv erzeugt ein Einmal-Passwort, das bei der ersten Anmeldung ersetzt wird, danach ist TOTP Pflicht. Rollen, Sperren (beendet Sitzungen sofort) und Zurücksetzen unter Einstellungen → Benutzer, nur für Admins mit frischer Bestätigung. SSHKV_LOKALE_KONTEN=0 schaltet lokale Konten ab; der Notfall-Admin bleibt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
6.8 KiB
Markdown
93 lines
6.8 KiB
Markdown
# 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.
|