Bisher endete setup.sh mit einer laufenden API auf 127.0.0.1:8080 und der Aufgabe, einen Webserver von Hand davorzusetzen. Das war der haeufigste Punkt, an dem eine Einrichtung liegen blieb. setup.sh richtet jetzt nginx ein und stellt ein selbst signiertes Zertifikat aus. Es gilt fuer Rechnernamen, vollstaendigen Namen und jede globale IPv4-Adresse (subjectAltName — moderne Browser lesen den CN nicht mehr), 3650 Tage; der SHA-256-Fingerabdruck wird genannt. Drei Entscheidungen: - Die API bleibt an 127.0.0.1:8080 gebunden. Sie auf alle Schnittstellen zu legen waere der kuerzere Weg und der falsche: Die Verschluesselung liesse sich dann umgehen, indem man Port 8080 direkt anspricht. - Die Firewall wird gemeldet, nicht geaendert. Eine Einrichtung, die selbsttaetig einen Port ins Netz oeffnet, hebelt genau die Entscheidung aus, fuer die jemand die Firewall aufgesetzt hat. - Scheitert die Oberflaeche, scheitert nicht die Einrichtung. Geprueft wird mit nginx -t, bevor die Konfiguration uebernommen wird; haelt sie nicht, wird sie entfernt und der Nachholweg gezeigt. Zwei Funde beim Erproben: - http2 on; gibt es erst ab nginx 1.25.1. Debian 12 liefert 1.22, wo HTTP/2 ein Parameter von listen ist — die neue Schreibweise ergibt dort "unknown directive http2", und nginx startet nicht. Die Fassung wird jetzt gelesen. - setup.sh kopierte nur diagnose.sh neben die Programme. Der eigene Hinweis "Spaeter nachholen: /opt/syncova/setup.sh --weboberflaeche" verwies damit auf eine Datei, die es nicht gab; schwerer wiegt uninstall.sh — wer das ausgepackte Paket aufraeumte, haette die Anlage nie wieder entfernen koennen. Jetzt kommen alle vier Skripte mit. Nachgewiesen im Container gegen Debian 12 mit nginx 1.22: Neuinstallation von Grund auf, Oberflaeche und /api/ von aussen ueber HTTPS erreichbar (200), SPA-Fallback traegt, Anmeldung und Repository-Anlage durch nginx hindurch, Durchsetzungsstufe gemessen, HTTP leitet mit 301 auf HTTPS, Fingerabdruck stimmt mit dem genannten ueberein, Neuausstellung des Zertifikats geprueft. Regressionstests fuer beide Funde, beide durch Mutation als fangend bestaetigt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
389 lines
14 KiB
Markdown
389 lines
14 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 und sein Repository, sonst nichts.
|
|
NoNewPrivileges=yes
|
|
PrivateTmp=yes
|
|
ProtectSystem=strict
|
|
ProtectHome=yes
|
|
ReadWritePaths=/srv/syncova-repository
|
|
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
|
|
```
|
|
|
|
## 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.**
|