
Un EE en production se rebuild toutes les semaines (CVEs), se scanne, se signe et se publie automatiquement. Cette page fournit deux pipelines équivalents — GitHub Actions et GitLab CI — durcis selon les pratiques 2026 : actions pinnées par SHA, permissions: {}, persist-credentials: false, scan Trivy bloquant, signature cosign keyless via Sigstore.
À la fin, vous saurez intégrer la chaîne build → scan → sign → push dans votre CI préférée et configurer Podman côté consommateur pour exiger une signature valide.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Pipeline GitHub Actions complet pour builder + push un EE.
- Équivalent GitLab CI avec stages.
- Scan Trivy en mode bloquant (
exit-code: 1sur HIGH/CRITICAL). - Signature cosign keyless via OIDC (sans clé privée à gérer).
- Politique Podman côté consommateur pour exiger les signatures.
- Bonnes pratiques 2026 : SHA pinning, permissions minimales, durcissement supply-chain.
Prérequis
Section intitulée « Prérequis »- Avoir lu Construire un EE.
- Compte GitHub ou GitLab.
- Accès à un registre OCI : GHCR (gratuit avec GitHub) ou GitLab Registry (intégré).
Vue d’ensemble du pipeline
Section intitulée « Vue d’ensemble du pipeline »[trigger push main] ↓ ┌────────────┐ │ build │ ← ansible-builder build --tag :sha :latest └────────────┘ ↓ ┌────────────┐ │ scan │ ← trivy image (HIGH+CRITICAL → exit 1) └────────────┘ ↓ ┌────────────┐ │ push │ ← podman push :sha :latest └────────────┘ ↓ ┌────────────┐ │ sign │ ← cosign sign --yes (keyless OIDC) └────────────┘Quatre étapes, séquentielles, bloquantes sur erreur. Si Trivy détecte une CVE HIGH non patchable, le pipeline échoue et l’image n’est pas publiée.
Pipeline GitHub Actions
Section intitulée « Pipeline GitHub Actions »name: Build & publish Execution Environmenton: push: branches: [main] paths: - 'execution-environment.yml' - 'requirements.*' - 'bindep.txt' - '.github/workflows/build-ee.yml'
permissions: {} # ← global = aucune
jobs: build-and-scan: runs-on: ubuntu-24.04 permissions: contents: read # ← lire le repo packages: write # ← push GHCR id-token: write # ← cosign keyless OIDC steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.2.2 with: persist-credentials: false # ← bloque token Git
- name: Set up Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12'
- name: Install ansible-builder run: | pip install ansible-builder==3.1.0 ansible-core==2.18.1
- name: Build EE run: | ansible-builder build \ --tag ghcr.io/${{ github.repository_owner }}/my-ee:${{ github.sha }} \ --tag ghcr.io/${{ github.repository_owner }}/my-ee:latest \ --container-runtime podman \ --file execution-environment.yml \ --context ./context \ --verbosity 2
- name: Scan EE with Trivy uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.31.0 with: image-ref: 'ghcr.io/${{ github.repository_owner }}/my-ee:${{ github.sha }}' severity: 'HIGH,CRITICAL' exit-code: '1' # ← bloque si CVE
- name: Login to GHCR uses: redhat-actions/podman-login@9184318aae1ee5034fbfbacc0388acf12669171f # v1.7 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Push EE run: | podman push ghcr.io/${{ github.repository_owner }}/my-ee:${{ github.sha }} podman push ghcr.io/${{ github.repository_owner }}/my-ee:latest
- name: Install cosign uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.10.1
- name: Sign EE (keyless OIDC) run: | cosign sign --yes \ ghcr.io/${{ github.repository_owner }}/my-ee:${{ github.sha }}Bonnes pratiques 2026 visibles
Section intitulée « Bonnes pratiques 2026 visibles »uses:pinné par SHA 40 caractères (@b4ffde65...), pas par tag — protège contre tag mutation (un attaquant qui repush une actionv4.2.2malicieuse).permissions: {}au workflow — aucune permission par défaut. Permissions élevées uniquement dans le job qui en a besoin.persist-credentials: falsesuractions/checkout— bloque l’usage du token Git après le checkout (évite l’exfiltration via un script externe).id-token: writeuniquement pour le job qui signe — limite la portée de l’OIDC.exit-code: '1'sur Trivy — fait échouer le pipeline sur une CVE non corrigée.
Pipeline GitLab CI équivalent
Section intitulée « Pipeline GitLab CI équivalent »stages: - build - scan - push - sign
variables: EE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA EE_LATEST: $CI_REGISTRY_IMAGE:latest
build-ee: stage: build image: quay.io/ansible/ansible-builder:3.1.0 script: - ansible-builder build --tag "$EE_TAG" --tag "$EE_LATEST" --container-runtime podman --file execution-environment.yml --context ./context
trivy-scan: stage: scan image: aquasec/trivy:0.59.1 script: - trivy image --severity HIGH,CRITICAL --exit-code 1 "$EE_TAG" needs: [build-ee]
push-ee: stage: push image: quay.io/podman/stable:latest before_script: - echo "$CI_REGISTRY_PASSWORD" | podman login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" script: - podman push "$EE_TAG" - podman push "$EE_LATEST" needs: [trivy-scan] only: [main]
cosign-sign: stage: sign image: gcr.io/projectsigstore/cosign:v2.4.1 script: - cosign sign --yes "$EE_TAG" needs: [push-ee] only: [main]Différences avec GitHub Actions :
- GitLab utilise
$CI_REGISTRY_IMAGEautomatique ($CI_PROJECT_URLcôté registry). - Pas de
permissions:au niveau projet — la sécurité passe par les environnements protégés (environment: production). cosign keylessfonctionne aussi via OIDC GitLab depuis 2024.
Cosign keyless — comment ça marche
Section intitulée « Cosign keyless — comment ça marche »Avant 2022, signer une image OCI demandait de gérer une clé privée : générée, stockée, distribuée, rotée. Lourd.
Cosign keyless s’appuie sur Sigstore :
- Le pipeline CI obtient un token OIDC de GitHub/GitLab (preuve d’identité du job).
- Fulcio (CA Sigstore) émet un certificat éphémère (durée : 10 minutes) lié à cette identité.
- cosign signe l’image avec ce certificat.
- La signature est inscrite publiquement dans Rekor (transparence log).
Pas de clé privée à gérer — l’identité du job CI est la clé. Vérification :
cosign verify ghcr.io/myorg/my-ee:abc123 \ --certificate-identity-regexp 'https://github.com/myorg/.+' \ --certificate-oidc-issuer https://token.actions.githubusercontent.comVérifie que l’image a été signée par un job qui tournait dans le repo myorg/.+ (pattern d’identité) issu du provider GitHub Actions (issuer OIDC).
Politique Podman côté consommateur
Section intitulée « Politique Podman côté consommateur »Pour exiger des images signées sur les serveurs de prod :
{ "default": [{"type": "reject"}], "transports": { "docker": { "ghcr.io/myorg/my-ee": [{ "type": "sigstoreSigned", "fulcioCAData": "...", "rekorPublicKeyData": "...", "signedIdentity": {"type": "remapIdentity"} }] } }}À placer dans /etc/containers/policy.json. Sans ce fichier, n’importe qui pousse une image avec le bon nom et elle est exécutée. Avec, seules les images signées par votre OIDC d’org sont autorisées.
Lab pratique
Section intitulée « Lab pratique »Le lab ee/ci-pipeline (labs/ee/ci-pipeline/) fournit les deux pipelines complets (GitHub Actions + GitLab CI) avec 8 tests pytest qui valident le SHA pinning, les permissions minimales, le scan Trivy bloquant et la signature cosign.
Bonnes pratiques 2026 — récap
Section intitulée « Bonnes pratiques 2026 — récap »- SHA pinning sur toutes les actions GitHub. Renovate génère les PR de bump auto.
permissions: {}au niveau workflow, élargies par job au strict nécessaire.persist-credentials: falsesuractions/checkout.- Scan Trivy bloquant :
severity: HIGH,CRITICAL,exit-code: 1. - Cosign keyless : pas de clé privée à gérer.
- Politique Podman côté consommateur pour exiger les signatures.
zizmor+poutinelints obligatoires sur les workflows.
À retenir
Section intitulée « À retenir »- 4 étapes : build → scan Trivy → push → cosign sign.
- GitHub Actions :
permissions: {}+ SHA pinning +id-token: writepour cosign keyless. - GitLab CI : 4 stages,
needs:pour la séquentialité,only: [main]pour push/sign. - Cosign keyless = OIDC du runner CI au lieu d’une clé privée.
- Politique Podman côté consommateur pour exiger la signature.
- Trivy bloquant : pas de release silencieuse en présence de CVE HIGH.