Security musings

Catégories

Tags

🔍 Licence d'Utilisation 🔍

Sauf mention contraire, le contenu de ce blog est sous licence CC BY-NC-ND 4.0.

© 2025 à 2042 Sébastien Gioria. Tous droits réservés.

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 :

  1. Un environnement de build de confiance qui garantit l’isolation.
  2. 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 :

  1. Un développeur pousse du code sur une branche protégée.
  2. Un workflow GitHub Actions se déclenche.
  3. Le workflow demande un token OIDC à GitHub, qui atteste de son identité (repo, commit SHA, déclencheur…).
  4. 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).
  5. 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.
  6. La signature est enregistrée dans un journal de transparence public (Rekor).
  7. 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 :

  1. Le job build-package compile mon package Python (wheel + sdist), calcule les hash SHA256 de tous les artefacts, et les upload.
  2. Le job provenance utilise 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 :

  1. Exécuter le build Python dans un environnement sécurisé.
  2. Générer les packages (my-app-1.0.0-py3-none-any.whl et my-app-1.0.0.tar.gz).
  3. Calculer les empreintes cryptographiques des artefacts.
  4. Générer la provenance, la signer via Sigstore, et créer une attestation (multiple.intoto.jsonl).
  5. 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


À retenir

À retenir 📌

  • SLSA 3 est accessible : Grâce aux workflows réutilisables, l'implémentation est grandement simplifiée.
  • Le duo gagnant : GitHub Actions (pour l'identité OIDC et l'isolation) + Sigstore (pour la signature "sans clé").
  • Permissions OIDC : La clé du système est `permissions: id-token: write` dans le workflow GitHub.
  • Utiliser les "builders" officiels : `slsa-github-generator` est la voie royale pour garantir la conformité.
  • Vérifier est aussi important que générer : Utiliser `slsa-verifier` dans les pipelines de déploiement pour boucler la chaîne de confiance.

SLSA Niv3