Enterprise-Backup-, Recovery-, Verification-, Security- und Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme. Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes geht in keine Bewertung als "gut" ein. Umfang (Phasen 0-23): - Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll, Katalogaufbau allein aus den Manifesten — ohne Datenbank - Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck - Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell) - Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive - Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit - Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center, Ransomware-Heuristik (meldet, handelt nie) - Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing - Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository - Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64 Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs. Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md und docs/release-candidate.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8.7 KiB
Sicherheitsarchitektur
Dieses Dokument beschreibt den aktuell implementierten Stand nach Phase 1.
Passwörter
Gespeichert wird ausschließlich ein Argon2id-Hash im PHC-Format:
$argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash>
- Parameter: 64 MiB Speicher, 3 Durchgänge, Parallelität 4, 16 Byte Salz, 32 Byte Hash. Speicherbedarf ist die wirksamste Verteidigung, weil er Angriffe mit Grafikkarten und Spezialhardware unwirtschaftlich macht.
- Die Parameter wandern mit dem Hash. Sie lassen sich später verschärfen, ohne bestehende Anmeldungen zu brechen;
NeedsRehasherkennt veraltete Hashes. - Vergleich in konstanter Zeit. Eine von der Übereinstimmung abhängige Laufzeit verriete, wie viele Zeichen bereits stimmen.
- Mindestanforderung: 12 Zeichen, mindestens 5 verschiedene, Buchstaben plus mindestens ein weiteres Zeichen. Bewusst keine starren Zeichenklassenregeln — die führen zu vorhersagbaren Mustern wie
Passwort1!, ohne die Sicherheit zu erhöhen.
Schutz vor Benutzer-Enumeration
Ein unbekannter Anmeldename und ein falsches Passwort liefern denselben Fehler (ErrInvalidCredentials) und denselben HTTP-Status.
Zusätzlich führt der Dienst bei unbekanntem Konto eine Schein-Passwortprüfung durch. Ohne sie wäre die Antwort messbar schneller als bei einem existierenden Konto — daraus liessen sich gültige Anmeldenamen ableiten.
Zweiter Faktor
TOTP nach RFC 6238, verifiziert gegen alle zehn offiziellen Testvektoren des RFC (SHA1/SHA256/SHA512).
- Eigene Umsetzung statt Fremdbibliothek: der Algorithmus ist klein und vollständig spezifiziert, die Korrektheit über die offiziellen Vektoren nachweisbar, und Syncova braucht ohnehin eigene Logik für Replay-Schutz und Zeitfenster-Toleranz.
- Replay-Schutz: der zuletzt akzeptierte Zeitschritt wird je Methode gespeichert. Ein Code aus diesem oder einem älteren Schritt wird abgelehnt, selbst wenn er rechnerisch stimmt. Ohne diese Prüfung liesse sich ein abgefangener Code innerhalb seines 30-Sekunden-Fensters erneut verwenden.
- Toleranz: je ein Zeitschritt in beide Richtungen. Ohne Toleranz schlüge jede leicht falsch gehende Uhr fehl; mehr Toleranz verlängerte das Zeitfenster für einen Angreifer.
- Bestätigung erforderlich: ein neu eingerichteter Faktor ist erst nach erfolgreicher Codeeingabe aktiv. Sonst könnte sich aussperren, wessen App das Secret nicht korrekt übernommen hat.
- Secrets liegen verschlüsselt (AES-256-GCM) mit Angabe der Schlüsselversion.
Wiederherstellungscodes
Zehn Codes im Format ABCDE-FGHIJ, Alphabet ohne 0/O und 1/I (Verwechslungsgefahr beim Abtippen). Gespeichert wird nur der Argon2id-Hash; der Klartext erscheint genau einmal bei der Ausgabe. Jeder Code gilt genau einmal — die Entwertung ist über eine Bedingung auf used_at gegen gleichzeitige Anfragen abgesichert.
Sitzungen
Opake Zufallstokens (32 Byte, 256 Bit Entropie) statt JWT. Der Grund: sofortige Widerrufbarkeit. Ein JWT bliebe bis zum Ablauf gültig, auch nach Sperre oder Passwortänderung.
- Gespeichert wird nur der SHA-256-Hash. Ein Datenbankleck erlaubt damit keine Übernahme laufender Sitzungen. Argon2id wäre hier ohne Nutzen: ein Token mit 256 Bit Zufall kann nicht erraten werden, die Rechenkosten brächten also nichts.
- Rotation bei jeder Erneuerung. Beide Tokens werden ausgetauscht, ein abgefangenes Erneuerungstoken wird beim nächsten regulären Gebrauch wertlos.
- Lebensdauer: Zugriffstoken 15 Minuten, Erneuerungstoken 12 Stunden (konfigurierbar).
- Ein gesperrtes Konto verliert den Zugriff sofort — bei jedem Request wird der Kontozustand geprüft, eine bestehende Sitzung überdauert die Sperre nicht.
- Passwortänderung und Deaktivierung beenden alle Sitzungen des Kontos.
Zugriffssteuerung
Sieben mitgelieferte Rollen (PROMPT.md §42) und 27 Einzelberechtigungen im Format bereich.aktion.
| Rolle | Rechte |
|---|---|
super_administrator |
27 (alle) |
infrastructure_administrator |
14 |
backup_operator |
12 |
security_administrator |
11 |
auditor |
11 (ausschließlich lesend) |
viewer |
10 (ausschließlich lesend) |
restore_operator |
10 |
Durchsetzung ausschließlich serverseitig. Authentifizierung und Berechtigungsprüfung sind in protectedHandler untrennbar verbunden — ein Endpunkt lässt sich damit nicht versehentlich ohne Berechtigungsprüfung einbinden. Ein abgewiesener Zugriff erzeugt ein Auditereignis.
Mitgelieferte Rollen sind unveränderlich: eine nachträgliche Änderung verschöbe die Bedeutung bestehender Zuweisungen.
Schutz des letzten Administrators
Löschung, Deaktivierung und Rollenentzug prüfen, ob danach noch ein aktives Konto mit users.write übrig bleibt. Andernfalls wäre die Installation nicht mehr verwaltbar.
Brute-Force-Schutz
Zwei Ebenen, die zusammenwirken:
- Kontobezogen: 5 Fehlversuche → 15 Minuten Sperre. Der Zähler wird in einer einzigen SQL-Anweisung erhöht, damit gleichzeitige Versuche ihn nicht überschreiben.
- Absenderbezogen: 10 Anmeldeversuche je 5 Minuten und IP-Adresse. Ohne diese Ebene könnte ein Angreifer viele Konten mit je wenigen Versuchen durchprobieren, ohne je eine Sperre auszulösen.
Eine MFA-Herausforderung erlaubt 5 Fehlversuche und wird danach geschlossen — sonst liesse sich ein sechsstelliger Code durchprobieren.
Auditprotokoll
Append-only, in der Datenbank durchgesetzt: Trigger weisen UPDATE und DELETE auf audit_events ab. Die Regel liegt bewusst nicht nur in der Anwendung — ein Angreifer mit Datenbankzugriff soll seine Spuren nicht durch ein einfaches UPDATE verwischen können.
Protokolliert werden Anmeldung (Erfolg und Fehlschlag), MFA-Ergebnisse, Kontosperren, abgewiesene Zugriffe, Benutzer- und Rollenänderungen sowie Passwortänderungen.
actor_usernamewird mitgeschrieben und bleibt erhalten, auch wenn der Benutzer später gelöscht wird — sonst verlöre das Protokoll seine Aussage.- Die Absenderadresse stammt ausschließlich aus
RemoteAddr. Weitergeleitete Adressen aus Headern werden nicht ausgewertet: sie sind frei fälschbar und machten das Protokoll wertlos, solange nicht bekannt ist, welchem Proxy zu trauen ist. - Ein fehlgeschlagener Auditeintrag wird als
ERRORgeloggt und lässt die auslösende Handlung nicht scheitern — sie ist bereits geschehen, ein stiller Verlust wäre der schlechtere Ausgang.
Secrets
SecretStore ist eine schmale Schnittstelle, damit die lokale Umsetzung später gegen KMS, HSM oder einen externen Store getauscht werden kann.
Die lokale Umsetzung nutzt AES-256-GCM (authentifiziert — jede nachträgliche Veränderung wird beim Entschlüsseln erkannt). Mehrere Schlüsselversionen können gleichzeitig vorliegen: neu verschlüsselt wird mit der aktuellen, ältere bleiben zur Entschlüsselung erhalten (Schlüsselrotation).
Der Verschlüsselungsschlüssel ist kritisch. Ohne ihn sind alle verschlüsselten Daten dauerhaft unlesbar. Er ist getrennt von der Datenbank zu sichern.
Secrets im Log
Die Redaction hängt an slog.HandlerOptions.ReplaceAttr — dem einzigen Ort, an dem sie nicht vergessen werden kann. Erkannt werden Feldnamen, die auf ein Geheimnis hindeuten (password, token, secret, connection_string und weitere), unabhängig von Schreibweise und Präfix.
Verbindungszeichenketten gehen nur über RedactedConnectionString() nach außen. Fehlermeldungen von Fremdbibliotheken, die eine DSN enthalten könnten, laufen durch redactPasswordInText.
Transport
Alle Antworten tragen X-Content-Type-Options, X-Frame-Options: DENY, Referrer-Policy: no-referrer, eine restriktive CSP und Cache-Control: no-store.
CORS ist standardmäßig leer; eine Wildcard-Herkunft wird grundsätzlich abgelehnt, eine unverschlüsselte Herkunft in der Produktion ebenfalls.
TLS wird in der Produktion vor dem Dienst terminiert (Reverse Proxy); der Dienst selbst bindet standardmäßig nur an 127.0.0.1.
Erstinbetriebnahme
Es gibt kein vorkonfiguriertes Standardkonto — das wäre eine bekannte Schwachstelle jeder Installation. Der erste Administrator wird mit syncova-admin create-admin auf dem Server angelegt, das Passwort dabei verdeckt eingegeben. Das Kommando verweigert den Dienst, sobald bereits ein Administrator existiert; weitere Benutzer entstehen über die API.
Bekannte Grenzen
- Rate-Limiting liegt im Arbeitsspeicher. Bei mehreren Control-Plane-Knoten müsste der Zähler in eine gemeinsame Ablage wandern.
- WebAuthn/Passkeys, OIDC, SAML und LDAP sind architektonisch vorgesehen, aber nicht implementiert.
- Four-Eyes-Freigabe (PROMPT.md §87) ist noch nicht umgesetzt.
- Step-up-MFA für einzelne kritische Aktionen ist noch nicht umgesetzt; MFA greift derzeit bei der Anmeldung.