Module 3 – Chaîne de supply chain conteneurs

Optimisez vos Dockerfile, générez des SBOM, scannez et signez vos images avec Cosign, appliquez des policies Kyverno.

Vous avez désormais des images de base : assurons la qualité de la supply chain conteneurs. Nous couvrons Docker multi-stage, BuildKit, SBOM, scanning et signature Cosign.

Objectifs

  • Écrire des Dockerfile optimisés (multi-stage, non-root, cache) et automatiser les builds.
  • Générer et publier des SBOM (Syft) et réaliser des scans Trivy/Snyk.
  • Signer et vérifier vos images (Cosign) et définir des policies d’admission.

1. Dockerfile multi-stage

# syntax=docker/dockerfile:1.6
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app ./cmd/api

FROM gcr.io/distroless/base-debian12
WORKDIR /app
COPY --from=builder /src/app /app/app
USER nonroot
ENTRYPOINT ["/app/app"]
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD ["/app/app", "--health"]

Build :

docker buildx build --platform linux/amd64,linux/arm64   -t ghcr.io/acme/webapp:$(git rev-parse --short HEAD)   --push .

2. SBOM et scanning

syft ghcr.io/acme/webapp:$(git rev-parse --short HEAD) -o json > sbom.json
trivy image --security-checks vuln,config ghcr.io/acme/webapp:$(git rev-parse --short HEAD)

Ajoutez le rapport SBOM en artefact CI et configurez un seuil d’échec (CVSS ≥ 7).

3. Signature Cosign

cosign generate-key-pair
COSIGN_PASSWORD="${COSIGN_PASSWORD}" cosign sign ghcr.io/acme/webapp:$(git rev-parse --short HEAD)
cosign verify ghcr.io/acme/webapp:$(git rev-parse --short HEAD)

Stockez la clé privée dans un secret (Vault, GitHub OIDC + KMS).

4. Admission policy (Kyverno)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-signed-images
spec:
  validationFailureAction: enforce
  rules:
    - name: require-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - image: "ghcr.io/acme/*"
          key: "k8s://kube-system/cosign-pub"

5. Pipeline exemple (GitHub Actions)

name: container-supply-chain
on:
  push:
    branches: [ "main" ]
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - name: Build & push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
          platforms: linux/amd64,linux/arm64
      - name: Generate SBOM
        run: syft ghcr.io/${{ github.repository }}:${{ github.sha }} -o json > sbom.json
      - name: Scan image
        run: trivy image --severity CRITICAL,HIGH ghcr.io/${{ github.repository }}:${{ github.sha }}
      - name: Sign image
        env:
          COSIGN_YES: "true"
        run: cosign sign ghcr.io/${{ github.repository }}:${{ github.sha }}
      - uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json

6. Lab guidé

  1. Refactoriser un Dockerfile existant en multi-stage, exécuter docker scout cves pour vérifier le gain.
  2. Configurer BuildKit + cache remote (--cache-to type=registry). Mesurer le temps de build.
  3. Générer une SBOM (syft) et un scan (trivy), enregistrer les rapports dans artifacts/.
  4. Signer l’image via Cosign et stocker la clé publique dans Kubernetes (kubectl create secret generic cosign-pub --from-file=cosign.pub).
  5. Déployer la policy Kyverno et vérifier qu’une image non signée est rejetée.

Challenge optionnel

  • Mettre en place une politique d’expiration (retention GHCR) et un job Cron GitHub Actions pour nettoyer les tags anciens.
  • Automatiser la génération de SBOM CycloneDX et l’envoyer vers Dependency-Track.
  • Utiliser cosign attest avec les résultats Trivy comme attestation.

Solution détaillée

  1. Build optimisé : docker buildx build affiche #2 exporting to image en ~40% de temps en moins grâce au cache. docker history montre moins de couches.
  2. Scan : trivy doit afficher 0 vulnérabilité critique. Si ce n’est pas le cas, mettez à jour la base (ex. apt-get dist-upgrade dans le Dockerfile).
  3. Signature : cosign verify renvoie Verified OK. Vérifiez dans le registre que l’attestation est présente (crane ls ghcr.io/... --full).
  4. Policy Kyverno : déployez un Pod avec une image non signée → Error from server (Forbidden) ... fails policy verify-signed-images.
  5. Rapports : artifacts/sbom.json, artifacts/trivy.txt disponibles en artefacts pipeline.

Documentez les étapes dans docs/supply-chain.md avant de poursuivre.

Pièges fréquents

  • Déployer des images root : pensez à USER nonroot et RUN addgroup/adduser.
  • Oublier de purger le cache APT (rm -rf /var/lib/apt/lists/*) → images lourdes.
  • Signer avec une clé stockée en clair dans le dépôt : utilisez OIDC + KMS ou Vault.

Ressources

Checklist

  • ✅ Dockerfile multi-stage et non-root.
  • ✅ SBOM et scan générés automatiquement.
  • ✅ Images signées et vérifiées.
  • ✅ Policy d’admission empêchant les images non signées.