
Les pillars sortent les secrets des states, mais un pillar en clair reste lisible sur le master et dans Git. Ce module protège vraiment les données sensibles avec le renderer GPG : les valeurs sont chiffrées sur disque, donc versionnables, et déchiffrées à la volée par le master au moment de servir le pillar. Vous verrez aussi quand déléguer à Vault. Public visé : avancés maîtrisant les pillars.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre pourquoi un pillar clair ne suffit pas.
- Chiffrer des valeurs de pillar avec le renderer GPG.
- Consommer un secret chiffré dans un state sans le fuiter.
- Savoir quand déléguer à Vault.
Prérequis
Section intitulée « Prérequis »- Maîtriser les pillars et disposer de notions sur la gestion des secrets.
Le problème du pillar clair
Section intitulée « Le problème du pillar clair »Un pillar déplace le secret hors du state, mais ne le chiffre pas. Si vous
versionnez l'arbre des pillars, le mot de passe se retrouve en clair dans
Git ; sur le master, il est lisible par qui a les droits sur /srv/pillar.
Il faut donc une couche de chiffrement ou une source externe.
Les secrets vivent en pillar, jamais ailleurs
Section intitulée « Les secrets vivent en pillar, jamais ailleurs »Avant de chiffrer quoi que ce soit, une règle non négociable : un secret vit exclusivement en pillar, jamais en grains ni dans un state versionné. Les grains sont modifiables localement par tout utilisateur ayant accès au minion, donc non sûrs pour une donnée sensible : un minion pourrait forger un grain pour se faire servir une valeur qui ne le concerne pas. Un state, lui, part dans Git en clair. Le pillar reste la seule source légitime, servie par le master aux seuls minions ciblés.
Mettre en place le renderer GPG
Section intitulée « Mettre en place le renderer GPG »Le renderer GPG chiffre les valeurs d'un pillar avec une clé GPG dont la clé privée vit sur le master. Les valeurs sont stockées chiffrées dans le dépôt et déchiffrées à la volée au moment de servir le pillar. C'est l'option native, sans dépendance externe.
Créer le trousseau du master
Section intitulée « Créer le trousseau du master »Salt cherche son trousseau dans /etc/salt/gpgkeys. On l'y crée, puis on
génère une paire de clés dédiée :
sudo install -d -m 0700 /etc/salt/gpgkeyssudo gpg --homedir /etc/salt/gpgkeys --batch --gen-key <<'EOF'%no-protectionKey-Type: RSAKey-Length: 3072Name-Real: Salt MasterName-Email: salt@lab.localExpire-Date: 0%commitEOFLa clé apparaît dans le trousseau :
sudo gpg --homedir /etc/salt/gpgkeys --list-secret-keyssec rsa3072/5B92B68E 2026-07-14 [SCEAR]uid [ultimate] Salt Master <salt@lab.local>Chiffrer une valeur
Section intitulée « Chiffrer une valeur »On chiffre le secret pour la clé du master, en sortie ASCII armor :
echo -n 'S3cr3t-PostgreSQL-2026' \ | sudo gpg --homedir /etc/salt/gpgkeys --armor --batch \ --trust-model always --encrypt -r salt@lab.local-----BEGIN PGP MESSAGE-----
hQGMA9YZatJbkraOAQwAl1EEFnfnN1WqkRgsjQET+Bsk0XiDqpviUfartwq2nOpl...-----END PGP MESSAGE-----Le pillar chiffré
Section intitulée « Le pillar chiffré »Le fichier pillar s'ouvre par le shebang #!yaml|gpg : il indique à Salt de
rendre le YAML, puis de faire passer le résultat dans le renderer GPG,
qui déchiffre tout bloc PGP MESSAGE rencontré. Le bloc chiffré est indenté
sous la clé, dans un scalaire littéral | :
#!yaml|gpg
db: password: | -----BEGIN PGP MESSAGE-----
hQGMA9YZatJbkraOAQwAl1EEFnfnN1WqkRgsjQET+Bsk0XiDqpviUfartwq2nOpl ... -----END PGP MESSAGE-----Le top file des pillars ne change pas : il cible db1 par identifiant,
et non par grain, précisément parce qu'un grain se forge.
base: 'role:web': - match: grain - web 'db1': - dbAprès modification, on recharge les pillars sur les minions :
sudo salt '*' saltutil.refresh_pillarTravaux pratiques : un secret qui ne fuit jamais
Section intitulée « Travaux pratiques : un secret qui ne fuit jamais »Vérifions la promesse à chaque étage de la chaîne. Sur disque d'abord : le fichier pillar que vous versionnez ne contient aucune valeur en clair, seul le bloc chiffré.
sudo grep -c 'S3cr3t-PostgreSQL-2026' /srv/pillar/db.sls0En sortie de commande ensuite. Depuis Salt 3008, la valeur est masquée par défaut, ce qui évite de l'exposer dans un historique de shell ou un log :
sudo salt db1 pillar.itemsdb1: ---------- db: ---------- password: **********Le déchiffrement fonctionne pourtant bien : unmask=True le prouve, en
affichant la valeur que le master a déchiffrée pour ce minion.
sudo salt db1 pillar.items unmask=Truedb1: ---------- db: ---------- password: S3cr3t-PostgreSQL-2026L'isolation tient enfin : web1 n'est pas ciblé par le pillar db, il ne
reçoit donc que ses propres données, même en demandant explicitement les
valeurs en clair.
sudo salt web1 pillar.items unmask=Trueweb1: ---------- app: ---------- admin_email: ops@exemple.fr paquets_web: - nginx - certbotConsommer le secret dans un state
Section intitulée « Consommer le secret dans un state »Le state lit le pillar comme n'importe quelle autre donnée, avec la syntaxe
mapping et une valeur de repli. Le fichier produit est en 0600,
lisible du seul root :
/etc/appdb/db.conf: file.managed: - makedirs: true - user: root - group: root - mode: "0600" - contents: | host=localhost password={{ salt['pillar.get']('db:password', '') }}sudo salt db1 state.apply dbconf ID: /etc/appdb/db.conf Comment: File /etc/appdb/db.conf updated
Succeeded: 1 (changed=1)Failed: 0Le second passage ne change rien, et le fichier porte bien le secret avec des permissions strictes :
sudo salt db1 cmd.run 'stat -c "%a %U:%G" /etc/appdb/db.conf'db1: 600 root:rootLa boucle est bouclée : le secret est chiffré dans le dépôt, déchiffré par le master au moment de servir, masqué en sortie CLI, et n'atterrit en clair que dans un fichier 0600 sur la machine qui en a besoin.
Quand déléguer à Vault
Section intitulée « Quand déléguer à Vault »Le renderer GPG fige une valeur chiffrée dans Git. C'est parfait pour des secrets stables et un parc modeste, mais cela montre ses limites dès que le nombre de secrets grandit : changer un mot de passe impose de rechiffrer et de recommiter, et rien n'expire tout seul.
HashiCorp Vault répond à ces limites : les states récupèrent le secret au moment de l'exécution, sans jamais le stocker dans le dépôt. Surtout, Vault génère des identifiants à durée de vie courte et sait les révoquer sans redéployer un pillar. Cette rotation automatique et ces secrets dynamiques (identifiants de base créés à la demande, puis expirés) réduisent fortement la fenêtre d'exposition en cas de fuite.
Le critère de bascule est simple : tant que vos secrets sont peu nombreux et rarement changés, le GPG suffit et coûte moins cher en exploitation. Dès que vous avez besoin de rotation, de révocation ou d'un audit centralisé des accès, passez à Vault. Les guides HashiCorp Vault et les secrets dynamiques de base de données détaillent cette mise en place.
Ne jamais exposer un secret dans une sortie
Section intitulée « Ne jamais exposer un secret dans une sortie »Un secret correctement chiffré peut quand même fuir en clair au moment de
l'exécution. Salt, comme tout outil, peut journaliser une valeur en mode
verbeux ou l'afficher dans un message d'erreur. Le masquage CLI introduit en
3008 aide, mais il ne couvre pas tout : un cmd.run qui affiche un fichier de
configuration, un mode debug, un retour d'erreur verbeux suffisent à
recracher la valeur. Ces traces atterrissent dans les logs du master, dans un
historique de shell ou dans un pipeline CI/CD, hors de tout contrôle.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
Pillar render error: Rendering SLS 'db' failed | Le master ne peut pas lire le trousseau | chown -R salt:salt /etc/salt/gpgkeys (le master tourne en salt, pas root) |
gpg: WARNING: unsafe ownership on homedir | Trousseau appartenant à root | Même correction, puis redémarrer salt-master |
gpg: decryption failed: No secret key | Clé privée absente du trousseau du master | Vérifier gpg --homedir /etc/salt/gpgkeys --list-secret-keys |
| Le pillar reste vide sur le minion | Pillars non rechargés | salt '<minion>' saltutil.refresh_pillar |
La valeur s'affiche ********** | Comportement normal depuis 3008 | Ajouter unmask=True pour vérifier, jamais en usage courant |
À retenir
Section intitulée « À retenir »- Un secret vit exclusivement en pillar, jamais en grains (modifiables localement) ni dans un state versionné.
- Un pillar clair n'est pas chiffré : lisible sur le master et dans Git.
- Le renderer GPG (
#!yaml|gpg) chiffre les valeurs : le dépôt reste versionnable, le master déchiffre à la volée. - Le trousseau doit appartenir à l'utilisateur
saltdu master, sinon le rendu du pillar échoue. - La clé privée du master déchiffre tout le parc : c'est l'actif à protéger en priorité.
- Vault prend le relais quand il faut de la rotation, de la révocation ou des secrets dynamiques.
- Ne jamais journaliser un secret : vu une fois dans un log, il est compromis.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- HashiCorp Vault : passer aux secrets dynamiques et à la rotation quand le parc grandit.
- Secrets dynamiques de base de données : générer des identifiants à durée de vie courte.
- Gérer ses secrets : les principes, indépendamment de l'outil.