Consolider les secrets dans une voûte unique par environnement
Fini les voûtes éparpillées : tous les secrets (token Proxmox + vault_*) vivent dans group_vars/all/vault.yml, un seul fichier chiffré, un seul mot de passe. Gabarit committé exemples/vault.exemple.yml (17 clés). make config écrit/édite cette voûte (semée depuis le gabarit si absente). .gitignore durci (**/vault.yml). Docs : config-proxmox.md (+ migration), intrants- communs.md §H, QUICKSTART. Le GUI ne stocke aucun secret. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
3a3a80e0ac
commit
c4e73f8712
7 changed files with 115 additions and 30 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,6 +6,7 @@ facts_cache/
|
||||||
.ansible/
|
.ansible/
|
||||||
*.vault
|
*.vault
|
||||||
*.vault.yml
|
*.vault.yml
|
||||||
|
**/vault.yml
|
||||||
*.secret
|
*.secret
|
||||||
*.pem
|
*.pem
|
||||||
*.key
|
*.key
|
||||||
|
|
|
||||||
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -2,6 +2,16 @@
|
||||||
|
|
||||||
## 2026-06-30
|
## 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/<env>/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é
|
### Corrigé
|
||||||
- **`chezlepro_timezone` n'était appliqué nulle part.** Cet intrant de base global
|
- **`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
|
était défini mais aucun rôle ne s'en servait. Le rôle `chrony` (appliqué à tout
|
||||||
|
|
|
||||||
|
|
@ -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...
|
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).
|
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
|
Tous tes secrets (token API + `vault_*`) vont dans **une voûte unique par
|
||||||
(`instance/inventories/lab/group_vars/proxmox.vault.yml`, à partir du `.example`,
|
environnement** : `instance/inventories/lab/group_vars/all/vault.yml`, à partir du
|
||||||
chiffré avec `ansible-vault`). Exporte ton mot de passe Vault, ex. :
|
gabarit [`exemples/vault.exemple.yml`](exemples/vault.exemple.yml), chiffrée avec
|
||||||
|
`ansible-vault`. Exporte ton mot de passe Vault, ex. :
|
||||||
```bash
|
```bash
|
||||||
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass
|
export ANSIBLE_VAULT_PASSWORD_FILE=~/.config/setops-vault-pass
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -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**.
|
**16 paramètres non sensibles** puis propose de saisir les **secrets API**.
|
||||||
|
|
||||||
- Non sensibles → `instance/inventories/lab/group_vars/proxmox.yml`
|
- Non sensibles → `instance/inventories/lab/group_vars/proxmox.yml`
|
||||||
- Secrets API → `instance/inventories/lab/group_vars/proxmox.vault.yml` (chiffré
|
- Secrets → **voûte unique** `instance/inventories/lab/group_vars/all/vault.yml`
|
||||||
par `ansible-vault`)
|
(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 :
|
À 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`
|
**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. |
|
| 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). |
|
| 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 ».
|
Tous les secrets de l'instance vivent dans **une seule voûte chiffrée par
|
||||||
Si `oui` :
|
environnement** : `instance/inventories/<env>/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 |
|
| 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 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**, écrite uniquement dans le Vault chiffré. |
|
| 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
|
- Si la voûte **existe déjà**, l'assistant ouvre directement `ansible-vault edit`
|
||||||
`ansible-vault edit` (pas de re-saisie en clair).
|
(aucune re-saisie en clair).
|
||||||
- Un secret vide ⇒ le Vault **n'est pas créé**.
|
- Si elle **n'existe pas**, l'assistant la **sème depuis le gabarit** (toutes les
|
||||||
- Ces deux clés ne transitent **jamais** par le GUI ni par aucun fichier en clair.
|
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
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,11 @@ journald (rétention) · core_dumps · systemd_ssh_auto.
|
||||||
- Smallstep (step-cli) — `client_pki` ↔ `serveur_step_ca`.
|
- Smallstep (step-cli) — `client_pki` ↔ `serveur_step_ca`.
|
||||||
- Grafana (alloy/loki/grafana) — `client_journal` ↔ `serveur_loki`/`serveur_grafana`.
|
- 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/<env>/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`,
|
`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_step_ca_provisioner_password`, `vault_openldap_admin`, `vault_ldap_sssd`,
|
||||||
`vault_keycloak_admin`, `vault_postgresql_keycloak`, `vault_bd_keycloak`,
|
`vault_keycloak_admin`, `vault_postgresql_keycloak`, `vault_bd_keycloak`,
|
||||||
|
|
|
||||||
43
exemples/vault.exemple.yml
Normal file
43
exemples/vault.exemple.yml
Normal file
|
|
@ -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: ""
|
||||||
|
|
@ -15,7 +15,10 @@ import yaml
|
||||||
RACINE = Path(__file__).resolve().parents[1]
|
RACINE = Path(__file__).resolve().parents[1]
|
||||||
INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance"))
|
INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance"))
|
||||||
FICHIER_PROXMOX = INSTANCE / "inventories/lab/group_vars/proxmox.yml"
|
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 = {
|
VALEURS_DEFAUT = {
|
||||||
|
|
@ -73,7 +76,7 @@ def ecrire_yaml(path: Path, data: dict) -> None:
|
||||||
with path.open("w", encoding="utf-8") as fichier:
|
with path.open("w", encoding="utf-8") as fichier:
|
||||||
fichier.write("---\n")
|
fichier.write("---\n")
|
||||||
fichier.write("# Parametres non sensibles pour les operations Proxmox.\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)
|
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:
|
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
|
return
|
||||||
|
|
||||||
if FICHIER_VAULT.exists():
|
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)
|
subprocess.run(["ansible-vault", "edit", str(FICHIER_VAULT)], check=True)
|
||||||
return
|
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_id = input("Token ID Proxmox [set-ops] : ").strip() or "set-ops"
|
||||||
token_secret = getpass("Token secret Proxmox : ").strip()
|
token_secret = getpass("Token secret Proxmox : ").strip()
|
||||||
if not token_secret:
|
if not token_secret:
|
||||||
print("Secret vide: Vault non cree.")
|
print("Secret vide: voûte non créée.")
|
||||||
return
|
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)
|
FICHIER_VAULT.parent.mkdir(parents=True, exist_ok=True)
|
||||||
try:
|
try:
|
||||||
fd = os.open(FICHIER_VAULT, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
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:
|
with os.fdopen(fd, "w", encoding="utf-8") as fichier:
|
||||||
yaml.safe_dump(
|
yaml.safe_dump(contenu, fichier, default_flow_style=False, sort_keys=False, allow_unicode=True)
|
||||||
{
|
|
||||||
"proxmox_api_token_id": token_id,
|
|
||||||
"proxmox_api_token_secret": token_secret,
|
|
||||||
},
|
|
||||||
fichier,
|
|
||||||
default_flow_style=False,
|
|
||||||
sort_keys=False,
|
|
||||||
)
|
|
||||||
subprocess.run(["ansible-vault", "encrypt", str(FICHIER_VAULT)], check=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:
|
except Exception:
|
||||||
if FICHIER_VAULT.exists():
|
if FICHIER_VAULT.exists():
|
||||||
FICHIER_VAULT.unlink()
|
FICHIER_VAULT.unlink()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue