Tous mes playbooks Ansible lisent leurs secrets (mots de passe de bases, clés d’API, jetons) dans OpenBao, le fork libre de HashiCorp Vault. Les secrets ne sont donc écrits ni dans le dépôt Git, ni dans des fichiers chiffrés à faire circuler : Ansible les demande au moment du déploiement, avec une identité AppRole qui n’a que le droit de lecture. Cet article explique pourquoi j’ai choisi de ne pas utiliser l’auto-unseal, ce que ça coûte, et la checklist que je suis après chaque redémarrage.

Schéma : redémarrage, OpenBao scellé sans auto-unseal, déploiements bloqués, raisons du choix de Shamir et checklist en cinq étapes
Pourquoi Shamir sans auto-unseal, et la checklist en cinq étapes après chaque redémarrage.

En un coup d’œil

SujetMon choix
SecretsOpenBao, lu par Ansible via une identité AppRole en lecture seule
DescellementShamir, 5 parts nécessaires sur 5 (t = n = 5), aucune stockée sur le serveur
Auto-unsealÉcarté : pas de dépendance à un fournisseur cloud, un seul serveur
Prix à payerAprès chaque redémarrage, le coffre est scellé et aucun déploiement n’est possible
ProcédureCinq étapes, environ cinq minutes
Prochaine améliorationUne alerte Prometheus sur l’état de scellement

Scellé, descellé : de quoi parle-t-on

OpenBao chiffre tout ce qu’il stocke avec une clé maîtresse. Au démarrage, il ne connaît pas cette clé : il est scellé. Il tourne et répond aux requêtes, mais refuse de lire ou d’écrire le moindre secret. Pour le desceller, il faut reconstituer la clé. Il existe deux grandes approches :

  • Shamir : à l’initialisation, la clé est découpée en n parts, dont t suffisent à la reconstituer. Au démarrage, il faut saisir ces parts à la main (bao operator unseal) ;
  • auto-unseal : la clé est confiée à un service externe (un KMS cloud, un HSM, un autre Vault). OpenBao la récupère tout seul au démarrage.

Mon choix : Shamir, sans auto-unseal

bao operator init -key-shares=5 -key-threshold=5
# Unseal Key 1: …   (×5)
# Initial Root Token: …

Ici, les cinq parts sont nécessaires (t = n = 5), et aucune n’est stockée sur le serveur. Elles sont conservées dans un endroit séparé, avec des copies sur deux autres supports physiques. Ce choix a des raisons précises :

Scellement d'OpenBao : cinq parts Shamir reconstruisent la clé maîtresse en mémoire, qui déchiffre les secrets lus par Ansible
Cinq parts, toutes nécessaires : la clé maîtresse n’existe qu’en mémoire, chaque redémarrage laisse OpenBao scellé.
  • un vol du disque ou d’une sauvegarde ne donne rien : sans les parts, les données d’OpenBao ne sont qu’un bloc chiffré ;
  • pas de dépendance à un fournisseur cloud : un auto-unseal par KMS ferait dépendre tout le parc d’un compte cloud et de sa disponibilité ;
  • un seul serveur : l’auto-unseal se justifie surtout pour des grappes qui redémarrent souvent, ce qui n’est pas mon cas.

Le prix à payer

Après n’importe quel redémarrage (mise à jour du noyau, coupure de courant, plantage), OpenBao repart scellé. Et comme tous les playbooks lisent leurs secrets dedans, aucun déploiement n’est possible sur aucune machine tant que quelqu’un n’a pas saisi les parts. Le symptôme côté Ansible est explicite :

fatal: … Vault is sealed

C’est un compromis assumé : un peu d’intervention humaine après un redémarrage, en échange d’un coffre qui ne s’ouvre jamais tout seul. Il faut juste ne pas l’oublier, et c’est l’objet de la checklist ci-dessous.

La checklist après redémarrage

Après chaque redémarrage du serveur qui héberge OpenBao, voici la procédure que je suis, dans l’ordre. Elle tient en cinq minutes.

1. Pointer le client sur le bon serveur

Chez moi, OpenBao écoute en HTTP sur 127.0.0.1, et OpenResty assure le TLS devant. Le client bao essaie HTTPS par défaut, d’où cette erreur trompeuse :

bao status
# Error checking seal status: … http: server gave HTTP response to HTTPS client
export BAO_ADDR=http://127.0.0.1:8200      # en local, sur le serveur
# ou : export BAO_ADDR=https://secrets.example.org   (via OpenResty)

Je mets cet export dans le .bashrc de root sur ce serveur, pour ne plus y penser.

2. Vérifier l’état

bao status
# Sealed          true
# Total Shares    5
# Threshold       5
# Unseal Progress 0/5

Sealed true confirme le besoin de desceller. Unseal Progress montre combien de parts ont déjà été saisies : la saisie peut se faire en plusieurs fois, et même par plusieurs personnes.

3. Desceller

bao operator unseal      # répété 5 fois ; chaque part est demandée sans écho à l'écran

Sans argument, la commande demande la part de façon interactive et ne l’affiche pas. Ne passez jamais une part en argument de la ligne de commande : elle finirait dans l’historique du shell et dans la liste des processus. Et ne la collez jamais dans un outil qui garde un historique, qu’il s’agisse d’une messagerie ou d’un assistant.

4. Vérifier la chaîne complète

Un coffre descellé ne suffit pas : il faut que les déploiements puissent vraiment lire leurs secrets. Je teste donc depuis le nœud de contrôle Ansible, avec la même identité AppRole que les playbooks :

# connexion AppRole, puis lecture des métadonnées d'un secret (pas de sa valeur)
bao write -field=token auth/approle/login role_id=… secret_id=… > /tmp/t
BAO_TOKEN=$(cat /tmp/t) bao kv metadata get -mount=ansible postgresql | head -3
shred -u /tmp/t

Lire les métadonnées (version, dates) prouve que l’authentification et les droits fonctionnent, sans faire transiter la valeur du secret.

5. Reprendre les déploiements

Un ansible-playbook … --check sur n’importe quel playbook qui lit un secret confirme que tout est revenu à la normale.

Ne pas l’oublier

Le plus grand risque, c’est l’oubli : un redémarrage nocturne, et personne ne s’aperçoit que les déploiements sont bloqués avant d’en avoir besoin. Une alerte Prometheus sur l’état de scellement préviendrait dès que ça arrive : le point /v1/sys/health renvoie 503 quand le coffre est scellé, et une sonde blackbox suffit à le surveiller. C’est la prochaine amélioration sur ma liste. Après chaque redémarrage du serveur de la maison, le descellement fait désormais partie de ma checklist, au même titre que la vérification des services.

Ce que j’en retiens

  • Un coffre qui ne s’ouvre pas tout seul est un choix de sécurité, avec un coût d’exploitation que l’on accepte en connaissance de cause ;
  • Ne passez jamais une part de clé en argument ni dans un outil qui garde un historique ;
  • Vérifiez la chaîne complète après le descellement, depuis le nœud Ansible et avec la même identité AppRole que les playbooks ;
  • Surveillez l’état de scellement (/v1/sys/health) : le risque principal est l’oubli.

Articles liés