syncova-backup/docs/installation.md
Jerrit Fritzsche b78a6fb51c
Some checks failed
CI / Backend (Go) (push) Failing after 32s
CI / Frontend (React/TypeScript) (push) Successful in 46s
CI / Sicherheitsprüfungen (push) Successful in 28s
Dokumentation und Aenderungsliste fuer rc8
Der Abschnitt "Wohin darf zurueckgeschrieben werden?" im Runbook ist der
wichtigste Zusatz: Dass ein Ziel an ProtectSystem=strict scheitert und nicht an
den Rechten des Verzeichnisses, sieht man dem Fehler nicht an. Die Tabelle nennt
die vier Faelle samt Grund.

Die Beispiel-Einheit in der Installationsanleitung fuehrte in denselben Fehler —
sie nannte nur das Repository in ReadWritePaths. Eine Anleitung, deren
Ergebnis keine Wiederherstellung zulaesst, ist schlimmer als keine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:55:52 +02:00

413 lines
15 KiB
Markdown

# Installation
Diese Anleitung führt von einem leeren Server zu einer Anlage, die sichert und
wiederherstellt. Sie beschreibt den Serverteil; die Agenten stehen in
[agent-installation.md](agent-installation.md).
## Voraussetzungen
| | |
| --- | --- |
| Betriebssystem | Linux (amd64 oder arm64). Der Server wird für Windows **nicht** ausgeliefert |
| PostgreSQL | **15 oder neuer**, erreichbar vom Server. Geprüft gegen 17 (Entwicklung und CI) und 15 (Debian 12) |
| Speicher für das Repository | eigener Datenträger oder eigene Freigabe — nicht dasselbe Gerät wie die zu sichernden Daten |
| Reverse Proxy | optional, aber empfohlen, sofern der Dienst nicht selbst TLS bedient |
**Das Repository gehört nicht auf denselben Datenträger wie die Quelle.** Ein
Datenträgerausfall nähme sonst Original und Sicherung gemeinsam mit. Das ist
keine Feinheit der Einrichtung, sondern der Zweck der Übung.
**Und nicht nach `/tmp`, `/var/tmp`, `/run` oder auf ein tmpfs.**
`systemd-tmpfiles` räumt die ersten beiden regelmäßig auf, ein tmpfs liegt im
Arbeitsspeicher. Die Sicherungen verschwänden dort von selbst — ohne Meldung,
bis jemand sie braucht. `setup.sh` lehnt solche Orte ab; die Diensteinheit
könnte mit `PrivateTmp=yes` ohnehin nicht starten.
## Der kurze Weg: setup.sh
Das Paket bringt ein Einrichtungsskript mit. Es geht genau die Schritte dieser
Anleitung, und was es angelegt hat, baut es bei einem Abbruch wieder zurück.
```bash
tar -xzf syncova-<version>-linux-amd64.tar.gz
cd syncova-<version>-linux-amd64
sudo ./setup.sh
```
Es fragt nach Datenbank, Repository und dem ersten Administrator, installiert
PostgreSQL auf Wunsch mit, erzeugt den Verschlüsselungsschlüssel und richtet den
Dienst ein. Für einen unbeaufsichtigten Lauf: `sudo ./setup.sh --unbeaufsichtigt`
mit den Werten aus der Umgebung (`./setup.sh --hilfe` zeigt sie).
Später aktualisieren: `sudo ./update.sh` — es sichert vorher Datenbank und
Konfiguration und nimmt sich selbst zurück, wenn der Dienst danach nicht
hochkommt. Entfernen: `sudo ./uninstall.sh` — es lässt Datenbank, Repository
und Konfiguration liegen, sofern man nicht ausdrücklich etwas anderes verlangt.
**Eine bestehende Installation überschreibt `setup.sh` nicht.** Es bricht ab und
verweist auf `update.sh`; der Unterschied ist, dass ein Update vorher sichert.
Der Rest dieses Dokuments beschreibt dieselben Schritte von Hand — für alle, die
wissen wollen, was das Skript tut, oder die davon abweichen müssen.
## 1. Paket auspacken
```bash
tar -xzf syncova-<version>-linux-amd64.tar.gz
cd syncova-<version>-linux-amd64
# Prüfsummen des Inhalts kontrollieren
shasum -a 256 -c SHA256SUMS
./bin/syncova-api --version
```
Die Versionsabfrage braucht keine Konfiguration. Sie ist der schnellste Weg
festzustellen, welche Fassung auf einem Server liegt.
Der Paketinhalt:
```text
bin/ Programme
web/ Weboberfläche (statische Dateien)
migrations/ SQL-Migrationen zum Nachlesen (sie stecken zusätzlich in den Programmen)
docs/ diese Dokumentation
deployment/ Beispiel für die Entwicklungsumgebung und die systemd-Einheit des Agenten
```
## 2. Datenbank anlegen
```sql
CREATE USER syncova WITH PASSWORD '<starkes-passwort>';
CREATE DATABASE syncova OWNER syncova;
```
Syncova legt das Schema **nicht** beim Start an. Das ist Absicht: Ein Dienst,
der beim Hochfahren stillschweigend das Schema ändert, macht aus einem
versehentlichen Neustart eine Migration.
## 3. Verschlüsselungsschlüssel erzeugen
```bash
./bin/syncova-admin generate-key
```
Ausgegeben wird ein Wert der Form `v1:<base64>`.
> **Ohne diesen Schlüssel sind die abgelegten Geheimnisse verloren** — MFA-Geheimnisse,
> Zugangsdaten der Virtualisierungsverbünde und die Datenschlüssel der
> Repositories. Er gehört an einen Ort **außerhalb** dieser Anlage: in einen
> Passwortspeicher oder einen Tresor. In ein Backup, das Syncova selbst
> erzeugt, gehört er ausdrücklich nicht — dann bräuchte man ihn, um an ihn
> heranzukommen.
## 4. Konfiguration
Syncova liest ausschließlich Umgebungsvariablen; eine Konfigurationsdatei gibt
es nicht. Alle tragen das Präfix `SYNCOVA_`.
```bash
# /etc/syncova/syncova.env — nur für root lesbar (chmod 0600)
SYNCOVA_ENV=production
SYNCOVA_DB_HOST=127.0.0.1
SYNCOVA_DB_PORT=5432
SYNCOVA_DB_NAME=syncova
SYNCOVA_DB_USER=syncova
SYNCOVA_DB_PASSWORD=<passwort>
SYNCOVA_DB_SSLMODE=require
SYNCOVA_ENCRYPTION_KEYS=v1:<base64-schluessel>
SYNCOVA_ENCRYPTION_CURRENT_KEY=v1
SYNCOVA_HTTP_LISTEN_ADDRESS=127.0.0.1:8080
SYNCOVA_LOG_LEVEL=info
SYNCOVA_LOG_FORMAT=json
```
Die wichtigsten weiteren Werte:
| Variable | Vorgabe | Wozu |
| --- | --- | --- |
| `SYNCOVA_HTTP_TLS_CERT_FILE` / `_KEY_FILE` | leer | TLS im Dienst selbst. **Nur eines von beiden zu setzen verweigert den Start** — sonst liefe der Dienst im Klartext, obwohl der Betreiber Verschlüsselung eingerichtet zu haben glaubt |
| `SYNCOVA_HTTP_REQUESTS_PER_MINUTE` | 600 | allgemeine Ratenbegrenzung |
| `SYNCOVA_AUTH_REQUIRE_MFA_FOR_PRIVILEGED_USERS` | false | zweiter Faktor für Konten mit Benutzer-, Rollen- oder Sicherheitsrechten |
| `SYNCOVA_RESTORE_ALLOWED_ROOTS` | leer | begrenzt Wiederherstellungsziele auf bestimmte Verzeichnisbäume |
**Lauscht der Dienst ohne TLS auf allen Schnittstellen, warnt er beim Start in
Großbuchstaben.** Der Betrieb hinter einem Reverse Proxy ist der Normalfall und
völlig in Ordnung — solange der Dienst dann nur lokal erreichbar ist.
## 5. Schema anlegen
```bash
set -a; . /etc/syncova/syncova.env; set +a
./bin/syncova-migrate up
./bin/syncova-migrate status
```
Passt das Schema später nicht zur Programmversion, verweigert `syncova-api` den
Start mit einer erklärenden Meldung. Das ist kein Schikane, sondern der Schutz
davor, dass eine neue Fassung auf ein altes Schema schreibt.
## 6. Ersten Administrator anlegen
```bash
./bin/syncova-admin create-admin --username admin
```
Das Passwort wird abgefragt und wiederholt — es steht **nicht** in einer
Umgebungsvariablen und nicht in den Aufrufparametern. Beides landete sonst in
der Prozessliste und in der Shell-Historie.
In einem Skript geht es über die Standardeingabe:
```bash
printf '%s\n%s\n' "$PASSWORD" "$PASSWORD" | ./bin/syncova-admin create-admin --username admin
```
**Es gibt kein Standardkonto im Programm.** Ein ausgeliefertes Kennwort wäre
bekannt, sobald es einmal jemand nachschlägt.
Das Kommando legt nur den **ersten** Administrator an; danach entstehen weitere
Konten über die Oberfläche oder die API.
## 7. Dienst einrichten
```ini
# /etc/systemd/system/syncova-api.service
[Unit]
Description=Syncova Control Plane
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=syncova
Group=syncova
EnvironmentFile=/etc/syncova/syncova.env
ExecStart=/opt/syncova/bin/syncova-api
Restart=on-failure
RestartSec=5
# Härtung: Der Dienst braucht Netz, sein Repository und eine Fläche für
# Wiederherstellungen — sonst nichts.
#
# ReadWritePaths ist die Stelle, an der eine Wiederherstellung scheitert, wenn
# man sie vergisst: Mit ProtectSystem=strict ist alles andere für den Dienst
# schreibgeschützt, und die Rechte des Zielverzeichnisses helfen dann nicht.
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/srv/syncova-repository /srv/syncova-restore
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
MemoryDenyWriteExecute=yes
[Install]
WantedBy=multi-user.target
```
`AF_UNIX` gehört in die Liste: Mit `systemd-resolved` läuft die Namensauflösung
über einen Unix-Socket. Ohne diesen Eintrag findet der Dienst seine Datenbank
nicht und meldet einen Netzfehler, während das Netz einwandfrei arbeitet.
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now syncova-api
curl -s http://127.0.0.1:8080/health/ready
```
## 8. Oberfläche ausliefern
`setup.sh` erledigt diesen Schritt selbst; er steht hier für den Fall, dass Sie
von Hand einrichten oder die Vorgabe ersetzen wollen. Ist die Anlage bereits
eingerichtet und die Oberfläche fehlt noch:
```bash
sudo /opt/syncova/setup.sh --weboberflaeche
```
Das richtet nginx ein, stellt ein selbst signiertes Zertifikat aus und nennt
dessen Fingerabdruck. Mit `--ohne-weboberflaeche` unterbleibt der Schritt.
**Die API bleibt dabei an `127.0.0.1:8080` gebunden.** Erreichbar ist sie nur
durch nginx hindurch — ein Angreifer im Netz kommt nicht an ihr vorbei, und die
Verschlüsselung lässt sich nicht umgehen, indem man Port 8080 direkt anspricht.
Die Oberfläche ist ein Satz statischer Dateien unter `web/`. Sie braucht einen
**SPA-Fallback**, sonst ergibt ein Neuladen auf `/recovery-points` einen 404:
```nginx
server {
listen 443 ssl;
server_name syncova.example;
root /opt/syncova/web;
location / {
try_files $uri /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
```
Zwei Punkte, die in dieser Kurzfassung fehlen und die `setup.sh` mitsetzt:
- **`http2` schreibt sich je nach nginx-Fassung anders.** Ab 1.25.1 als eigene
Anweisung `http2 on;`, davor als Parameter `listen 443 ssl http2;`. Die neue
Schreibweise auf einer älteren Fassung ergibt „unknown directive http2" —
nginx startet dann nicht. Debian 12 liefert 1.22.
- **Zeitüberschreitungen hochsetzen.** Eine Vorabprüfung liest jeden Block; die
Vorgabe von 60 Sekunden bricht sie bei großen Beständen ab.
### Selbst signiertes Zertifikat
Es schützt gegen Mitlesen, nicht gegen einen Mittelsmann — niemand bestätigt,
dass es zu diesem Server gehört. Deshalb nennt `setup.sh` den Fingerabdruck;
vergleichen Sie ihn beim ersten Aufruf mit dem, den der Browser anzeigt. Danach
ist die Warnung unbedenklich.
Für den Dauerbetrieb gehört ein Zertifikat einer Zertifizierungsstelle hierher
— eine eigene CA oder Let's Encrypt. Legen Sie es nach
`/etc/syncova/tls/server.crt` und `server.key` und laden Sie nginx neu.
### Firewall
`setup.sh` **meldet** den Zustand der Firewall und **öffnet nichts**. Eine
Einrichtung, die selbsttätig einen Port ins Netz öffnet, hebelt genau die
Entscheidung aus, für die jemand die Firewall aufgesetzt hat. Port 443 geben
Sie selbst frei:
```bash
sudo ufw allow 443/tcp # ufw
sudo firewall-cmd --permanent --add-service=https # firewalld
sudo firewall-cmd --reload
```
## 8b. Wohin Wiederherstellungen schreiben dürfen
`setup.sh` legt `/srv/syncova-restore` an und trägt es in `ReadWritePaths` der
systemd-Einheit ein. Ohne diesen Eintrag scheitert **jede** Wiederherstellung:
Der Dienst läuft mit `ProtectSystem=strict`, und die Rechte des
Zielverzeichnisses helfen dann nicht.
Weitere Ziele beim Einrichten nennen:
```bash
sudo ./setup.sh --wiederherstellungsziel /srv/wiederherstellung \
--wiederherstellungsziel /mnt/nas/restore
```
Nicht möglich sind `/tmp` (privater Namensraum des Dienstes) und die
Systemverzeichnisse `/etc`, `/usr`, `/var/lib`, `/root` — Letztere sperrt der
Zielschutz, weil eine Wiederherstellung dorthin das System überschriebe, auf dem
die Anlage läuft.
## 9. Erstes Repository
Ein Repository entsteht **auf einem Datenträger**, nicht in einer
Datenbankzeile:
```bash
sudo install -d -o syncova -g syncova -m 0700 /srv/syncova-repository
sudo -u syncova ./bin/syncova-repo create \
--path /srv/syncova-repository --name "Hauptziel" --hardened
```
`--hardened` setzt den Aufbewahrungsschutz: Manifeste, Blöcke, Schutzvermerke,
Descriptor und Datenschlüssel bekommen das Unveränderlich-Kennzeichen des
Dateisystems. **Ein so angelegtes Repository lässt sich nicht mit `rm -rf`
entfernen** — auch nicht von root, ohne den Schutz vorher aufzuheben.
Danach wird es in der Control Plane eingetragen:
```bash
curl -X POST https://syncova.example/api/v1/repositories \
-H "Authorization: Bearer <token>" -H 'Content-Type: application/json' \
-d '{"name":"Hauptziel","location":"/srv/syncova-repository"}'
```
Der Endpunkt **legt nichts an, sondern übernimmt**: Er öffnet das vorhandene
Repository und liest dessen Kennung aus dem Descriptor. Liegt dort keines,
lehnt er ab, statt einen Eintrag ohne Ablage dahinter zu erzeugen.
Anschließend die Durchsetzungsstufe messen — sie wird gemessen, nicht behauptet:
```bash
curl -X POST https://syncova.example/api/v1/repositories/<id>/enforcement/measure \
-H "Authorization: Bearer <token>"
```
## 10. Erster Auftrag
Über die Oberfläche (Backup-Assistent, zehn Schritte) oder die API:
```bash
curl -X POST https://syncova.example/api/v1/jobs \
-H "Authorization: Bearer <token>" -H 'Content-Type: application/json' \
-H "Idempotency-Key: $(uuidgen)" -d '{
"name": "Dateiserver täglich",
"repository_id": "<repository>",
"schedule": {"type": "daily", "time": "02:00", "time_zone": "Europe/Berlin"},
"sources": [{"type": "filesystem", "id": "/srv/daten", "name": "Daten"}]
}'
```
**Ohne Zeitzone rechnet der Server in UTC**, nicht in der Ortszeit des Servers.
Sonst liefe dieselbe Konfiguration auf zwei Servern zu verschiedenen Zeiten.
## 11. Der Schritt, den die meisten auslassen
```bash
# Eine Wiederherstellung durchführen, bevor man sie braucht
curl -X POST https://syncova.example/api/v1/verification \
-H "Authorization: Bearer <token>" -H 'Content-Type: application/json' \
-d '{"backup_id":"<id>","verification_type":"restore_test"}'
```
Erst danach steigt ein Wiederherstellungspunkt auf `recoverable`. Alles davor
ist ein Indiz, kein Nachweis.
## Aktualisierung auf eine neue Fassung
```bash
# 1. Datenbank sichern — ein Rollback ersetzt keine Sicherung
pg_dump -Fc syncova > syncova-vor-update.dump
# 2. Dienst anhalten
sudo systemctl stop syncova-api
# 3. Programme austauschen
sudo tar -xzf syncova-<neu>-linux-amd64.tar.gz -C /opt/syncova --strip-components=1
# 4. Migrationen anwenden
sudo -u syncova /opt/syncova/bin/syncova-migrate up
# 5. Starten und nachsehen
sudo systemctl start syncova-api
curl -s http://127.0.0.1:8080/health/ready
```
Reihenfolge einhalten: Die Migrationen laufen mit dem **neuen** Programm, und
der Dienst startet erst danach. Läuft er noch, während sich das Schema ändert,
arbeitet er auf einem Stand, den er nicht kennt.
Geht etwas schief: `syncova-migrate down` nimmt **genau eine** Migration zurück
und verlangt in der Produktion `SYNCOVA_MIGRATE_CONFIRM_DOWN=yes`. Daten in
Tabellen, die es vorher nicht gab, sind danach weg — deshalb Schritt 1.
## Was nach der Installation zu tun bleibt
- **Zweiten Faktor einrichten**, mindestens für alle Konten mit Löschrecht.
- **Benachrichtigungsweg einrichten**, sonst erfährt niemand von einem Ausfall.
- **`syncova-dr export` regelmäßig laufen lassen** — die Konfigurationssicherung
im Repository ist das, was nach einem Totalverlust des Servers übrig bleibt.
- **Den Verschlüsselungsschlüssel außerhalb der Anlage hinterlegen.**