Aller au contenu
Infrastructure as Code medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Pulumi - securiser une stack et la CI d'equipe

12 min de lecture

logo pulumi

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 creer une stack team chiffree, verifier 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 ete rejoue le 1 avril 2026 sur KVM/libvirt avec preview, up, verification virsh, puis destroy.

Le but n’est pas de transformer le projet en usine a gaz. Vous allez proteger 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 securisee s’appelle team et pilote une VM pulumi-team-vm sur le reseau pulumi-team-net.

Dans les guides precedents, le projet etait volontairement simple : stack dev, backend local, secret masque dans Pulumi et workflow self-hosted minimal. Pour apprendre, c’est tres bien. Pour travailler a plusieurs, ce n’est plus suffisant.

Les risques deviennent vite concrets :

  • une passphrase vide laisse la stack dechiffrable sans protection reelle ;
  • un fichier Pulumi.<stack>.yaml trop ouvert peut etre lu par un autre compte du meme poste ;
  • un workflow CI avec une passphrase en clair dans le YAML devient une fuite de secret versionnee.

Autrement dit : la bonne question n’est plus seulement “est-ce que le preview passe ?” mais aussi “qui peut dechiffrer la stack et ou le secret circule-t-il ?”.

Etape 1 - Creer une stack d’equipe chiffree des l’initialisation

Section intitulée « Etape 1 - Creer une stack d’equipe chiffree des l’initialisation »

Commencez par une nouvelle stack dediee a un usage d’equipe. Dans le lab valide, la passphrase n’est plus vide et la stack porte des noms de ressources separes de dev.

Fenêtre de terminal
source venv/bin/activate
export PULUMI_CONFIG_PASSPHRASE='Team-Pulumi-2026!'
pulumi stack init team
pulumi config set libvirt:uri qemu:///system --stack team
pulumi config set vmName pulumi-team-vm --stack team
pulumi config set networkName pulumi-team-net --stack team
pulumi config set vmMemoryMiB 3072 --stack team
pulumi config set vmVcpu 2 --stack team
pulumi config set adminUser devops --stack team
pulumi config set --secret adminPassword 'Team-Pulumi-2026!' --stack team
chmod 600 Pulumi.team.yaml

Verification :

Fenêtre de terminal
pulumi config --stack team
stat -c '%a %n' Pulumi.team.yaml
sed -n '1,120p' Pulumi.team.yaml

Dans le lab rejoue, vous devez observer :

  • adminPassword [secret] dans la sortie de pulumi config ;
  • des permissions 600 sur Pulumi.team.yaml ;
  • un bloc encryptionsalt: et une entree secure: dans le fichier de stack, sans secret en clair.

Etape 2 - Verifier que Pulumi refuse la stack sans passphrase

Section intitulée « Etape 2 - Verifier que Pulumi refuse la stack sans passphrase »

Une stack d’equipe ne doit pas etre lisible si la passphrase n’est pas fournie. Testez explicitement ce refus :

Fenêtre de terminal
env -u PULUMI_CONFIG_PASSPHRASE \
pulumi preview --stack team --diff --non-interactive

Dans 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 variables

Ce message est utile : il prouve que la stack team n’est plus exploitable sans secret de dechiffrement.

Rejouez ensuite le preview avec la passphrase correcte :

Fenêtre de terminal
export PULUMI_CONFIG_PASSPHRASE='Team-Pulumi-2026!'
pulumi preview --stack team --diff --non-interactive

Le preview doit alors annoncer les 7 ressources attendues et continuer a masquer userData comme secret.

Etape 3 - Eviter l’export direct de la passphrase dans le shell

Section intitulée « Etape 3 - Eviter 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 evite de garder la valeur dans l’historique du shell ou dans un copier-coller malheureux.

Dans le lab rejoue, ce flux fonctionne :

Fenêtre de terminal
printf 'Team-Pulumi-2026!' > .pulumi-passphrase
chmod 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-passphrase

Verification : le preview passe normalement avec le fichier de passphrase, alors que le meme preview sans passphrase echoue.

Important : ce fichier ne doit jamais etre committe. Gardez-le hors du repo ou ajoutez-le explicitement a votre .gitignore si vous utilisez cette strategie au quotidien.

Le backend file://~ reste pedagogique, mais il ne doit pas etre lisible en groupe sur un poste partage. Dans le lab, les permissions initiales de ~/.pulumi et ~/.pulumi/stacks etaient trop ouvertes pour un usage serieux.

Le durcissement rejoue est le suivant :

Fenêtre de terminal
chmod 700 ~/.pulumi ~/.pulumi/stacks
chmod 600 Pulumi.dev.yaml Pulumi.team.yaml
stat -c '%a %n' ~/.pulumi ~/.pulumi/stacks Pulumi.dev.yaml Pulumi.team.yaml

Dans le lab valide, la verification retourne :

  • 700 pour ~/.pulumi ;
  • 700 pour ~/.pulumi/stacks ;
  • 600 pour Pulumi.dev.yaml et Pulumi.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 precedent utilisait une passphrase vide. Pour une stack partagee, 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@v4
- name: Setup Python
uses: actions/setup-python@v5
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-interactive

Verification :

Fenêtre de terminal
actionlint .github/workflows/pulumi-preview-secure.yml
yamllint .github/workflows/pulumi-preview-secure.yml

Dans 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_PASSPHRASE et 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 securisee

Section intitulée « Etape 6 - Rejouer le cycle complet sur la stack securisee »

Une securisation credible doit encore laisser le projet exploitable. Rejouez un cycle complet :

Fenêtre de terminal
export PULUMI_CONFIG_PASSPHRASE='Team-Pulumi-2026!'
pulumi up --stack team --yes
virsh list --all | grep pulumi-team-vm
virsh domstate pulumi-team-vm
virsh domiflist pulumi-team-vm
pulumi destroy --stack team --yes

Dans le lab valide, vous obtenez :

  • une VM pulumi-team-vm en etat running apres up ;
  • une interface virtio reliee au reseau pulumi-team-net ;
  • un destroy qui supprime proprement les 7 ressources de la stack.

Cette etape compte autant que le reste. Une pile de securite qui empeche le travail ou casse le destroy n’est pas une bonne securisation.

Ce guide valide trois protections utiles et concretes :

  • le chiffrement de la stack avec une passphrase non vide ;
  • la reduction 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 echelle.

Il faut rester honnete sur ce perimetre. Ici, on durcit un workflow local serieux. On ne pretend pas encore couvrir tout le sujet plateforme.

SymptomeCause probableSolution
incorrect passphraseLa valeur injectee ne correspond pas a la stackVerifiez la passphrase active et rejouez pulumi preview avec la bonne valeur
passphrase must be set with PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILEAucune passphrase n’a ete fournieExportez PULUMI_CONFIG_PASSPHRASE ou pointez PULUMI_CONFIG_PASSPHRASE_FILE
La passphrase se retrouve dans le repoUn fichier temporaire ou un YAML a ete committeSortez la valeur du repo, supprimez le fichier et utilisez secrets ou un fichier local ignore
Le preview CI echoue sur ubuntu-latestLe runner ne voit pas qemu:///systemGardez le workflow sur un runner self-hosted Linux
Le backend local reste trop ouvertLes permissions de ~/.pulumi ou des fichiers de stack sont trop largesAppliquez chmod 700 ~/.pulumi ~/.pulumi/stacks et chmod 600 Pulumi.<stack>.yaml
  • Une stack d’equipe Pulumi doit exiger une passphrase non vide.
  • PULUMI_CONFIG_PASSPHRASE_FILE est utile pour eviter l’export direct du secret dans le shell.
  • Un backend local file://~ doit au minimum etre limite au proprietaire sur un poste Linux.
  • Dans GitHub Actions, la passphrase doit venir d’un secret, jamais du YAML versionne.
  • Une securisation utile se valide encore avec preview -> up -> virsh -> destroy.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn