# Betrieb und Wiederherstellung Für die Installation aus der Gitea Container Registry siehe [Deployment-Anleitung](deployment.md). Alle `docker compose`-Befehle unten werden im Deployment-Verzeichnis mit `compose.yaml` und `.env` ausgeführt. `PROVISIONER_IMAGE` verweist auf das veröffentlichte Image, vorzugsweise per Digest. Die CLI ist im Container enthalten; auf dem Zielserver ist keine eigene Python-Installation erforderlich. ## Konfiguration `proxmox-ais` liest die optionale TOML-Datei aus `APP_CONFIG`, anschließend gleichnamige großgeschriebene Umgebungsvariablen. Relative Pfade beziehen sich auf das Arbeitsverzeichnis. Die mitgelieferte Compose-Datei übernimmt `PROVISIONER_IMAGE`, `PUBLIC_URL` und `MAINTENANCE` aus `.env`. Weitere Anwendungseinstellungen bei Bedarf unter `services.provisioner.environment` in `compose.yaml` ergänzen. | Einstellung | Standard | Bedeutung | | --- | --- | --- | | `DATA_DIR` | `./data` | SQLite, Artefakte und Dienstsperre auf lokalem Speicher | | `MASTER_KEY_FILE` | `./secrets/master.key` | Separat abgelegter Fernet-Schlüssel | | `PUBLIC_URL` | `https://localhost:8080` | Extern erreichbare Basis-URL ohne Pfad | | `SECURE_COOKIES` | `true` | Sessioncookies nur über HTTPS | | `FOUR_EYES` | `true` | Autor darf eigene Versionen nicht veröffentlichen | | `MAINTENANCE` | `false` | Neue Installationsfreigaben aussetzen | | `ANSWER_WINDOW_SECONDS` | `300` | Wiederholungsfenster des Antwortabrufs | | `ENROLLMENT_HOURS` | `4` | Maximale Registrierungsfrist | | `LEASE_SECONDS` | `900` | Gültigkeit der Runner-Ausführungsberechtigung | | `RUNNER_CA_FILE` | nicht gesetzt | Vertrauensanker für Runner bei interner CA | `TESTING` und Bootstrap-Passwörter nicht im Regelbetrieb setzen. Benutzer werden interaktiv mit `init` und danach über die Administrationsoberfläche angelegt. Das Datenverzeichnis darf den Master-Key nicht enthalten. Unter Windows schützt zusätzlich eine auf das Dienstkonto beschränkte NTFS-ACL das Schlüsselverzeichnis. ## TLS und Reverse-Proxy Der Compose-Port ist nur an Loopback gebunden. Ein bestehender Proxy terminiert TLS. Bei externem Proxy das private Container-/Verwaltungsnetz gezielt freigeben. Der Dienst wertet Proxy-Header standardmäßig nicht aus; alle absoluten URLs entstehen aus `PUBLIC_URL`. Bei einer nativen Installation kann der Dienst TLS auch direkt terminieren: ```bash proxmox-ais serve --host 0.0.0.0 --port 8080 \ --tls-cert /etc/proxmox-ais/fullchain.pem \ --tls-key /etc/proxmox-ais/server.key ``` Zertifikatskette und DNS müssen im Installer und auf installierten Hosts gültig sein. Ein ISO-Fingerprint ersetzt nicht automatisch den CA-Vertrauensanker des Runners. Bei interner CA `RUNNER_CA_FILE` konfigurieren und die gesamte Kette vom Installer bis zum Runner prüfen. Keine TLS-Prüfung abschalten. Zertifikatsrotation erfordert die Prüfung vorhandener Medien. Capability-Tokens stehen zwangsläufig in Bootstrap-URLs. Der integrierte Server schreibt daher keine Access-Logs. Im vorgeschalteten Proxy Bootstrap- und Installer-Report-Pfade redigieren oder deren Access-Logging deaktivieren. Keine Authorization-Header protokollieren. Skriptausgaben werden zusätzlich redigiert; Skripte sollten Geheimnisse grundsätzlich nie ausgeben. ## Betrieb Der Dienst verwendet genau einen Anwendungsprozess. Mehrere Uvicorn-Worker oder gleichzeitige Dienste für dasselbe Datenverzeichnis verhindert die Dienstsperre. SQLite benötigt lokalen Speicher mit zuverlässigen Dateisperren; kein NFS/SMB-Volume einsetzen. Als Startwert sind 2 vCPU, 2 GB RAM und 20 GB Speicher ohne ISO-Archiv vorgesehen; Kapazität muss unter realer Last geprüft werden. `/health/live` prüft den Prozess, `/health/ready` Datenbank und Konfiguration. Readiness, freien Speicher, Backupalter und ausbleibende Runner-Meldungen überwachen. DHCP, DNS, NTP und Paketquellen sind externe Voraussetzungen. Der Server baut keine eingehenden SSH-Verbindungen zu Hosts auf. ## Backup Beim Registry-Deployment die Sicherung mit der aktuell eingesetzten Image-Version erstellen, bevor `PROVISIONER_IMAGE` für ein Update geändert wird. Das Hostverzeichnis `/srv/ais-backups` muss vorhanden und für UID/GID 10001 beschreibbar sein. Beispielsweise einmalig vom Administrator anlegen: ```bash sudo install -d -o 10001 -g 10001 -m 0700 /srv/ais-backups docker compose run --rm --no-deps --volume /srv/ais-backups:/backups \ provisioner backup /backups/2026-09-13 ``` Für jede Sicherung einen neuen Zielnamen wählen. Bei einer nativen Python-Installation lautet der entsprechende Befehl `proxmox-ais backup /srv/ais-backups/2026-09-13`. Das Ziel muss neu sein und außerhalb von `DATA_DIR` liegen. Die Anwendung kann weiterlaufen: Die SQLite-Backup-API erzeugt einen konsistenten Snapshot, anschließend werden unveränderliche Artefakte kopiert. Artefakte währenddessen nicht manuell löschen. Die Sicherung enthält `database.sqlite3`, `artifacts/`, `settings.json` und `manifest.json`. Der Master-Key wird ausgeschlossen; sein Fingerprint verhindert die Wiederherstellung mit einem versehentlich falschen Schlüssel. `settings.json` enthält die wirksamen Anwendungseinstellungen einschließlich Standortvorgaben, Fristen und Freigaberegeln. Lokale Daten-/Schlüsselpfade und Bootstrap-Zugangsdaten sind ausgeschlossen. Individuelle TOML-Datei, Umgebungsvariablen, Proxykonfiguration, CA-/TLS-Dateien, ursprüngliche ISOs und Prüfprotokolle separat sichern. Registrierte ISO-Metadaten liegen in der Datenbank. Backup-Prüfsummen erkennen beschädigte Dateien; sie sind keine Signatur gegen einen Angreifer mit Schreibzugriff. Backupverzeichnis deshalb schützen. Den Master-Key aus `proxmox-ais-keys` über einen getrennten verschlüsselten Sicherungsweg verwahren. Eine lokale Kopie in ein bereits vorhandenes Zielverzeichnis ist möglich: ```bash docker compose cp provisioner:/run/secrets/master.key /secure/offline/master.key ``` Die Schlüsselkopie ist genauso vertraulich wie alle gespeicherten Geheimnisse. ## Restore 1. Wartungsmodus aktivieren, neue Freigaben stoppen und Anwendungsdienst herunterfahren. Die Host-Runner separat berücksichtigen: ein bereits gestarteter Befehl kann noch laufen. 2. Bisheriges Datenverzeichnis erhalten. Ein neues leeres Datenverzeichnis wählen und den zum Backup gehörenden Master-Key separat bereitstellen. 3. Die gleiche Anwendungsversion wie beim Backup verwenden. Beim Registry-Deployment deren Digest in `.env` als `PROVISIONER_IMAGE` setzen und herunterladen. Das Schlüsselvolume muss bereits den passenden Master-Key als `master.key`, lesbar für UID/GID 10001, enthalten. In ein neues, leeres Datenvolume wiederherstellen: ```bash docker compose stop provisioner docker compose pull provisioner docker volume create proxmox-ais-data-restored docker compose run --rm --no-deps \ --volume /srv/ais-backups:/backups:ro \ --volume proxmox-ais-data-restored:/var/lib/proxmox-ais \ provisioner restore /backups/2026-09-13 ``` `proxmox-ais-data-restored` muss neu sein; existiert es schon, einen anderen unbenutzten Namen wählen. Nach erfolgreichem Restore in `compose.yaml` unter `volumes.ais-data.name` diesen neuen Namen eintragen. Das bisherige Datenvolume bleibt erhalten. Bei einer nativen Python-Installation alternativ: ```bash export DATA_DIR=/srv/proxmox-ais-restored export MASTER_KEY_FILE=/secure/offline/master.key proxmox-ais restore /srv/ais-backups/2026-09-13 ``` 4. Individuelle Konfiguration und Zertifikate wiederherstellen. Im Wartungsmodus starten, anmelden und Inventar sowie aktuelle Hostzustände abgleichen. Beim Registry-Deployment dafür `MAINTENANCE=true` in `.env` setzen und `docker compose up -d --no-build --wait` ausführen. 5. Aktive Läufe stehen auf `needs_review`. Alte Sessions, Gerätezugänge, Bootstrap-/Enrollment-/Report-Capabilities und Gruppentokens gelten nicht mehr; offene Installationsfreigaben sind gesperrt. Gruppentokens und davon abhängige Medien neu ausgeben. Zuvor ausgelieferte Installer nicht ungeprüft neu booten. 6. Installer und Runner auf jedem betroffenen Host lokal stoppen und den tatsächlichen Zustand prüfen. Danach den verlassenen Lauf über `POST /api/v1/runs/{id}/reconcile` abschließen: aktuelle `expected_version`, aussagekräftige `reason`, `confirmation` mit dem Host-FQDN und `execution_stopped: true` übermitteln. Die Aktion beendet den Lauf als `cancelled`, sperrt seine Berechtigungen und schreibt einen Auditeintrag. Erst danach ist eine neue ausdrückliche Installationsfreigabe möglich. Der automatische Wiederanschluss alter Geräteidentitäten nach Restore ist nicht Teil dieser Version. Außerhalb eines Restore zuerst `/cancel` anfordern. Ein offline befindlicher Runner kann seine bereits erhaltene Lease bis zum Ablauf behalten; eine Abbruchanforderung zieht diese nicht zurück. `/reconcile` weist den Abgleich deshalb zurück, solange eine zuvor ausgestellte Lease oder das Antwortauslieferungsfenster noch gültig ist. Auch nach deren Ablauf ist die ausdrückliche Bestätigung erforderlich, dass Installer und Runner lokal gestoppt und kontrolliert wurden. Eine nicht mehr erreichbare Maschine ist kein Nachweis dafür. Das Verfahren erteilt selbst keine Neuinstallationsfreigabe. Restore überschreibt keine bestehende Datenbank. Die Dienstsperre verhindert Restore während eines laufenden Dienstes im Zielverzeichnis. Ein fehlgeschlagener Restore hinterlässt `RESTORE_FAILED`; der CLI-Start verweigert dann den Betrieb. Nach Ursachenklärung erneut in ein neues leeres Ziel restaurieren. Bereits heruntergeladene Antwortdateien kann der Server nicht zurückziehen. Ebenso beendet eine Credential-Sperrung einen lokal laufenden Skriptprozess nicht unmittelbar. Restore erfordert deshalb den physischen Zustandsabgleich der Hosts. RPO 24 Stunden und RTO zwei Stunden sind Ziele aus dem Konzept. Erst wiederholte Sicherung und zeitlich gemessene Wiederherstellung unter realen Bedingungen weisen diese Ziele nach. ## Updates Der vollständige Ablauf für Registry-Images steht unter [Deployment aktualisieren](deployment.md#updates). Vor dem ersten Neustart `PROVISIONER_IMAGE` auf den Digest des tatsächlich laufenden Images festlegen, wie dort beschrieben. Anschließend `MAINTENANCE=true` in `.env` setzen und die laufende Version mit `docker compose up -d --no-build --wait` neu erstellen. Der Wartungsmodus sperrt neue Freigaben; bereits gestartete Installations- und Modulabläufe werden dadurch nicht beendet. Diese vor dem Update kontrolliert abschließen lassen. Mit der bisherigen Version Daten und Schlüssel sichern und den alten Image-Digest festhalten. Erst danach `PROVISIONER_IMAGE` ändern, das neue Image mit `docker compose pull provisioner` laden, den Dienst mit `docker compose stop provisioner` stoppen und mit `docker compose up -d --no-build --wait` neu starten. Nach Prüfung von Anmeldung, Inventar und Readiness `MAINTENANCE=false` setzen und erneut `up` ausführen. Bei Updates wird `init` nicht wiederholt. Die Datenbank führt eine Migrationstabelle und `PRAGMA user_version`. Neuere unbekannte Schemas werden abgewiesen. Downgrades erfolgen über die zur Sicherung passende Version und den beschriebenen Restore; keine alte Anwendung auf eine bereits migrierte Produktivdatenbank starten.