Exercice 05 : Sécuriser la chaîne d'approvisionnement logicielle
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Générer un SBOM (Software Bill of Materials) avec
syft - ✅ Signer une image Docker avec
cosignet vérifier la signature - ✅ Configurer GitHub Actions pour attester une image
- ✅ Expliquer les niveaux SLSA 1 à 3 et leurs exigences
- ✅ Décrire le mécanisme d'une attaque supply chain (type SolarWinds)
Durée estimée : 30 minutes
Difficulté :⭐⭐⭐⭐☆ (Avancé)
Prérequis :
- Docker installé
- Compte GitHub (pour la partie GitHub Actions)
- cosign et syft installés (instructions ci-dessous)
📖 Contexte
En décembre 2020, l'attaque SolarWinds a compromis la chaîne de build d'un éditeur de logiciels : les attaquants ont injecté du code malveillant dans le processus de compilation, distribuant une mise à jour corrompue à 18 000 clients. La supply chain sécurity répond à cette menace : signer les artefacts, auditer les dépendances, et garantir l'intégrité du processus de build. Le framework SLSA (Supply chain Levels for Software Artifacts) définit quatre niveaux de maturité pour y parvenir.
📋 Énoncé
Inventoriez les dépendances d'une image Docker avec syft, signez-la avec cosign, et configurez une attestation dans GitHub Actions.
🧭 Déroulement de l'exercice
Tâche 1 : Générer un SBOM avec syft
Installez syft et générez un SBOM pour l'image nginx:alpine en format SPDX-JSON.
Indice : Installez syft avec
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin. Puis :syft nginx:alpine -o spdx-json > sbom.json. Le SBOM liste tous les packages installés dans l'image avec leurs versions et licences.
Vérification : cat sbom.json | jq '.packages | length' retourne un nombre positif (les packages présents dans l'image).
Tâche 2 : Analyser le SBOM
Explorez le contenu du SBOM pour extraire les informations clés : liste des packages, versions, et licences.
Indice :
syft nginx:alpineen mode table (sans-o) affiche directement un tableau lisible. Pour extraire les licences du JSON :cat sbom.json | jq '[.packages[].licenseDeclared] | unique'. Un SBOM complet permet d'identifier rapidement les packages affectés par une nouvelle CVE publiée.
Vérification : Vous identifiez au moins 10 packages dans le SBOM de nginx:alpine avec leurs versions respectives.
Tâche 3 : Signer une image avec cosign
Installez cosign, générez une paire de clés, et signez une image locale.
Indice :
cosign generate-key-paircréecosign.key(privée) etcosign.pub(publique). Pour signer :cosign sign --key cosign.key <registry>/<image>:<tag>. Pour tester en local sans registry, utilisez un registre local :docker run -d -p 5000:5000 registry:2puis poussez une image verslocalhost:5000/test:v1.
Vérification : cosign verify --key cosign.pub localhost:5000/test:v1 retourne les informations de signature sans erreur.
Tâche 4 : Configurer une attestation dans GitHub Actions
Écrivez un workflow GitHub Actions qui construit une image, la pousse vers GitHub Container Registry (ghcr.io), et l'atteste avec l'action officielle.
Indice : Utilisez
actions/attest-build-provenance@v1après le build et le push de l'image. Cette action génère une attestation SLSA de provenance signée par Sigstore, prouvant que l'image a été construite par ce workflow GitHub Actions précis. Le tokenGITHUB_TOKENsuffit - aucune clé externe n'est nécessaire.
Vérification : Dans GitHub > packages, la page de l'image affiche un badge "Provenance attestation". La commande gh attestation verify oci://ghcr.io/<owner>/<image>:<tag> --repo <owner>/<repo> confirme l'attestation.
Tâche 5 : Analyser les niveaux SLSA
Sans outil supplémentaire, étudiez la grille SLSA et évaluez à quel niveau correspond votre workflow GitHub Actions.
Indice : SLSA niveau 1 : build scripté (pas de build manuel). Niveau 2 : build sur infrastructure hébergée avec provenance générée. Niveau 3 : build hermétique avec environnement éphémère et provenance vérifiable par un tiers. GitHub Actions +
attest-build-provenanceatteint le niveau 2 (voire 3 avec des runners éphémères). Niveau 4 n'existe plus dans SLSA v1.0 (remplacé par des profils).
Vérification : Vous pouvez expliquer pourquoi SLSA niveau 3 résiste à une attaque de type SolarWinds (build hermétique = pas d'injection possible pendant la compilation).
🗂️ Mini-Projet : Pipeline supply chain sécurisée
# 1. Installer syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh \
| sh -s -- -b /usr/local/bin
# 2. Installer cosign
curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 \
-o /usr/local/bin/cosign && chmod +x /usr/local/bin/cosign
# 3. Générer le SBOM
syft nginx:alpine -o spdx-json > sbom-nginx.json
syft nginx:alpine # Affichage tabulaire lisible
# 4. Lancer un registry local
docker run -d -p 5000:5000 --name registry registry:2
# 5. Préparer une image de test
docker pull nginx:alpine
docker tag nginx:alpine localhost:5000/test:v1
docker push localhost:5000/test:v1
# 6. Générer la paire de clés cosign
cosign generate-key-pair
# Crée cosign.key (privée) et cosign.pub (publique)
# 7. Signer l'image
COSIGN_INSECURE_SKIP_TLS_VERIFY=1 cosign sign \
--key cosign.key \
--allow-insecure-registry \
localhost:5000/test:v1
# 8. Vérifier la signature
COSIGN_INSECURE_SKIP_TLS_VERIFY=1 cosign verify \
--key cosign.pub \
--allow-insecure-registry \
localhost:5000/test:v1Workflow GitHub Actions pour l'attestation :
# .github/workflows/build-attest.yml
name: Build and Attest Image
on:
push:
branches: [main]
permissions:
contents: read
packages: write
attestations: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
id: push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
format: spdx-json
output-file: sbom.spdx.json
- name: Attest build provenance
uses: actions/attest-build-provenance@v1
with:
subject-name: ghcr.io/${{ github.repository }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: trueCheckpoints de validation :
syft nginx:alpineliste les packages de l'image avec leurs versionssbom-nginx.jsonest généré en format SPDX-JSONcosign generate-key-paircréecosign.keyetcosign.pubcosign verifyconfirme la signature de l'image locale- Le workflow GitHub Actions contient les permissions
attestations: writeetid-token: write - Vous pouvez expliquer la différence entre SLSA niveau 1 et niveau 3