Interface web : écarts, dry-run et synchronisation (phases 1 et 2) #1
No files matched your search
+6
-2
@@ -7,6 +7,7 @@ COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY cmd ./cmd
|
||||
COPY internal ./internal
|
||||
COPY web ./web
|
||||
ARG VERSION=dev
|
||||
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" \
|
||||
-o /out/pve-confsync ./cmd/pve-confsync
|
||||
@@ -20,6 +21,9 @@ COPY --from=build /out/pve-confsync /usr/local/bin/pve-confsync
|
||||
USER confsync
|
||||
WORKDIR /data
|
||||
VOLUME /data
|
||||
ENV CONFSYNC_CONFIG=/config/confsync.yaml
|
||||
ENV CONFSYNC_CONFIG=/config/confsync.yaml DATA_DIR=/data LISTEN=:8080
|
||||
EXPOSE 8080
|
||||
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://127.0.0.1:8080/healthz || exit 1
|
||||
ENTRYPOINT ["/usr/local/bin/pve-confsync"]
|
||||
CMD ["check"]
|
||||
# interface web ; la CLI reste disponible : docker compose run --rm confsync diff
|
||||
CMD ["serve"]
|
||||
@@ -4,9 +4,10 @@ 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.**
|
||||
**État : phases 0 à 2.** CLI (export, diff et apply en YAML) et interface web
|
||||
(écarts par client, dry-run et synchronisation par objet, par client ou global, journal,
|
||||
audit) 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
|
||||
|
||||
@@ -71,12 +72,59 @@ docker compose run --rm confsync check
|
||||
docker compose run --rm confsync apply -confirm
|
||||
```
|
||||
|
||||
## Interface web
|
||||
|
||||
```sh
|
||||
go run ./cmd/pve-confsync serve -demo # démo : faux clusters PVE, http://localhost:8080
|
||||
docker compose up -d confsync-web # production : données dans le volume /data
|
||||
```
|
||||
|
||||
Le mode démo démarre une référence PVE 9.1, deux clients avec des écarts (dont un mapping
|
||||
manquant, bloquant) et un cluster PVE 8 refusé. Comptes `admin`, `operateur` et `lecteur`,
|
||||
mot de passe `confsync-demo`.
|
||||
|
||||
- **Écarts** : une carte par cluster (version, compteurs à créer / modifier / erreurs /
|
||||
hors référence), puis les objets du client choisi ; le détail d'un objet montre pour
|
||||
chaque champ la politique, la valeur de référence, le mapping, la valeur actuelle et
|
||||
la valeur après synchro.
|
||||
- **Dry-run / Synchroniser** par objet (avec ses dépendances : zone et vnet parents à
|
||||
créer), par client ou global. La synchronisation passe toujours par un dry-run : le
|
||||
serveur recalcule le plan et refuse s'il a changé depuis (empreinte du plan). Avec des
|
||||
changements SDN, il faut taper le nom du client (celui de la référence en global).
|
||||
Une seule synchronisation à la fois, suivie étape par étape.
|
||||
- **Journal** des dry-runs et synchronisations (plans, étapes, résultats) et **Audit**
|
||||
(connexions, actions, modifications des paramètres ; admin).
|
||||
- **Paramètres** : clusters (test d'accès et des droits, TLS, mapping et politiques par
|
||||
client), types synchronisés, sélection, politiques, suppression (désactivée par défaut),
|
||||
utilisateurs locaux, import / export YAML (format de la CLI), compte, apparence.
|
||||
|
||||
Rôles : `admin` (tout), `operator` (dry-run et synchronisation), `viewer` (lecture).
|
||||
Les comptes locaux restent disponibles même avec OIDC.
|
||||
|
||||
| Variable | Défaut | Rôle |
|
||||
|---|---|---|
|
||||
| `LISTEN` | `:8080` | adresse d'écoute |
|
||||
| `DATA_DIR` | `/data` | base SQLite, `secret.key`, `session.key` |
|
||||
| `CONFSYNC_SECRET_KEY` / `_FILE` | `DATA_DIR/secret.key` (créée) | clé AES-256 (hex ou base64) des secrets en base |
|
||||
| `CONFSYNC_ADMIN_USER`, `CONFSYNC_ADMIN_PASSWORD` / `_FILE` | `admin`, aléatoire (journalisé une fois) | premier administrateur, base vide |
|
||||
| `CONFSYNC_IMPORT` | | YAML de la CLI importé si la base n'a aucun cluster |
|
||||
| `AUTH_LOCAL` | `true` | comptes locaux |
|
||||
| `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET` / `_FILE`, `OIDC_REDIRECT_URL` | | OIDC (code + PKCE) ; `OIDC_REDIRECT_URL` complète, par ex. `https://confsync.example.org/auth/callback` |
|
||||
| `OIDC_SCOPES`, `OIDC_GROUPS_CLAIM` | `openid profile email`, `groups` | ajouter le scope qui fournit les groupes chez l'IdP |
|
||||
| `OIDC_ADMIN_GROUP`, `OIDC_OPERATOR_GROUP`, `OIDC_DEFAULT_ROLE` | `viewer` | rôle d'après les groupes |
|
||||
| `SESSION_TTL` | `12h` | durée des sessions |
|
||||
| `CONFSYNC_DEMO` | `false` | mode démo |
|
||||
|
||||
## 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).
|
||||
secret docker). Ils ne sont ni affichés ni journalisés. Dans l'interface, ils sont chiffrés en base
|
||||
(AES-256-GCM, clé hors base) et jamais renvoyés au navigateur ; l'export YAML les remplace
|
||||
par `token_secret_env`.
|
||||
- Interface : mots de passe PBKDF2-SHA256 (600 000 itérations), cookie de session signé
|
||||
(HttpOnly, SameSite=Lax, Secure derrière HTTPS) invalidé au changement de mot de passe,
|
||||
en-tête `X-Confsync` exigé sur les écritures (CSRF), CSP sans script ni style en ligne.
|
||||
- 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) :
|
||||
@@ -99,7 +147,12 @@ un droit.
|
||||
## Arborescence
|
||||
|
||||
```
|
||||
cmd/pve-confsync/ CLI (check, export, diff, apply)
|
||||
cmd/pve-confsync/ CLI (check, export, diff, apply, serve)
|
||||
internal/service/ analyse pour l'UI : lecture parallèle, plans, lignes de diff par champ
|
||||
internal/store/ SQLite : clusters (secrets chiffrés), paramètres, utilisateurs, journal, audit
|
||||
internal/server/ HTTP : auth locale + OIDC, rôles, API, jobs de synchronisation
|
||||
internal/demo/ faux clusters du mode démo
|
||||
web/ UI embarquée (index.html, app.js, style.css, login.html)
|
||||
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
|
||||
@@ -111,18 +164,19 @@ 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).
|
||||
canonique des options datacenter, CLI dry-run/apply/export ; sélection d'objets et dépendances ; chiffrement, utilisateurs ;
|
||||
serveur de bout en bout : rôles, CSRF, dry-run, empreinte périmée, confirmation SDN, apply).
|
||||
|
||||
## Phasage
|
||||
|
||||
0. **CLI** export / diff / apply YAML (options datacenter, tags, SDN). ← ici
|
||||
0. **CLI** export / diff / apply YAML (options datacenter, tags, SDN).
|
||||
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.
|
||||
2. UI d'action : Dry-run / Synchroniser (par objet, par client, global), journal des exécutions. ← ici
|
||||
3. Planification, détection de dérive en lecture seule, notifications.
|
||||
|
||||
## Reste à faire avant la phase 1
|
||||
## Reste à faire
|
||||
|
||||
- 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`.
|
||||
|
||||
+38
-4
@@ -1,14 +1,44 @@
|
||||
# Phase 0 : outil en ligne de commande, lancé à la demande :
|
||||
# Interface web (phases 1-2) :
|
||||
# docker compose up -d confsync-web # http://<hôte>:8080 ; mot de passe admin dans les logs au 1er démarrage
|
||||
# CLI de la phase 0, lancée à la demande avec confsync.yaml :
|
||||
# docker compose run --rm confsync check
|
||||
# docker compose run --rm confsync diff
|
||||
# docker compose run --rm confsync export -o /data/ref.yaml
|
||||
# docker compose run --rm confsync apply # dry-run
|
||||
# docker compose run --rm confsync apply -confirm # application (confirmation SDN interactive)
|
||||
services:
|
||||
confsync:
|
||||
confsync-web:
|
||||
build: .
|
||||
image: pve-confsync:dev
|
||||
container_name: pve-confsync-web
|
||||
command: ["serve"]
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080" # ou derrière Traefik (HTTPS : cookies Secure)
|
||||
environment:
|
||||
TZ: ${TZ:-Europe/Paris}
|
||||
CONFSYNC_SECRET_KEY_FILE: /run/secrets/confsync_key # clé des secrets en base, hors volume /data
|
||||
# CONFSYNC_ADMIN_PASSWORD_FILE: /run/secrets/confsync_admin
|
||||
# CONFSYNC_IMPORT: /config/confsync.yaml # import initial de la config CLI (base vide)
|
||||
# OIDC_ISSUER: https://auth.example.org/application/o/confsync/
|
||||
# OIDC_CLIENT_ID: confsync
|
||||
# OIDC_CLIENT_SECRET_FILE: /run/secrets/oidc_client
|
||||
# OIDC_REDIRECT_URL: https://confsync.example.org/auth/callback
|
||||
# OIDC_ADMIN_GROUP: confsync-admins
|
||||
# OIDC_OPERATOR_GROUP: confsync-operators
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
volumes:
|
||||
- confsync-data:/data # SQLite, session.key
|
||||
secrets:
|
||||
- confsync_key
|
||||
|
||||
confsync:
|
||||
image: pve-confsync:dev
|
||||
container_name: pve-confsync
|
||||
profiles: ["cli"] # pas démarré par "up" ; "run" l'active
|
||||
command: ["check"]
|
||||
environment:
|
||||
TZ: ${TZ:-Europe/Paris}
|
||||
PVE_SITE_B_SECRET: ${PVE_SITE_B_SECRET:-} # secrets par variable (voir token_secret_env)
|
||||
@@ -17,15 +47,18 @@ services:
|
||||
required: false
|
||||
volumes:
|
||||
- ./confsync.yaml:/config/confsync.yaml:ro
|
||||
- confsync-data:/data # exports YAML, plus tard SQLite (phase 1)
|
||||
- confsync-cli:/data # exports YAML
|
||||
secrets:
|
||||
- pve_ref
|
||||
- pve_site_a
|
||||
stdin_open: true
|
||||
tty: true
|
||||
|
||||
# Secrets des tokens API sous forme de fichiers (chmod 600), montés dans /run/secrets/<nom>
|
||||
# Fichiers chmod 600, propriétaire 10001, montés dans /run/secrets/<nom>.
|
||||
# confsync_key : openssl rand -hex 32 > secrets/confsync_key
|
||||
secrets:
|
||||
confsync_key:
|
||||
file: ./secrets/confsync_key
|
||||
pve_ref:
|
||||
file: ./secrets/pve_ref
|
||||
pve_site_a:
|
||||
@@ -33,3 +66,4 @@ secrets:
|
||||
|
||||
volumes:
|
||||
confsync-data:
|
||||
confsync-cli:
|
||||
Reference in new issue
Block a user