syncova-backup/docs/linux-agent.md
Jerrit Fritzsche 610719c316
Some checks failed
CI / Backend (Go) (push) Failing after 3m7s
CI / Frontend (React/TypeScript) (push) Successful in 37s
CI / Sicherheitsprüfungen (push) Successful in 44s
Syncova Backups V1
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>
2026-08-17 09:10:54 +02:00

6.6 KiB

Linux-Agent (Phase 6)

Phase 6 teilt sich fast alles mit Phase 5 — dieselbe Aufnahme, dieselbe Lebendmeldung, dieselbe Auftragsübermittlung, dieselbe Engine. Was sie eigenständig macht, sind die Eigenheiten eines Unix-Dateisystems: Sockets, benannte Pipes, Gerätedateien, harte Verknüpfungen — und ein Dienstmodell, das sich einsperren lässt.

Der Fund: Ein Socket ist kein Fehler

Bis zu dieser Phase galt jedes Erfassungsproblem als übergangenes Objekt und machte den Lauf damit zum Teilfehler. Auch ein Socket.

Auf einem Linux-System ist das ein Dauerzustand: In /var/run und /tmp liegen ständig Sockets. Wer /var sichert, bekäme bei jedem Lauf einen Teilfehler. Nach einer Woche klickt niemand mehr einen Teilfehler an — und dann fällt auch der echte nicht mehr auf. Dieselbe Alarmmüdigkeit, gegen die Phase 14 und 16 gebaut sind.

Die Unterscheidung, die jetzt gilt:

Objekt Ergebnis Warum
Nicht lesbare Datei Datenverlust → Teilfehler Dort waren Daten, und sie fehlen
Gesperrtes Verzeichnis Datenverlust → Teilfehler Dasselbe
Socket Vermerk, kein Fehler Endpunkt eines laufenden Prozesses, kein Inhalt
Benannte Pipe (FIFO) Vermerk, kein Fehler Hat keinen Inhalt zum Sichern
Block-/Zeichengerät Vermerk, kein Fehler Dasselbe

Verschwiegen wird trotzdem nichts. Die Zusammenfassung nennt sie auch im Erfolgsfall:

Erfolgreich: 4 Dateien, 1 Verzeichnisse gesichert. 2 Objekt(e) ohne sicherbaren
Inhalt (Sockets, Pipes, Gerätedateien) wurden übergangen; das ist kein Fehler.

Und die Meldung benennt den Typ in Worten. prw-r--r-- sagt einem Betreiber nichts; „benannte Pipe (FIFO)" sagt ihm, dass er nichts zu tun hat.

Was der Agent mit einer FIFO nicht tut

Er liest sie nicht. Das ist der klassische Backup-Bug: Ein Leseversuch auf einer benannten Pipe ohne Schreiber blockiert für immer. Die Erfassung entscheidet anhand des Dateityps, bevor sie öffnet — ein Test mit Zeitgrenze hält das fest.

Symbolische Verweise werden erfasst, nicht verfolgt

Ein Verweis kommt als Verweis ins Backup und wird als Verweis zurückgeschrieben. Verfolgte die Erfassung ihn, geriete sie bei einem Verweis auf ein übergeordnetes Verzeichnis in eine Schleife — und ein Verweis nach / zöge das ganze System ins Backup.

Harte Verknüpfungen: der Inhalt kommt zurück, die Verknüpfung nicht

Zwei Namen derselben Datei erscheinen als zwei Dateien. Der Inhalt liegt dank Deduplizierung nur einmal im Repository — der Platzbedarf im Backup ist also derselbe.

Nach einer Wiederherstellung sind es jedoch zwei unabhängige Dateien:

Quelle: 2 Verknüpfungen
Ziel:   1 Verknüpfung

Das kostet Platz am Ziel und ändert die Bedeutung: Eine Änderung an der einen wirkt nicht mehr auf die andere. Für die meisten Anwendungsfälle ist das unerheblich; wer Paketverwaltungen oder Deduplizierungsbäume sichert, sollte es wissen.

Umsetzbar wäre es — Inode-Nummer erfassen und beim Zurückschreiben link() statt Kopie —, aber es griffe in das Manifestformat ein. Es steht hier als benannte Grenze, nicht als Versehen.

Die systemd-Einheit hatte drei Fehler

deployment/syncova-agent.service war gut gehärtet und hätte nicht funktioniert:

--state /var/lib/syncova-agent — das ist ein Verzeichnis. Der Parameter erwartet eine Datei. Der Agent hätte beim Start gemeldet, er sei bereits registriert (ein vorhandenes Verzeichnis sieht für ihn wie eine Zustandsdatei aus), und wäre ohne Token gelaufen.

Kein ReadWritePaths für das Repository. Bei ProtectSystem=strict ist das gesamte Dateisystem nur lesbar. Der Agent hätte die Quelle vollständig gelesen und gehasht — und dann keinen einzigen Block ablegen können. Der Fehler wäre erst am Ende des Laufs aufgetreten.

RestrictAddressFamilies ohne AF_UNIX. Auf Systemen mit systemd-resolved oder nscd läuft die Namensauflösung über einen Unix-Socket. Ohne AF_UNIX findet der Agent seinen Server nicht und meldet einen Netzfehler, während das Netz einwandfrei arbeitet.

Alle drei sind behoben. Der Repositorypfad ist an die eigene Anlage anzupassen — liegt das Repository auf einer Freigabe, gehört deren Einhängepunkt in ReadWritePaths.

Die Einheit ist weiterhin nicht auf einem Linux-System geprüft. systemd-analyze verify gibt es auf macOS nicht.

Was die Härtung leistet

Der Agent liest fremde Daten und spricht mit dem Netz — genau die Art Dienst, die man einsperrt:

  • CAP_DAC_READ_SEARCH statt root. Er darf fremde Dateien lesen, sonst nichts. Der Unterschied ist beträchtlich: Lesen ist etwas anderes als Alleskönnen.
  • ProtectSystem=strict, ProtectHome=read-only, Schreibrechte nur auf Zustand und Repository.
  • MemoryDenyWriteExecute, RestrictSUIDSGID, NoNewPrivileges, Systemaufruffilter — die üblichen Riegel gegen Ausnutzung eines Fehlers im Dienst selbst.
  • Nice=10, IOSchedulingClass=idle — ein Backup soll dem laufenden Betrieb nicht die Maschine wegnehmen.
  • Das Schlüsselmaterial steht in einer Datei, nicht in Environment=. Ein Environment= im Unit-File ist für jeden Benutzer über systemctl show lesbar.

Nachweis

Auf einem Quellverzeichnis mit gewöhnlichen Dateien, einer harten Verknüpfung, einem symbolischen Verweis, einer benannten Pipe, einem Unix-Socket und einem gesperrten Verzeichnis:

Erfassung    4 Dateien, 2 Objekte ohne sicherbaren Inhalt vermerkt
Sicherung    Erfolg (Exit 0) — kein Teilfehler wegen Socket und Pipe
Restore      4 Dateien bitgenau, Symlink erhalten, Rechte erhalten
Sonderdateien im Ziel: keine — korrekt
Harte Verknüpfung: Quelle 2, Ziel 1 — dokumentierte Grenze

Der gesperrte Ordner erzeugte dabei erwartungsgemäß einen Rechtefehler und damit einen Teilfehler — bis er freigegeben wurde. Genau diese Unterscheidung ist der Kern der Phase.

Was offen bleibt

  • Die systemd-Einheit ist ungeprüft. Sie ist inhaltlich korrigiert, aber nie auf einem Linux-System geladen worden.
  • Erweiterte Attribute und ACLs werden nicht gesichert. POSIX-ACLs, SELinux-Kontexte und xattr gehen verloren. Für eine Wiederherstellung auf demselben System ist das meist unerheblich, für eine Systemwiederherstellung nicht.
  • Keine Sicherung offener Dateien mit konsistentem Stand. Es gibt keinen Schattenkopie-Mechanismus wie VSS unter Windows; eine Datenbank, die während der Sicherung schreibt, landet in dem Zustand, in dem sie gerade ist. Für Datenbanken gehört ein eigener Ablauf davor (Dump oder Snapshot des Dateisystems).
  • Harte Verknüpfungen werden aufgelöst (siehe oben).