# pve-confsync Synchronisation d'une partie de la configuration d'un cluster Proxmox VE 9.x « référence » vers N clusters « clients », via l'API PVE et des tokens API. Sens unique référence → clients. Pas de VM, pas de disques. **État : phase 0 (CLI).** Export, diff et apply en YAML pour les options datacenter et le SDN (zones VLAN/VXLAN, VNets, subnets). Testé contre un faux serveur PVE ; **pas encore testé sur les clusters de labo.** ## Périmètre | Type (`kind`) | API | Politique par défaut | |---|---|---| | `datacenter-options` | `/cluster/options` | `tag-style`, `registered-tags`, `user-tag-access`, `next-id` en `sync`, le reste ignoré ; `webauthn`, `u2f`, `http_proxy` jamais synchronisés | | `sdn-zone` (vlan, vxlan) | `/cluster/sdn/zones` | `nodes`, `bridge`, `peers`, `fabric`, `dns`, `reversedns`, `dnszone` en `override` (mapping client) ; le reste en `sync` | | `sdn-vnet` | `/cluster/sdn/vnets` | `sync` | | `sdn-subnet` | `/cluster/sdn/vnets/{vnet}/subnets` | `sync` | Hors périmètre v1 : EVPN (et les zones simple, QinQ, faucet, avec leurs VNets, ignorées avec un avertissement), firewall, storage, pools/ACL, jobs, secrets. Détail des routes, schémas et droits, avec les sources : [docs/api-pve.md](docs/api-pve.md). ## Fonctionnement ``` lecture source -> normalisation -> diff -> plan -> apply -> vérification ``` - **Lecture** : la référence est lue en configuration SDN *appliquée* (`running=1`) ; les clients en configuration courante. Chaque cluster est refusé si un nœud en ligne n'est pas en PVE 9.x (`/nodes/{node}/version`). - **Normalisation** : objets JSON génériques → champs canoniques (chaînes, listes triées, property strings à clés triées). Les champs calculés par PVE sont retirés. - **Politiques** : `sync` (copie de la référence), `ignore`, `override` (valeur fournie uniquement par le `mapping` du client). Le mapping d'un client est prioritaire sur la référence. Résolution : politique du client → politique globale → défaut de l'adaptateur. - **Plan** : création / mise à jour (champs modifiés, champs à supprimer) ; suppression d'objets uniquement avec `prune` (option explicite), dépendants d'abord. Erreurs bloquantes : champ obligatoire absent à la création (ex. `bridge` d'une zone VLAN sans mapping), champ immuable modifié (type de zone, vnet d'un subnet). - **Apply SDN** : `POST /cluster/sdn/lock` → écritures avec `lock-token` → `PUT /cluster/sdn` (commit + rechargement réseau, suivi de la tâche). Toute erreur avant le commit déclenche `POST /cluster/sdn/rollback`. Le verrou est refusé si le client a déjà des changements SDN en attente : on ne mélange pas nos modifications avec celles de quelqu'un d'autre. - **Vérification** : relecture du client, nouveau plan qui doit être vide, et aucune zone en `error` sur les nœuds (`/nodes/{node}/sdn/zones`). ## Utilisation ```sh go build -o pve-confsync ./cmd/pve-confsync cp examples/confsync.example.yaml confsync.yaml # puis adapter ./pve-confsync check # accès, version 9.x, droits des tokens ./pve-confsync export -o ref.yaml # état normalisé de la référence (versionnable dans Git) ./pve-confsync diff # écarts par client ; code retour 2 s'il y en a ./pve-confsync diff -format json ./pve-confsync apply # dry-run (défaut) : affiche le plan, n'écrit rien ./pve-confsync apply -client site-a -confirm # applique ; SDN : saisir le nom du client ./pve-confsync apply -source ref.yaml -confirm -yes # depuis un export Git, sans question ``` Avec docker compose (images et utilisateur non privilégié alignés sur Logstream) : ```sh mkdir -p secrets && printf '%s' 'xxxxxxxx-...' > secrets/pve_ref chmod 600 secrets/* && sudo chown 10001 secrets/* # lisibles par l'utilisateur du conteneur uniquement docker compose run --rm confsync check docker compose run --rm confsync apply -confirm ``` ## Sécurité - Secrets des tokens : jamais dans le YAML (`token_secret` en clair est refusé) ; lus depuis une variable d'environnement (`token_secret_env`) ou un fichier (`token_secret_file`, secret docker). Ils ne sont ni affichés ni journalisés. Le chiffrement au repos arrive avec le stockage SQLite (phase 1). - TLS : `fingerprint` (pinning SHA-256 du certificat, adapté aux certificats auto-signés PVE), `ca_file`, ou `insecure: true` réservé au labo. - Tokens (privilege separation activée, l'ACL doit être posée sur l'utilisateur et le token) : ```sh # référence : lecture seule pveum role add ConfSyncRO --privs "Sys.Audit SDN.Audit" pveum user add confsync@pve pveum user token add confsync@pve ro --privsep 1 pveum acl modify / --users confsync@pve --roles ConfSyncRO pveum acl modify / --tokens 'confsync@pve!ro' --roles ConfSyncRO # clients : Sys.Modify uniquement si les options datacenter sont synchronisées pveum role add ConfSyncRW --privs "Sys.Audit Sys.Modify SDN.Audit SDN.Allocate" ``` `check` signale un token de référence capable d'écrire et un token client à qui il manque un droit. ## Arborescence ``` cmd/pve-confsync/ CLI (check, export, diff, apply) internal/pve/ client API : token, TLS, form-encoding, version, tâches internal/pve/pvetest/ faux serveur PVE 9.x pour les tests internal/model/ objets génériques, snapshot YAML, valeurs canoniques internal/adapter/ un adaptateur par type : routes, champs calculés, locaux, immuables internal/config/ configuration YAML : clusters, politiques, mappings, sélection internal/engine/ lecture, plan, apply (verrou SDN, rollback), vérification docs/api-pve.md routes et schémas vérifiés dans les sources PVE ``` Tests : `go test ./...` (bout en bout contre le faux serveur : synchro complète, mapping manquant, rollback sur erreur, refus de PVE 8, prune et ordre de suppression, forme canonique des options datacenter, CLI dry-run/apply/export). ## Phasage 0. **CLI** export / diff / apply YAML (options datacenter, tags, SDN). ← ici 1. UI lecture seule : clusters, diff visuel par client. Ergonomie reprise de Logstream (`web/index.html`, `style.css`, `app.js`, `login.html`, auth locale + OIDC, audit) ; SQLite (clusters, profils, mappings, historique, audit), secrets chiffrés. 2. UI d'action : Dry-run / Synchroniser (par objet, par client, global), journal des exécutions. 3. Planification, détection de dérive en lecture seule, notifications. ## Reste à faire avant la phase 1 - Valider sur les deux clusters de labo : forme réelle des réponses (`nodes`, `dhcp-range`, booléens), comportement de `PUT /cluster/options` avec `delete`. - Exploiter `GET /cluster/sdn/dry-run` (diff frr / interfaces, PVE ≥ 9.2 environ) pour enrichir le dry-run SDN. - Rechargement en échec après commit : aujourd'hui signalé, pas d'annulation automatique (la configuration appliquée est déjà la nouvelle).