Bisher ließ sich der Instanz-Key nur von Hand auf jedem Host tauschen. Die Rotation läuft jetzt in fünf Schritten: Nachfolger erzeugen, per Dry-Run neben den alten verteilen, Anmeldung je Host prüfen, umstellen, alten Key aus allen Blöcken entfernen und seinen Private Key verwerfen. Der Nachfolger steht während der Rotation in Block und Pflichtliste, damit kein Host ohne funktionierenden Key dasteht. Umgestellt wird nur nach erfolgreicher Prüfung oder bewusstem Übergehen eines Hosts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.5 KiB
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/workerimportiert 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/shadowzu 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 nur mit Passwort und TOTP; Rate-Limit gegen Raten.
- 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“
- Sofort: Netzwerkzugang zu sshkv sperren (Firewall/VPN), Container anhalten
(
docker compose stop). Die Hosts bleiben erreichbar – sshkv ist kein Teil des Logins. - Deployment-Key auf allen Hosts entfernen: Auf jedem Host in
~sshkv/.ssh/authorized_keys(bzw./root/.ssh/authorized_keysbei Anmeldeart root) die Zeile mitsshkv-deployment@…löschen und/etc/sudoers.d/sshkventfernen. Mit einem Konfigurationsmanagement oder einer Schleife über die Hostliste (CSV-Export der Zuweisungen bzw. Hostliste). - 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.
- 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. - 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). - 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.