
Pour partager un projet Pulumi local sans laisser une stack lisible par
n'importe quel shell du poste, commencez par une vraie passphrase, des
permissions strictes et un preview self-hosted qui lit son secret depuis un
stockage adapte. Ce guide montre comment créer une stack team chiffrée,
vérifier que Pulumi echoue sans passphrase, utiliser
PULUMI_CONFIG_PASSPHRASE_FILE en local, durcir le backend file://~ et
injecter la passphrase dans GitHub Actions via secrets. Le flux a été
rejoue le 1 avril 2026 sur KVM/libvirt avec preview, up, verification
virsh, puis destroy.
Ce que vous allez sécuriser
Section intitulée « Ce que vous allez sécuriser »Le but n'est pas de transformer le projet en usine a gaz. Vous allez protéger les trois zones qui fuient le plus vite dans un petit workflow d'equipe :
- le fichier de stack et ses secrets ;
- le backend local et ses permissions ;
- la CI self-hosted qui doit lire la passphrase sans la coder en dur.
Dans le lab rejoue, la stack sécurisée s'appelle team et pilote une VM
pulumi-team-vm sur le réseau pulumi-team-net.
Pourquoi le workflow dev ne suffit plus
Section intitulée « Pourquoi le workflow dev ne suffit plus »Dans les guides précédents, le projet était volontairement simple : stack dev,
backend local, secret masque dans Pulumi et workflow self-hosted minimal.
Pour apprendre, c'est très bien. Pour travailler a plusieurs, ce n'est plus
suffisant.
Les risques deviennent vite concrets :
- une passphrase vide laisse la stack dechiffrable sans protection réelle ;
- un fichier
Pulumi.<stack>.yamltrop ouvert peut être lu par un autre compte du même poste ; - un workflow CI avec une passphrase en clair dans le YAML devient une fuite de secret versionnée.
Autrement dit : la bonne question n'est plus seulement "est-ce que le preview
passe ?" mais aussi "qui peut déchiffrer la stack et ou le secret circule-t-il ?".
Etape 1 - Créer une stack d'equipe chiffrée des l'initialisation
Section intitulée « Etape 1 - Créer une stack d'equipe chiffrée des l'initialisation »Commencez par une nouvelle stack dédiée a un usage d'equipe. Dans le lab valide,
la passphrase n'est plus vide et la stack porte des noms de ressources séparés
de dev.
source venv/bin/activateexport PULUMI_CONFIG_PASSPHRASE='Team-Pulumi-2026!'
pulumi stack init teampulumi config set libvirt:uri qemu:///system --stack teampulumi config set vmName pulumi-team-vm --stack teampulumi config set networkName pulumi-team-net --stack teampulumi config set vmMemoryMiB 3072 --stack teampulumi config set vmVcpu 2 --stack teampulumi config set adminUser devops --stack teampulumi config set --secret adminPassword 'Team-Pulumi-2026!' --stack teamchmod 600 Pulumi.team.yamlVerification :
pulumi config --stack teamstat -c '%a %n' Pulumi.team.yamlsed -n '1,120p' Pulumi.team.yamlDans le lab rejoue, vous devez observer :
adminPassword [secret]dans la sortie depulumi config;- des permissions
600surPulumi.team.yaml; - un bloc
encryptionsalt:et une entréesecure:dans le fichier de stack, sans secret en clair.
Etape 2 - Vérifier que Pulumi refuse la stack sans passphrase
Section intitulée « Etape 2 - Vérifier que Pulumi refuse la stack sans passphrase »Une stack d'equipe ne doit pas être lisible si la passphrase n'est pas fournie. Testez explicitement ce refus :
env -u PULUMI_CONFIG_PASSPHRASE \ pulumi preview --stack team --diff --non-interactiveDans le lab valide, Pulumi retourne :
error: getting stack configuration: get stack secrets manager: passphrase must be set with PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE environment variablesCe message est utile : il prouve que la stack team n'est plus exploitable sans
secret de déchiffrement.
Rejouez ensuite le preview avec la passphrase correcte :
export PULUMI_CONFIG_PASSPHRASE='Team-Pulumi-2026!'pulumi preview --stack team --diff --non-interactiveLe preview doit alors annoncer les 7 ressources attendues et continuer a
masquer userData comme secret.
Etape 3 - Éviter l'export direct de la passphrase dans le shell
Section intitulée « Etape 3 - Éviter l'export direct de la passphrase dans le shell »Exporter la passphrase dans l'environnement fonctionne, mais ce n'est pas
toujours le meilleur compromis. Pour un poste local, PULUMI_CONFIG_PASSPHRASE_FILE
évite de garder la valeur dans l'historique du shell ou dans un copier-coller
malheureux.
Dans le lab rejoue, ce flux fonctionne :
printf 'Team-Pulumi-2026!' > .pulumi-passphrasechmod 600 .pulumi-passphrase
env -u PULUMI_CONFIG_PASSPHRASE \ PULUMI_CONFIG_PASSPHRASE_FILE=$PWD/.pulumi-passphrase \ pulumi preview --stack team --diff --non-interactive
rm -f .pulumi-passphraseVerification : le preview passe normalement avec le fichier de passphrase,
alors que le même preview sans passphrase echoue.
Important : ce fichier ne doit jamais être committe. Gardez-le hors du repo
ou ajoutez-le explicitement a votre .gitignore si vous utilisez cette
stratégie au quotidien.
Etape 4 - Durcir les permissions du backend local
Section intitulée « Etape 4 - Durcir les permissions du backend local »Le backend file://~ reste pédagogique, mais il ne doit pas être lisible en
groupe sur un poste partage. Dans le lab, les permissions initiales de
~/.pulumi et ~/.pulumi/stacks étaient trop ouvertes pour un usage
sérieux.
Le durcissement rejoue est le suivant :
chmod 700 ~/.pulumi ~/.pulumi/stackschmod 600 Pulumi.dev.yaml Pulumi.team.yamlstat -c '%a %n' ~/.pulumi ~/.pulumi/stacks Pulumi.dev.yaml Pulumi.team.yamlDans le lab valide, la verification retourne :
700pour~/.pulumi;700pour~/.pulumi/stacks;600pourPulumi.dev.yamletPulumi.team.yaml.
Ce n'est pas un substitut a un backend distant avec verrouillage, mais c'est un minimum sain pour un backend local sur une machine Linux.
Etape 5 - Injecter la passphrase dans GitHub Actions sans la versionner
Section intitulée « Etape 5 - Injecter la passphrase dans GitHub Actions sans la versionner »Le workflow CI du guide précédent utilisait une passphrase vide. Pour une stack
partagée, il faut sortir le secret du YAML et le fournir via secrets.
Le workflow valide dans le lab est le suivant :
---name: pulumi-preview-secure
"on": pull_request: paths: - "**/*.py" - "Pulumi.yaml" - "Pulumi.team.yaml" - "requirements.txt" - ".github/workflows/pulumi-preview-secure.yml" workflow_dispatch:
permissions: contents: read
jobs: validate-preview: runs-on: - self-hosted - linux steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12"
- name: Install dependencies run: | python -m venv venv . venv/bin/activate pip install -r requirements.txt
- name: Compile Python sources run: | . venv/bin/activate python -m compileall .
- name: Run unit tests run: | . venv/bin/activate python -m unittest discover -s tests -p 'test_*.py'
- name: Check passphrase secret is available env: PULUMI_CONFIG_PASSPHRASE: ${{ secrets.PULUMI_CONFIG_PASSPHRASE }} run: | test -n "$PULUMI_CONFIG_PASSPHRASE"
- name: Pulumi preview env: PULUMI_CONFIG_PASSPHRASE: ${{ secrets.PULUMI_CONFIG_PASSPHRASE }} run: | . venv/bin/activate pulumi login --local pulumi stack select team pulumi preview --stack team --diff --non-interactiveVerification :
actionlint .github/workflows/pulumi-preview-secure.ymlyamllint .github/workflows/pulumi-preview-secure.ymlDans le lab rejoue, ces deux commandes passent proprement.
Trois choix sont importants ici :
- le runner reste self-hosted parce qu'il doit voir
qemu:///system; - la passphrase vient de
secrets.PULUMI_CONFIG_PASSPHRASEet non du repo ; - une etape intermediaire verifie que le secret est bien present avant de lancer
le
preview.
Etape 6 - Rejouer le cycle complet sur la stack sécurisée
Section intitulée « Etape 6 - Rejouer le cycle complet sur la stack sécurisée »Une sécurisation credible doit encore laisser le projet exploitable. Rejouez un cycle complet :
export PULUMI_CONFIG_PASSPHRASE='Team-Pulumi-2026!'
pulumi up --stack team --yesvirsh list --all | grep pulumi-team-vmvirsh domstate pulumi-team-vmvirsh domiflist pulumi-team-vm
pulumi destroy --stack team --yesDans le lab valide, vous obtenez :
- une VM
pulumi-team-vmen étatrunningaprèsup; - une interface
virtioreliee au réseaupulumi-team-net; - un
destroyqui supprime proprement les 7 ressources de la stack.
Cette etape compte autant que le reste. Une pile de sécurité qui empêche le
travail ou casse le destroy n'est pas une bonne sécurisation.
Ce que ce guide couvre réellement
Section intitulée « Ce que ce guide couvre réellement »Ce guide valide trois protections utiles et concrètes :
- le chiffrement de la stack avec une passphrase non vide ;
- la réduction de surface locale via des permissions
600/700; - l'injection de la passphrase dans une CI self-hosted via un secret GitHub.
Ce guide ne valide pas encore :
- un backend distant partage avec verrouillage ;
- un chiffrement par KMS ;
- des politiques RBAC ou SSO autour de Pulumi Cloud ;
- la rotation organisationnelle des secrets a grande échelle.
Il faut rester honnête sur ce périmètre. Ici, on durcit un workflow local sérieux. On ne pretend pas encore couvrir tout le sujet plateforme.
Pieges fréquents
Section intitulée « Pieges fréquents »| Symptôme | Cause probable | Solution |
|---|---|---|
incorrect passphrase | La valeur injectee ne correspond pas a la stack | Vérifiez la passphrase active et rejouez pulumi preview avec la bonne valeur |
passphrase must be set with PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE | Aucune passphrase n'a été fournie | Exportez PULUMI_CONFIG_PASSPHRASE ou pointez PULUMI_CONFIG_PASSPHRASE_FILE |
| La passphrase se retrouve dans le repo | Un fichier temporaire ou un YAML a été committe | Sortez la valeur du repo, supprimez le fichier et utilisez secrets ou un fichier local ignore |
Le preview CI echoue sur ubuntu-latest | Le runner ne voit pas qemu:///system | Gardez le workflow sur un runner self-hosted Linux |
| Le backend local reste trop ouvert | Les permissions de ~/.pulumi ou des fichiers de stack sont trop larges | Appliquez chmod 700 ~/.pulumi ~/.pulumi/stacks et chmod 600 Pulumi.<stack>.yaml |
A retenir
Section intitulée « A retenir »- Une stack d'equipe Pulumi doit exiger une passphrase non vide.
PULUMI_CONFIG_PASSPHRASE_FILEest utile pour éviter l'export direct du secret dans le shell.- Un backend local
file://~doit au minimum être limite au propriétaire sur un poste Linux. - Dans GitHub Actions, la passphrase doit venir d'un secret, jamais du YAML versionne.
- Une sécurisation utile se valide encore avec
preview -> up -> virsh -> destroy.