diff --git a/.gitignore b/.gitignore index baa5268..176cba7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ facts_cache/ .ansible/ *.vault *.vault.yml +**/vault.yml *.secret *.pem *.key diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f0ee79..73e2b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## 2026-06-30 +### Modifié +- **Voûte de secrets unique par environnement.** Fini les voûtes éparpillées : tous + les secrets de l'instance (token Proxmox + 17 `vault_*` pour PKI, LDAP/SSO, bases, + forge, observabilité) vivent dans **un seul fichier chiffré**, + `inventories//group_vars/all/vault.yml`. Gabarit committé + `exemples/vault.exemple.yml`. `make config` (`config_proxmox.py`) écrit/édite + désormais cette voûte (semée depuis le gabarit si absente). `.gitignore` durci + (`**/vault.yml`). Docs mises à jour (config-proxmox.md avec étapes de migration, + intrants-communs.md §H, QUICKSTART). Le GUI ne stocke toujours aucun secret. + ### Corrigé - **`chezlepro_timezone` n'était appliqué nulle part.** Cet intrant de base global était défini mais aucun rôle ne s'en servait. Le rôle `chrony` (appliqué à tout diff --git a/QUICKSTART.md b/QUICKSTART.md index 7ed8a5f..d7ef67d 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -44,9 +44,10 @@ Tu peux aussi le faire dans le GUI plus tard (`make inventaire-ui`). make config # renseigne API host/user/port, nœud, stockage, VMID du template... ``` Chaque paramètre demandé est expliqué dans [`docs/config-proxmox.md`](docs/config-proxmox.md). -Place ton **token API** et tes secrets dans le Vault de l'instance -(`instance/inventories/lab/group_vars/proxmox.vault.yml`, à partir du `.example`, -chiffré avec `ansible-vault`). Exporte ton mot de passe Vault, ex. : +Tous tes secrets (token API + `vault_*`) vont dans **une voûte unique par +environnement** : `instance/inventories/lab/group_vars/all/vault.yml`, à partir du +gabarit [`exemples/vault.exemple.yml`](exemples/vault.exemple.yml), chiffrée avec +`ansible-vault`. Exporte ton mot de passe Vault, ex. : ```bash export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass ``` diff --git a/docs/config-proxmox.md b/docs/config-proxmox.md index 62065da..0077ff1 100644 --- a/docs/config-proxmox.md +++ b/docs/config-proxmox.md @@ -5,8 +5,9 @@ la connexion au cluster Proxmox et les valeurs de clonage par défaut. Il pose **16 paramètres non sensibles** puis propose de saisir les **secrets API**. - Non sensibles → `instance/inventories/lab/group_vars/proxmox.yml` -- Secrets API → `instance/inventories/lab/group_vars/proxmox.vault.yml` (chiffré - par `ansible-vault`) +- Secrets → **voûte unique** `instance/inventories/lab/group_vars/all/vault.yml` + (chiffrée par `ansible-vault`), qui contient **tous** les secrets de l'instance + (token Proxmox + `vault_*`). Voir [§4](#4-secrets-de-linstance-). À chaque invite, la valeur courante (ou le défaut) est affichée entre crochets : **appuyer sur Entrée conserve cette valeur**. On peut donc relancer `make config` @@ -56,20 +57,41 @@ Ces valeurs s'appliquent à toute VM clonée, **sauf** si l'hôte les surcharge | Pare-feu interface Proxmox | `proxmox_clone_parefeu_interface` | `non` | Active le pare-feu Proxmox **au niveau de la NIC**. Laisser `non` : le filtrage se fait dans l'invité (nftables), pas chez l'hyperviseur. | | Démarrer le clone après création | `proxmox_clone_demarrer` | `oui` | `oui` = booter la VM dès la création (nécessaire pour qu'Ansible la joigne ensuite). | -## 4. Secrets API Proxmox 🔒 *(constantes, chiffrées)* +## 4. Secrets de l'instance 🔒 *(voûte unique)* -L'assistant demande ensuite : « Configurer les secrets API Proxmox maintenant ». -Si `oui` : +Tous les secrets de l'instance vivent dans **une seule voûte chiffrée par +environnement** : `instance/inventories//group_vars/all/vault.yml`. Un seul +fichier, un seul mot de passe — fini les voûtes éparpillées. Gabarit committé : +[`exemples/vault.exemple.yml`](../exemples/vault.exemple.yml) (token Proxmox + +17 clés `vault_*` pour PKI, LDAP/SSO, bases, forge, observabilité). + +L'assistant demande « Configurer la voûte de secrets maintenant ». Si `oui` : | Invite | Variable | Défaut | Sens / quoi saisir | | --- | --- | --- | --- | -| Token ID Proxmox | `proxmox_api_token_id` | `set-ops` | Identifiant du **token API** créé côté Proxmox pour l'utilisateur API. | -| Token secret Proxmox | `proxmox_api_token_secret` | *(aucun)* | Secret du token. **Saisie masquée**, écrite uniquement dans le Vault chiffré. | +| Token ID Proxmox | `proxmox_api_token_id` | `set-ops` | Identifiant du **token API** créé côté Proxmox. | +| Token secret Proxmox | `proxmox_api_token_secret` | *(aucun)* | Secret du token. **Saisie masquée**, écrit dans la voûte chiffrée. | -- Si `proxmox.vault.yml` existe déjà, l'assistant ouvre directement - `ansible-vault edit` (pas de re-saisie en clair). -- Un secret vide ⇒ le Vault **n'est pas créé**. -- Ces deux clés ne transitent **jamais** par le GUI ni par aucun fichier en clair. +- Si la voûte **existe déjà**, l'assistant ouvre directement `ansible-vault edit` + (aucune re-saisie en clair). +- Si elle **n'existe pas**, l'assistant la **sème depuis le gabarit** (toutes les + clés présentes, vides), y place le token, puis la chiffre. On renseigne ensuite + les autres secrets avec `ansible-vault edit …/all/vault.yml`. +- Ces secrets ne transitent **jamais** par le GUI ni par aucun fichier en clair ; + le GUI n'en affiche que les **noms** (panneau « Intrants »). + +### Migration depuis l'ancienne `proxmox.vault.yml` + +Si tu as déjà une `proxmox.vault.yml`, déplace son contenu dans la voûte unique : + +```bash +cd instance/inventories/lab/group_vars +cp ../../../../exemples/vault.exemple.yml all/vault.yml +ansible-vault view proxmox.vault.yml # relève token_id + secret +ansible-vault edit all/vault.yml # colle-les, renseigne le reste +ansible-vault encrypt all/vault.yml # si pas déjà chiffrée +git rm --cached --ignore-unmatch proxmox.vault.yml 2>/dev/null; rm proxmox.vault.yml proxmox.vault.yml.example +``` --- diff --git a/docs/intrants-communs.md b/docs/intrants-communs.md index 738fe29..daa4fe5 100644 --- a/docs/intrants-communs.md +++ b/docs/intrants-communs.md @@ -48,7 +48,11 @@ journald (rétention) · core_dumps · systemd_ssh_auto. - Smallstep (step-cli) — `client_pki` ↔ `serveur_step_ca`. - Grafana (alloy/loki/grafana) — `client_journal` ↔ `serveur_loki`/`serveur_grafana`. -### H. Secrets Vault communs 🔒 — `proxmox.vault.yml` + vault d'instance +### H. Secrets Vault communs 🔒 — **voûte unique** `group_vars/all/vault.yml` +> Tous les secrets de l'instance dans **un seul fichier chiffré par environnement** +> (`inventories//group_vars/all/vault.yml`), gabarit +> [`exemples/vault.exemple.yml`](../exemples/vault.exemple.yml). Un mot de passe, un +> endroit. Édité via `ansible-vault edit` (jamais par le GUI). `proxmox_api_token_id/secret`, `vault_step_ca_password`, `vault_step_ca_fingerprint`, `vault_step_ca_provisioner_password`, `vault_openldap_admin`, `vault_ldap_sssd`, `vault_keycloak_admin`, `vault_postgresql_keycloak`, `vault_bd_keycloak`, diff --git a/exemples/vault.exemple.yml b/exemples/vault.exemple.yml new file mode 100644 index 0000000..b9e2f7f --- /dev/null +++ b/exemples/vault.exemple.yml @@ -0,0 +1,43 @@ +--- +# Voûte UNIQUE de l'instance — TOUS les secrets, un seul fichier, un seul mot de passe. +# +# Installation : +# 1. Copier ce gabarit vers la voûte de chaque environnement : +# cp exemples/vault.exemple.yml instance/inventories/lab/group_vars/all/vault.yml +# 2. Renseigner les valeurs, puis chiffrer : +# ansible-vault encrypt instance/inventories/lab/group_vars/all/vault.yml +# 3. Éditer ensuite sans jamais déchiffrer sur disque : +# ansible-vault edit instance/inventories/lab/group_vars/all/vault.yml +# +# Ne JAMAIS versionner le fichier vault.yml (couvert par .gitignore). +# Le GUI ne stocke ni n'affiche ces valeurs : il n'en montre que les NOMS +# (panneau « Intrants de base »). Le mot de passe du vault est saisi au déploiement. + +# --- Accès API Proxmox (clonage des VM) --- +proxmox_api_token_id: "" +proxmox_api_token_secret: "" + +# --- AC / PKI interne (step-ca) --- +vault_step_ca_password: "" +vault_step_ca_fingerprint: "" +vault_step_ca_provisioner_password: "" + +# --- Identité (LDAP / SSO) --- +vault_openldap_admin: "" +vault_ldap_sssd: "" +vault_keycloak_admin: "" + +# --- Bases de données --- +vault_postgresql_keycloak: "" +vault_bd_keycloak: "" +vault_bd_forgejo: "" +vault_bd_icingadb: "" + +# --- Forge (Forgejo) --- +vault_forgejo_admin: "" +vault_forgejo_secret_key: "" +vault_forgejo_internal_token: "" + +# --- Observabilité / divers --- +vault_grafana_admin: "" +vault_redis: "" diff --git a/scripts/config_proxmox.py b/scripts/config_proxmox.py index 43cbc9a..3cbbba8 100644 --- a/scripts/config_proxmox.py +++ b/scripts/config_proxmox.py @@ -15,7 +15,10 @@ import yaml RACINE = Path(__file__).resolve().parents[1] INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance")) FICHIER_PROXMOX = INSTANCE / "inventories/lab/group_vars/proxmox.yml" -FICHIER_VAULT = INSTANCE / "inventories/lab/group_vars/proxmox.vault.yml" +# Voûte UNIQUE de l'environnement : tous les secrets (token Proxmox + vault_*) au +# même endroit, un seul mot de passe. Gabarit : exemples/vault.exemple.yml. +FICHIER_VAULT = INSTANCE / "inventories/lab/group_vars/all/vault.yml" +GABARIT_VAULT = RACINE / "exemples/vault.exemple.yml" VALEURS_DEFAUT = { @@ -73,7 +76,7 @@ def ecrire_yaml(path: Path, data: dict) -> None: with path.open("w", encoding="utf-8") as fichier: fichier.write("---\n") fichier.write("# Parametres non sensibles pour les operations Proxmox.\n") - fichier.write("# Les secrets API doivent aller dans proxmox.vault.yml.\n\n") + fichier.write("# Les secrets vont dans la voûte unique group_vars/all/vault.yml.\n\n") yaml.safe_dump(data, fichier, default_flow_style=False, sort_keys=False, allow_unicode=True) @@ -119,35 +122,36 @@ def demander_oui_non(question: str, defaut: bool = False) -> bool: def configurer_vault() -> None: - if not demander_oui_non("Configurer les secrets API Proxmox maintenant"): + if not demander_oui_non("Configurer la voûte de secrets maintenant"): return if FICHIER_VAULT.exists(): - print(f"Ouverture du Vault existant: {os.path.relpath(FICHIER_VAULT, RACINE)}") + print(f"Ouverture de la voûte existante: {os.path.relpath(FICHIER_VAULT, RACINE)}") subprocess.run(["ansible-vault", "edit", str(FICHIER_VAULT)], check=True) return + # Voûte absente : on la sème à partir du gabarit (toutes les clés vides), + # on y place le token Proxmox saisi, puis on chiffre. Les autres secrets se + # renseignent ensuite via `ansible-vault edit`. token_id = input("Token ID Proxmox [set-ops] : ").strip() or "set-ops" token_secret = getpass("Token secret Proxmox : ").strip() if not token_secret: - print("Secret vide: Vault non cree.") + print("Secret vide: voûte non créée.") return + contenu = charger_yaml(GABARIT_VAULT) + contenu["proxmox_api_token_id"] = token_id + contenu["proxmox_api_token_secret"] = token_secret + FICHIER_VAULT.parent.mkdir(parents=True, exist_ok=True) try: fd = os.open(FICHIER_VAULT, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as fichier: - yaml.safe_dump( - { - "proxmox_api_token_id": token_id, - "proxmox_api_token_secret": token_secret, - }, - fichier, - default_flow_style=False, - sort_keys=False, - ) + yaml.safe_dump(contenu, fichier, default_flow_style=False, sort_keys=False, allow_unicode=True) subprocess.run(["ansible-vault", "encrypt", str(FICHIER_VAULT)], check=True) - print(f"Vault cree: {os.path.relpath(FICHIER_VAULT, RACINE)}") + print(f"Voûte créée: {os.path.relpath(FICHIER_VAULT, RACINE)}") + print("Renseigne les autres secrets avec : ansible-vault edit " + f"{os.path.relpath(FICHIER_VAULT, RACINE)}") except Exception: if FICHIER_VAULT.exists(): FICHIER_VAULT.unlink()