Dans mon précédent article sur SLSA, j’ai présenté le framework et ses niveaux de maturité. Maintenant, passons à la pratique. Comment atteindre le niveau 3, ce “sweet spot” qui offre de solides garanties de sécurité sans la complexité du niveau 4 ? La bonne nouvelle, c’est qu’avec GitHub Actions et Sigstore, c’est devenu étonnamment accessible.
Après avoir compris la théorie de SLSA, ma première question a été : “Ok, mais concrètement, je fais comment ?”. Le niveau 3 semble être l’objectif idéal : il garantit que mes builds sont isolés et que la provenance générée est non falsifiable. C’est ce niveau qui protège réellement contre les attaques de compromission de la plateforme de build, le risque le plus critique et simple a éviter à mes yeux.
Pour atteindre le niveau 3, il faut deux choses principales :
- Un environnement de build de confiance qui garantit l’isolation.
- Un moyen de signer la provenance de manière non falsifiable, sans qu’il soit possible de manipuler la clé de signature.
C’est là que la combinaison GitHub Actions + Sigstore intervient.
- GitHub Actions fournit les environnements de build éphémères et isolés. Surtout, il peut générer un token d’identité OIDC (OpenID Connect) unique pour chaque exécution de workflow.
- Sigstore (et son service de certification
Fulcio) peut utiliser ce token OIDC pour émettre un certificat de signature à très courte durée de vie.
Le résultat ? Je peux signer mes artefacts “sans clé” (keyless signing). La signature est liée à l’identité de mon workflow GitHub, pas à un secret que je dois gérer (et que je pourrais me faire voler).
L’architecture cible pour SLSA 3
Le flux que nous allons mettre en place est le suivant :
- Un développeur pousse du code sur une branche protégée.
- Un workflow GitHub Actions se déclenche.
- Le workflow demande un token OIDC à GitHub, qui atteste de son identité (repo, commit SHA, déclencheur…).
- Le processus de build (un “builder” SLSA) utilise ce token pour obtenir un certificat de signature éphémère auprès de Fulcio (l’autorité de certification de Sigstore).
- Le builder compile le code, génère l’attestation de provenance (un fichier
.intoto.jsonl), et la signe avec le certificat éphémère. - La signature est enregistrée dans un journal de transparence public (
Rekor). - L’artefact et son attestation signée sont publiés dans un registre (ex: GHCR).
Ce processus garantit que seule la plateforme GitHub Actions a pu générer cette signature pour ce build spécifique. Un attaquant ne peut pas la reproduire, même s’il vole mes identifiants GitHub.
Mise en pratique : Utiliser les “SLSA Builders”
La manière la plus simple d’atteindre le niveau 3 est d’utiliser les workflows réutilisables fournis par le projet SLSA lui-même. Ils encapsulent toute la complexité de la génération et de la signature de la provenance.
Je vais montrer deux exemples : un pour un package Python et un pour une image de conteneur.
1. Sécuriser le build d’un package Python
Imaginons que j’ai un projet Python que je veux packager et signer. Pour cela, je vais utiliser le builder générique SLSA qui me permet de définir mes propres étapes de build.
Voici à quoi ressemble le workflow .github/workflows/build.yml :
name: SLSA Build Python Package
on:
workflow_dispatch:
push:
branches: [ "main" ]
jobs:
# Job 1 : Build du package Python
build-package:
runs-on: ubuntu-latest
outputs:
hashes: $
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build wheel setuptools
- name: Build package
run: python -m build
- name: Generate hashes
id: hash
run: |
cd dist
echo "hashes=$(sha256sum * | base64 -w0)" >> "$GITHUB_OUTPUT"
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: python-package
path: dist/
if-no-files-found: error
# Job 2 : Génération de la provenance SLSA
provenance:
needs: [build-package]
permissions:
id-token: write # Pour l'authentification OIDC avec Sigstore
contents: write # Pour uploader les attestations
actions: read # Pour utiliser le workflow réutilisable
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
with:
base64-subjects: "$"
upload-assets: true
Ce workflow fait deux choses importantes :
- Le job
build-packagecompile mon package Python (wheel + sdist), calcule les hash SHA256 de tous les artefacts, et les upload. - Le job
provenanceutilise le builder générique SLSA pour générer et signer l’attestation de provenance basée sur ces hash.
À chaque push sur main, GitHub Actions va :
- Exécuter le build Python dans un environnement sécurisé.
- Générer les packages (
my-app-1.0.0-py3-none-any.whletmy-app-1.0.0.tar.gz). - Calculer les empreintes cryptographiques des artefacts.
- Générer la provenance, la signer via Sigstore, et créer une attestation (
multiple.intoto.jsonl). - Uploader les packages et leur attestation comme artefacts du build.
Je n’ai eu à gérer aucune clé, aucun secret.
2. Sécuriser le build d’une image de conteneur
Pour les images de conteneur, le principe est similaire. On utilise un autre workflow réutilisable qui se charge de builder l’image avec Docker/BuildKit et de générer la provenance.
name: SLSA Build Container Image
on:
workflow_dispatch:
push:
branches: [ "main" ]
jobs:
build-container:
permissions:
id-token: write # Pour l'authentification OIDC avec Sigstore
contents: read # Pour le checkout du code
packages: write # Pour pusher l'image sur GHCR
actions: read # Pour utiliser le workflow réutilisable
uses: slsa-framework/slsa-github-generator/.github/workflows/container_slsa3.yml@v1.9.0
with:
# Le nom de l'image à builder et pusher
image-name: my-secure-app
# Le registre où pusher l'image (ici, GitHub Container Registry)
registry: ghcr.io/$
# Le Dockerfile à utiliser
dockerfile: "Dockerfile"
# Le contexte de build
build-context: "."
Ce workflow va builder l’image, la pusher sur GHCR, puis générer et signer une attestation de provenance qu’il attachera à l’image dans le registre.
Vérifier la provenance
Générer la provenance, c’est bien. La vérifier, c’est mieux ! C’est ce qui permet de s’assurer, avant un déploiement, que l’artefact est bien légitime.
Pour cela, j’utilise l’outil slsa-verifier.
# Installer slsa-verifier
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest
# Vérifier un package Python (wheel)
slsa-verifier verify-artifact \
my-app-1.0.0-py3-none-any.whl \
--provenance-path multiple.intoto.jsonl \
--source-uri github.com/my-org/my-repo \
--source-branch main
# Ou vérifier le source distribution
slsa-verifier verify-artifact \
my-app-1.0.0.tar.gz \
--provenance-path multiple.intoto.jsonl \
--source-uri github.com/my-org/my-repo \
--source-branch main
# La commande doit retourner "PASSED: Verified SLSA provenance"
Pour une image de conteneur, la vérification est encore plus simple car l’attestation est attachée à l’image :
# Vérifier une image de conteneur
slsa-verifier verify-image ghcr.io/my-org/my-secure-app:latest \
--source-uri github.com/my-org/my-repo \
--source-branch main
Cette commande va automatiquement trouver l’attestation dans le registre, vérifier sa signature, valider son contenu (source, builder…) et confirmer que l’image est conforme au niveau 3 de SLSA.
Je peux intégrer cette vérification dans mon pipeline de déploiement (CD) ou dans mon cluster Kubernetes avec un admission controller comme Kyverno pour bloquer tout déploiement d’artefact non vérifié.
Quelques références pour aller plus loin
- SLSA GitHub Generator : Le projet officiel pour les workflows réutilisables. C’est le point de départ.
- Sigstore Documentation : Pour comprendre le “keyless signing” et l’écosystème (Fulcio, Rekor).
- SLSA Verifier : L’outil pour vérifier les attestations.
- Using OIDC with GitHub Actions : La documentation de GitHub sur le sujet. Essentielle pour comprendre comment fonctionne l’authentification.
À retenir
