10 KiB
Fixes & Hardening TODO
Kritisches Review-Backlog vor Produktiveinsatz und OSS-Release. Sortiert nach Risikoklasse. Datei-/Zeilenreferenzen beziehen sich auf den Stand bei Review-Erstellung.
🔴 Kritisch — vor Produktion blockierend
-
1. Default-Admin
admin/adminentfernen ✅ erledigtmanagement/src/db.js:83— wirft jetzt Exception bei leeremAUTH_PASSWORD. Kein Silent-Fallback mehr.- README/
docs/deployment.mdentsprechend anpassen (noch offen).
-
2.
API_TOKENim Node-Agent zur Pflicht machen ✅ erledigtagent/src/config.js:44— Start schlägt fehl wenn Token fehlt oder kürzer als 32 Zeichen.agent/src/index.js—if (!config.apiToken) next()entfernt; Token-Prüfung immer aktiv.
-
3. HTTPS zwischen Management und Agent erzwingen ✅ erledigt
management/src/routes/nodes.js:102-112—validateBaseUrlerzwingthttps://. Escape-HatchALLOW_INSECURE_AGENT_HTTP=truenur für Dev.management/src/agentClient.js— eigene CA überAGENT_CA_FILEkonfigurierbar.- Agent-Server unterstützt TLS via
HTTPS_ENABLED,TLS_CERT_FILE,TLS_KEY_FILE.
-
4. CSRF-Risiko durch reflektierendes CORS schließen ✅ erledigt
management/src/index.js:18-27— Nur Origins ausCORS_ORIGINS-Env zugelassen.management/src/auth.js:57— Cookie aufSameSite=Strictgesetzt.
-
5. Brute-Force-Schutz beim Login ✅ erledigt
management/src/routes/auth.js:7-9— In-Memory Rate-Limit: 5 Versuche / 15 min / IP+User.- Audit-Event
login_blockedundlogin_failedimplementiert. - Hinweis: Kein externer
express-rate-limitnötig, eigenständige Implementierung ausreichend.
-
6. Restore atomarisieren — kein direktes
ddauf Produktiv-Volume ✅ staged Restore umgesetzt- Datei:
agent/src/executor.js:92(streamResticDumpToDd),agent/src/routes/restore.js - Umsetzung: Restore schreibt zuerst in ein temporäres ZFS-Volume, prüft die Größe vorab und tauscht danach per
zfs renamegegen das Produktiv-Volume. - Das alte Volume bleibt als
*.pre-restore-*Rollback-Kopie erhalten. - Bei Fehlern vor dem Swap wird nur das temporäre Volume entfernt; bei Fehlern nach dem Swap versucht der Agent den alten Volume-Namen wiederherzustellen.
- Datei:
-
7. Backup-Verifikation einbauen
- Datei:
agent/src/routes/backup.js(VM-Pfad ~Z.71, Container-Pfad ~Z.137) - Aktuell: Bei Pipe-Fehler (
zfs send!=0,createReadStream-Abbruch) committed restic ggf. einen unvollständigen Snapshot mit Status „success". - Fix:
- Vor Start erwartete Größe ermitteln (
zfs get volsize/used). - Nach Restic-Commit
restic stats <snapshotId>vergleichen. - Bei Mismatch oder Pipe-Fehler: Restic-Snapshot per ID
forget+pruneen, Job auffailed.
- Vor Start erwartete Größe ermitteln (
- Datei:
-
8. Bearer-Token aus Frontend-
localStorageentfernen ✅ erledigtfrontend/src/api.js— nur noch Cookie-Auth (withCredentials: true).localStorage-Token undVITE_API_TOKENentfernt.
🟠 Hoch
-
[~] 9. Session-Cookie
Secure-Flag ⚠️ teilweise erledigtmanagement/src/auth.js:56—Secure-Flag viaSESSION_COOKIE_SECUREkonfigurierbar, auto-aktiviert beiNODE_ENV=production.- Offen:
management/.env.examplehatSESSION_COOKIE_SECURE=false— in Deployment-Doku explizit als "in Produktion auftruesetzen" dokumentieren.
-
10. Race beim Sichtbarmachen des Snapshot-Devices
- Datei:
agent/src/routes/backup.js:57 - Aktuell: 2 s
sleepreicht nicht garantiert. - Fix: Polling-Schleife auf
fs.access(snapshotDevice)mit Timeout; zusätzlichudevadm trigger && udevadm settle.
- Datei:
-
11. Persistenter Job- und Lock-Store im Agent ✅ erledigt
- Datei:
agent/src/jobs.js - Umsetzung: Agent speichert Jobs in SQLite (
AGENT_DATABASE_PATH). - Beim Start werden
running/queuedJobs alsfailedmarkiert, damit Management einen finalen Zustand pollen kann und VM-Locks nicht hängen bleiben. - Offen für später: ressourcenspezifische Cleanup-Recovery für abgebrochene Host-Operationen.
- Datei:
-
12. Toten
SESSION_SECRETaufräumen- Datei:
management/src/config.js:8 - Aktuell:
SESSION_SECRETwird geladen, aber nirgendwo genutzt — toter Code. - Fix: Variable und zugehörigen
.env.example-Eintrag entfernen, oder Session-IDs HMAC-signieren und Variable dann sinnvoll nutzen.
- Datei:
-
13. Snapshot-ID-Prefix-Matching eindeutig machen
- Datei:
agent/src/validators.js:66 - Aktuell:
startsWith— bei Prefix-Kollision wird stillschweigend der erste Treffer genommen. - Fix: Bei >1 Treffer 409 zurückgeben; UI auf 12-Hex-Prefix umstellen.
- Datei:
-
14. RBAC einführen
- Aktuell: ein User, alle Rechte, kein Read-only.
- Fix: Mindestens Rollen
admin/operator/viewer. Restore nur füradmin.
-
15. ENV-Escaping in
writeEnvSettingsverbessern- Datei:
agent/src/config.js:105 - Aktuell: Nur
\und"escaped;$, Backticks, Newlines nicht. - Fix: Eigener Serializer mit korrektem Escaping aller Sonderzeichen.
- Datei:
-
16. Settings-Endpunkt sperren bis
API_TOKENinitial gesetzt ist- Datei:
agent/src/routes/settings.js - Hängt mit Fix #2 zusammen — nach #2 automatisch erfüllt, hier zur Sicherheit dokumentieren/testen.
- Datei:
🟡 Mittel
-
17.
Math.random()durchcrypto.randomBytesersetzen- Datei:
management/src/db.js:93(cryptoId). - Noch vorhanden:
Math.random().toString(16)für DB-interne User-IDs — kein akutes Sicherheitsproblem, aber nicht kryptografisch sicher.
- Datei:
-
18. Systemd-Hardening für den Agent
- Datei:
deploy/systemd/incus-backup-agent.service - Hinzufügen:
NoNewPrivileges=true,ProtectSystem=strict,ProtectHome=true,PrivateTmp=true,ReadWritePaths=/dev/zvol /var/lib/incus /opt/incus-backup-ui/agent,CapabilityBoundingSet=..., eingeschränkteAmbientCapabilities.
- Datei:
-
19.
npm startdurch direktennode-Aufruf ersetzen- Datei: beide
deploy/systemd/*.service ExecStart=/usr/bin/node src/index.js— kein npm-Wrapper-Prozess, kein PATH-Risiko.
- Datei: beide
-
20. CORS am Agent entfernen
- Datei:
agent/src/index.js:16 - Agent wird nie aus dem Browser angesprochen → Angriffsfläche raus.
- Datei:
-
21.
schedules.json-Pfad explizit konfigurierbar- Datei:
agent/src/scheduler.js:6 - Aktuell:
process.cwd()-abhängig. - Fix: über Env (
SCHEDULES_PATH) absolut konfigurieren, Default unter/var/lib/incus-backup-agent/.
- Datei:
-
22. Audit-Log-Integrität
- Datei:
management/src/store.js:71 - Append-only Constraint + Hash-Chain (jeder Event hat
prev_hash). Optional Off-Site-Versand (Webhook).
- Datei:
-
23. Passwort-Hashing auf argon2id umstellen
- Datei:
management/src/crypto.js - Aktuell:
scryptSyncmit Node-Defaults, ohne explizite Parameter. - Fix:
@node-rs/argon2oder explizite scrypt-Parameter dokumentieren + Migration-Pfad.
- Datei:
-
24. Rate-Limit / Quoten für Backups
- Pro VM und global (z.B. max N parallele Streams nach S3). Disk-Space-/Quota-Check vor Start.
-
25.
incus snapshot deleteRetry verallgemeinern- Datei:
agent/src/routes/backup.js:303 - Aktuell: Substring-Match auf englische Stderr — bricht bei lokalisierten Builds.
- Fix: Generischer Retry (n Versuche, Backoff) bei nicht-0 Exit-Code.
- Datei:
-
26. Restic-
lsstreamen statt vollständig in RAM laden- Datei:
agent/src/routes/snapshots.js:19 - Für Container-Backups mit vielen Files relevant.
- Datei:
-
27. Container-Restore implementieren oder Container-Backup deaktivieren
- Datei:
agent/src/routes/restore.js:21 - Aktuell: 501. Backups laufen, aber nicht wiederherstellbar = Backup-Theater.
- Fix: Sicheren
zfs receive-Workflow umsetzen oder Container-Backup im UI/API ausschalten bis fertig.
- Datei:
-
28. Pre-Backup VM-Zustand prüfen
- Live-Migration, laufende interne Snapshots, fehlende Berechtigungen → klare Fehler statt halb durchgeführter Pipeline.
🟢 Niedrig / Aufräumen
- 29.
.DS_Storeaus Repo entfernen und in.gitignoreaufnehmen - 30.
frontend/dist/ist eingecheckt — ignorieren und löschen. - 31.
incus-backup-ui-plan.mdauf Secrets/Bucket-Namen prüfen bevor OSS. - 32. Container-Limit (Restore fehlt) prominent in README dokumentieren.
- 33. Scheduler-Jitter einbauen (
management/src/scheduler.js,agent/src/scheduler.js) — sonst belasten viele Nodes synchron S3. - 34.
formatBytesdeduplizieren (agent/src/routes/backup.js,restore.js) →executor.jsoderutils.js. - 35.
.env-Dateipermissions dokumentieren —chmod 600im Deployment-Doc verlangen. - 36. Schema-Versionierung statt
addColumnIfMissing— z.B.schema_version-Tabelle + nummerierte Migrationen. - 37.
audit_events.detailsGrößenlimit oder JSON-Spalte (SQLite hat JSON1).
📦 OSS-Release-Voraussetzungen
- 38. Tests einführen — aktuell keinerlei Tests im Repo. Mindestens:
validators.js(vmName-/snapshot-Regex, Pfad-Validation).crypto.js(hash/verify Roundtrip, Timing-Safe).jobs.js(Locking, Trimming).- Pipeline-Smoke-Tests mit gemockten Befehlen.
- 39. CI-Pipeline (GitHub Actions): Lint, Tests,
npm audit, Lockfile-Check. - 40.
SECURITY.mdmit Disclosure-Adresse + GPG-Key. - 41. Threat-Model dokumentieren — explizit machen, was außerhalb des Schutzbereichs liegt (root-Agent, vertrauenswürdiges Netz, etc.).
- 42.
LICENSE-Datei ergänzen (z.B. MIT/Apache-2.0). - 43.
CONTRIBUTING.md+ Code-of-Conduct. - 44. Beispiel-
compose.yamloder Ansible-Rolle für reproduzierbares Deployment.
Status-Übersicht kritische Punkte (Stand 2026-06-04)
| # | Titel | Status |
|---|---|---|
| 1 | Default-Admin admin/admin entfernen |
✅ erledigt |
| 2 | API_TOKEN Pflicht |
✅ erledigt |
| 3 | HTTPS Management↔Agent | ✅ erledigt |
| 4 | CORS-Whitelist + SameSite=Strict | ✅ erledigt |
| 5 | Brute-Force-Schutz Login | ✅ erledigt |
| 6 | Restore atomarisieren | ✅ staged Restore umgesetzt |
| 7 | Backup-Verifikation | ⬜ offen |
| 8 | Bearer-Token aus localStorage | ✅ erledigt |
| 9 | Session-Cookie Secure-Flag |
⚠️ teilweise |
Nächste Prioritäten
- #7 Backup-Verifikation weiter härten — Pipeline-/Stream-Fehler testen und vollständiger absichern.
- #9
SESSION_COOKIE_SECURE=truein Deployment-Doku festschreiben. - #12 Toten
SESSION_SECRETentfernen. - #15 ENV-Escaping vervollständigen (
$, Backticks, Newlines). - #11 Folgearbeit ressourcenspezifische Cleanup-Recovery nach Agent-Crash definieren.