Files
Proxmox-AIS-Server/docs/operations.md
T
BartelLuis 06c3474636
CI / javascript-check (push) Successful in 51s
CI / container-policy (push) Successful in 2s
CI / container-verify (push) Canceled after 0s
CI / container-publish (push) Canceled after 0s
CI / python-tests (push) Canceled after 6m59s
feat: add Proxmox provisioning service with CI and deployment tooling
2026-09-14 20:09:12 +02:00

11 KiB

Betrieb und Wiederherstellung

Für die Installation aus der GitHub Container Registry (GHCR) siehe Deployment-Anleitung. 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:

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:

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:

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:
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:

export DATA_DIR=/srv/proxmox-ais-restored
export MASTER_KEY_FILE=/secure/offline/master.key
proxmox-ais restore /srv/ais-backups/2026-09-13
  1. 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.
  2. 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.
  3. 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. 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.