feat(engine): Adapter debian und Profil-Engine
Profile sind deklarative Schritte je Distributionsfamilie; der Adapter übersetzt sie in ein idempotentes Shell-Skript. Ein neuer Adapter (rhel, alpine) ist eine Datei und ein Eintrag in der Registry. Variablen werden typisiert geprüft, in run-Schritten jeder Wert für die Shell gequotet. SSH-Härtung läuft zuletzt, damit sich eine Provisionierung per SSH nicht selbst aussperrt. Geprüft mit shellcheck und echt in Containern (Debian 12/13, Ubuntu 22.04/24.04/26.04) inklusive zweitem Lauf. Dabei aufgefallen: frische Images haben keine Paketlisten – der Vorspann macht jetzt apt-get update. Closes #005 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,23 +1,31 @@
|
||||
# #005 Adapter debian und Profil-Engine
|
||||
**Status:** offen
|
||||
**Status:** erledigt
|
||||
**Meilenstein:** v0.2.0
|
||||
**Labels:** feature, engine
|
||||
**Geschätzt:** 6 h **Tatsächlich:** –
|
||||
**Geschätzt:** 6 h **Tatsächlich:** 0 h 20 min
|
||||
|
||||
## Ziel
|
||||
Interface `FamilienAdapter`, Umsetzung `debian`, Profil `base` wird zu einem idempotenten Skript.
|
||||
Interface `FamilienAdapter`, Umsetzung `debian`, Profile werden zu einem idempotenten Skript.
|
||||
|
||||
## Aufgaben
|
||||
- [ ] Interface + Registry; `debian`-Adapter (apt, systemd, deb822, locale, keyboard, sshd_config.d)
|
||||
- [ ] Profilauflösung (requires, Reihenfolge, Variablen, Shell-Quoting)
|
||||
- [ ] Profil `base` (Updates, Guest-Agent, Europe/Berlin, de_DE.UTF-8, Tastatur de, unattended-upgrades, sudo-User, SSH-Härtung, chrony, fail2ban optional)
|
||||
- [ ] Snapshot-Tests der erzeugten Skripte; shellcheck auf die Ausgabe
|
||||
- [x] Interface + Registry; `debian`-Adapter (apt mit Lock-Timeout, deb822, systemd, locale, keyboard, sshd_config.d, sudoers mit visudo-Prüfung)
|
||||
- [x] Profilauflösung (requires transitiv, Zyklen, Variablen mit Typen und Standardwerten, Familien-Prüfung)
|
||||
- [x] Platzhalter `{{ var.x }}`, `{{ os.* }}`, `{{ guest.* }}`, `a ?? b`; in `run` jeder Wert shell-gequotet
|
||||
- [x] Profil `base` (Updates, Guest-Agent, Europe/Berlin, de_DE.UTF-8, Tastatur de, unattended-upgrades, chrony, SSH-Härtung, fail2ban optional)
|
||||
- [x] Tests der erzeugten Skripte; shellcheck auf die Ausgabe aller Distributionen
|
||||
- [x] `pnpm test:container`: Skripte wirklich ausführen (Debian 12/13, Ubuntu 22.04/24.04/26.04), Ergebnis prüfen, zweiter Lauf auf Idempotenz
|
||||
- [x] `docs/katalog.md`: neue Distribution, neuer Adapter, eigenes Profil
|
||||
|
||||
## Akzeptanzkriterien
|
||||
- [ ] Neues Profil/neuer Adapter braucht keine Änderung an Engine-Code
|
||||
- [x] Neues Profil/neuer Adapter braucht keine Änderung an Engine-Code
|
||||
- [x] base + docker + zabbix-agent laufen auf allen fünf Distributionen durch und sind idempotent (Container, 2026-10-12)
|
||||
|
||||
## Abhängigkeiten
|
||||
#004
|
||||
|
||||
## Notizen / Entscheidungen
|
||||
–
|
||||
- **Fund im Container:** frische Images haben keine Paketlisten – der Vorspann macht jetzt immer `apt-get update`, sonst scheiterte schon die Benutzeranlage an „Unable to locate package sudo“.
|
||||
- **Zeitzone, Locale und Tastatur werden gegen strenge Muster geprüft**, bevor sie ins Skript kommen (shellcheck fand verschachteltes Quoting im sed-Ausdruck).
|
||||
- **SSH-Härtung läuft immer zuletzt:** läuft die Provisionierung per SSH als root (LXC ohne Node-SSH), schlösse sie sich sonst selbst aus.
|
||||
- **Marken `::proxdeploy:: profil <id>`** im Skript zeigen im Live-Log, welches Profil gerade läuft.
|
||||
- Dateiinhalte gehen base64-kodiert ins Skript – beliebige Zeichen kommen unverändert an.
|
||||
|
||||
@@ -8,7 +8,7 @@ Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden a
|
||||
| [#002](002-verschluesselung-server.md) | Server-Verwaltung und Verschlüsselung | erledigt | v0.1.0 | 6 h | 0 h 11 min |
|
||||
| [#003](003-verbindungstest.md) | Verbindungstest und Fähigkeitserkennung | erledigt | v0.1.0 | 6 h | 0 h 25 min |
|
||||
| [#004](004-katalog.md) | Katalog: Distributionen und Profile als Daten | erledigt | v0.2.0 | 5 h | 0 h 09 min |
|
||||
| [#005](005-adapter-profile.md) | Adapter debian und Profil-Engine | offen | v0.2.0 | 6 h | – |
|
||||
| [#005](005-adapter-profile.md) | Adapter debian und Profil-Engine | erledigt | v0.2.0 | 6 h | 0 h 20 min |
|
||||
| [#006](006-auftraege-sse.md) | Aufträge, Phasen, worker und Live-Fortschritt | offen | v0.2.0 | 8 h | – |
|
||||
| [#007](007-template-bau.md) | Template-Bau für VMs | offen | v0.2.0 | 8 h | – |
|
||||
| [#008](008-vm-ausrollen.md) | Deploy-Assistent und VM ausrollen | offen | v0.2.0 | 10 h | – |
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
# Katalog: Distributionen und Profile
|
||||
|
||||
Distributionen und Profile sind Daten, kein Code. Jede liegt als YAML-Datei unter `catalog/` und wird
|
||||
beim Start von ProxDeploy in die Datenbank abgeglichen. Die Datei ist maßgeblich für den Inhalt; in der
|
||||
Oberfläche (*Katalog*) lassen sich Einträge nur aktivieren und deaktivieren. Eine fehlerhafte Datei
|
||||
wird mit Dateiname und Fehlerstelle im Log gemeldet und übersprungen.
|
||||
|
||||
Regeln für beide Arten:
|
||||
|
||||
- Dateiname = `id` + `.yaml` (`catalog/distros/debian-13.yaml` → `id: debian-13`)
|
||||
- unbekannte Schlüssel sind ein Fehler – ein Tippfehler fällt so sofort auf
|
||||
- nur HTTPS-Quellen
|
||||
|
||||
## Neue Distribution hinzufügen
|
||||
|
||||
Beispiel Debian 13:
|
||||
|
||||
```yaml
|
||||
id: debian-13
|
||||
name: Debian 13 „Trixie“
|
||||
family: debian # wählt den Provisionierungs-Adapter
|
||||
os:
|
||||
id: debian # für Platzhalter in Profilen: {{ os.id }}
|
||||
version_id: "13" # {{ os.version_id }} – in Anführungszeichen, sonst wird 22.04 zur Zahl
|
||||
codename: trixie # {{ os.codename }}
|
||||
arch: amd64
|
||||
vm:
|
||||
image_url: https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
|
||||
checksum_url: https://cloud.debian.org/images/cloud/trixie/latest/SHA512SUMS
|
||||
checksum_type: sha512 # sha256 | sha512
|
||||
format: qcow2 # qcow2 | raw | vmdk
|
||||
default_user: debian
|
||||
guest_agent_preinstalled: false # sonst baut ProxDeploy ihn beim Template-Bau ein
|
||||
bios: ovmf # ovmf (UEFI, Standard) | seabios
|
||||
min_disk_gb: 4
|
||||
lxc:
|
||||
template_pattern: "debian-13-standard_*_amd64.tar.zst" # wie in `pveam available`
|
||||
ostype: debian
|
||||
```
|
||||
|
||||
`vm` oder `lxc` darf fehlen, wenn die Distribution nur eins davon kann. Bei mehreren passenden
|
||||
LXC-Templates gewinnt die höchste Version. Die Prüfsummen-Datei darf im GNU-Format (`<hash> <datei>`
|
||||
oder `<hash> *<datei>`) oder im BSD-Format (`SHA256 (<datei>) = <hash>`) vorliegen.
|
||||
|
||||
Gehört die Distribution zu einer Familie, für die es noch keinen Adapter gibt (etwa `rhel` für Rocky
|
||||
oder Alma, `alpine`), braucht es zusätzlich einen Adapter:
|
||||
|
||||
1. `src/engine/adapter/<familie>.ts` anlegen, das Interface `FamilienAdapter` aus
|
||||
`src/engine/adapter/schnittstelle.ts` umsetzen (Pakete, Dienste, Paketquellen, Zeitzone, Locale,
|
||||
Tastatur, Benutzer, SSH-Härtung, Guest-Agent, automatische Updates)
|
||||
2. in `src/engine/adapter/index.ts` eintragen
|
||||
3. Tests nach dem Muster von `tests/engine/profile.test.ts`, dazu ein Image in
|
||||
`tests/container/profile-pruefen.sh`
|
||||
4. in den Profilen unter `families:` eine Umsetzung für die neue Familie ergänzen – Profile ohne
|
||||
Umsetzung erscheinen im Assistenten ausgegraut
|
||||
|
||||
## Eigenes Profil anlegen
|
||||
|
||||
Ein Profil ist eine Liste von Schritten je Distributionsfamilie. Der Adapter übersetzt sie in ein
|
||||
Shell-Skript, das bei VMs über den Guest-Agent und bei Containern per SSH läuft.
|
||||
|
||||
```yaml
|
||||
id: nginx
|
||||
name: nginx
|
||||
description: Webserver mit Startseite
|
||||
requires: [base] # wird automatisch davor ausgeführt
|
||||
variables:
|
||||
- { name: titel, label: Seitentitel, default: Willkommen }
|
||||
- { name: tls, label: Zertifikat per certbot, type: boolean, default: false }
|
||||
lxc:
|
||||
features: { nesting: false } # Container-Features, die das Profil braucht
|
||||
families:
|
||||
debian:
|
||||
- packages: [nginx]
|
||||
- file:
|
||||
path: /var/www/html/index.html
|
||||
content: |
|
||||
<h1>{{ var.titel }}</h1>
|
||||
<p>{{ guest.name }} · {{ os.id }} {{ os.version_id }}</p>
|
||||
- service: { name: nginx, enable: true, start: true }
|
||||
- packages: [certbot, python3-certbot-nginx]
|
||||
if: tls
|
||||
```
|
||||
|
||||
### Schritte
|
||||
|
||||
| Schritt | Wirkung |
|
||||
|---|---|
|
||||
| `update: true` | Paketlisten aktualisieren und alle Pakete aktualisieren |
|
||||
| `packages: [a, b]` | Pakete installieren |
|
||||
| `service: { name, enable, start, restart }` | Dienst aktivieren, starten oder neu starten |
|
||||
| `repo: { name, key_url, uris, suites, components }` | Paketquelle mit Schlüssel einrichten (deb822) |
|
||||
| `repo_package: <url>` | ein Paket installieren, das selbst eine Quelle einrichtet (z. B. zabbix-release) |
|
||||
| `file: { path, content, mode }` | Datei schreiben (Inhalt wird unverändert übertragen) |
|
||||
| `timezone`, `locale`, `keyboard` | Zeitzone, Sprache, Tastaturbelegung |
|
||||
| `ssh_harden: { password_auth, root_login }` | SSH absichern – läuft immer ganz am Ende |
|
||||
| `guest_agent: true` | QEMU-Guest-Agent (in Containern übersprungen) |
|
||||
| `unattended_upgrades: true` | automatische Sicherheitsupdates |
|
||||
| `run: <befehl>` | freier Shell-Befehl – nicht portabel, nur wenn nichts anderes passt |
|
||||
|
||||
Jeder Schritt kann `if: <variable>` (oder `if: "!<variable>"`) tragen und läuft dann nur, wenn die
|
||||
Variable wahr bzw. falsch ist.
|
||||
|
||||
### Platzhalter
|
||||
|
||||
`{{ var.<name> }}` (Profil-Variable), `{{ os.id }}`, `{{ os.version_id }}`, `{{ os.codename }}`,
|
||||
`{{ guest.name }}`, `{{ guest.user }}`. `{{ var.a ?? var.b }}` nimmt `b`, wenn `a` leer ist. Ein
|
||||
unbekannter Platzhalter ist ein Fehler, kein leerer Text.
|
||||
|
||||
In `run`-Schritten wird jeder eingesetzte Wert für die Shell gequotet – eine Eingabe im Formular kann
|
||||
so nie zu Code werden. Variablen dürfen keine Zeilenumbrüche enthalten.
|
||||
|
||||
### Variablentypen
|
||||
|
||||
`string` (Standard), `number`, `boolean`, `select` (mit `options: [...]`). `required: true` macht
|
||||
das Feld im Assistenten zur Pflicht; `default` füllt es vor; `help` erscheint darunter.
|
||||
|
||||
### Prüfen
|
||||
|
||||
```bash
|
||||
pnpm exec tsx scripts/profil-rendern.ts debian-13 nginx titel=Hallo # Skript ansehen
|
||||
pnpm test:container debian-13 # wirklich ausführen
|
||||
```
|
||||
Reference in New Issue
Block a user