docs, docker : interface web (serve), variables, compose web + CLI
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015a8P6WMRPeqdpd5VCh6jAy
This commit was merged in pull request #1.
This commit is contained in:
1 parent
0cac77e63f
commit
5561bcfcf9
3 files changed
+110
-18
No files matched your search
+6
-2
@@ -7,6 +7,7 @@ COPY go.mod go.sum ./
|
|||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY cmd ./cmd
|
COPY cmd ./cmd
|
||||||
COPY internal ./internal
|
COPY internal ./internal
|
||||||
|
COPY web ./web
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" \
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" \
|
||||||
-o /out/pve-confsync ./cmd/pve-confsync
|
-o /out/pve-confsync ./cmd/pve-confsync
|
||||||
@@ -20,6 +21,9 @@ COPY --from=build /out/pve-confsync /usr/local/bin/pve-confsync
|
|||||||
USER confsync
|
USER confsync
|
||||||
WORKDIR /data
|
WORKDIR /data
|
||||||
VOLUME /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"]
|
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.
|
« 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.
|
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
|
**État : phases 0 à 2.** CLI (export, diff et apply en YAML) et interface web
|
||||||
et le SDN (zones VLAN/VXLAN, VNets, subnets). Testé contre un faux serveur PVE ;
|
(écarts par client, dry-run et synchronisation par objet, par client ou global, journal,
|
||||||
**pas encore testé sur les clusters de labo.**
|
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
|
## Périmètre
|
||||||
|
|
||||||
@@ -71,12 +72,59 @@ docker compose run --rm confsync check
|
|||||||
docker compose run --rm confsync apply -confirm
|
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é
|
## Sécurité
|
||||||
|
|
||||||
- Secrets des tokens : jamais dans le YAML (`token_secret` en clair est refusé) ; lus depuis
|
- 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`,
|
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
|
secret docker). Ils ne sont ni affichés ni journalisés. Dans l'interface, ils sont chiffrés en base
|
||||||
le stockage SQLite (phase 1).
|
(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),
|
- TLS : `fingerprint` (pinning SHA-256 du certificat, adapté aux certificats auto-signés PVE),
|
||||||
`ca_file`, ou `insecure: true` réservé au labo.
|
`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) :
|
- Tokens (privilege separation activée, l'ACL doit être posée sur l'utilisateur et le token) :
|
||||||
@@ -99,7 +147,12 @@ un droit.
|
|||||||
## Arborescence
|
## 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/ client API : token, TLS, form-encoding, version, tâches
|
||||||
internal/pve/pvetest/ faux serveur PVE 9.x pour les tests
|
internal/pve/pvetest/ faux serveur PVE 9.x pour les tests
|
||||||
internal/model/ objets génériques, snapshot YAML, valeurs canoniques
|
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
|
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
|
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
|
## 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
|
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) ;
|
(`web/index.html`, `style.css`, `app.js`, `login.html`, auth locale + OIDC, audit) ;
|
||||||
SQLite (clusters, profils, mappings, historique, audit), secrets chiffrés.
|
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.
|
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`,
|
- 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`.
|
booléens), comportement de `PUT /cluster/options` avec `delete`.
|
||||||
|
|||||||
+40
-6
@@ -1,31 +1,64 @@
|
|||||||
# 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 check
|
||||||
# docker compose run --rm confsync diff
|
# docker compose run --rm confsync diff
|
||||||
# docker compose run --rm confsync export -o /data/ref.yaml
|
# docker compose run --rm confsync export -o /data/ref.yaml
|
||||||
# docker compose run --rm confsync apply # dry-run
|
# docker compose run --rm confsync apply # dry-run
|
||||||
# docker compose run --rm confsync apply -confirm # application (confirmation SDN interactive)
|
# docker compose run --rm confsync apply -confirm # application (confirmation SDN interactive)
|
||||||
services:
|
services:
|
||||||
confsync:
|
confsync-web:
|
||||||
build: .
|
build: .
|
||||||
image: pve-confsync:dev
|
image: pve-confsync:dev
|
||||||
container_name: pve-confsync
|
container_name: pve-confsync-web
|
||||||
|
command: ["serve"]
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080" # ou derrière Traefik (HTTPS : cookies Secure)
|
||||||
environment:
|
environment:
|
||||||
TZ: ${TZ:-Europe/Paris}
|
TZ: ${TZ:-Europe/Paris}
|
||||||
PVE_SITE_B_SECRET: ${PVE_SITE_B_SECRET:-} # secrets par variable (voir token_secret_env)
|
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)
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env
|
- path: .env
|
||||||
required: false
|
required: false
|
||||||
volumes:
|
volumes:
|
||||||
- ./confsync.yaml:/config/confsync.yaml:ro
|
- ./confsync.yaml:/config/confsync.yaml:ro
|
||||||
- confsync-data:/data # exports YAML, plus tard SQLite (phase 1)
|
- confsync-cli:/data # exports YAML
|
||||||
secrets:
|
secrets:
|
||||||
- pve_ref
|
- pve_ref
|
||||||
- pve_site_a
|
- pve_site_a
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
tty: 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:
|
secrets:
|
||||||
|
confsync_key:
|
||||||
|
file: ./secrets/confsync_key
|
||||||
pve_ref:
|
pve_ref:
|
||||||
file: ./secrets/pve_ref
|
file: ./secrets/pve_ref
|
||||||
pve_site_a:
|
pve_site_a:
|
||||||
@@ -33,3 +66,4 @@ secrets:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
confsync-data:
|
confsync-data:
|
||||||
|
confsync-cli:
|
||||||
Reference in new issue
Block a user