
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 »
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # ← bloque token Git
- name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.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@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.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@4934294ad0449894bcd1e9f191899d7292469603 # 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@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- 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 # ce depot ne publie que `latest` : seul le digest fige l'image image: quay.io/ansible/ansible-builder@sha256:75c4e4b5be6aab3386381b1e202ad382002f6830fd4b524bcd5ddc718e4f6a89 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@sha256:029e990b328d149bf0a9ffe355919041e1f86192db2df47e217f8a36dd42ceac script: - trivy image --severity HIGH,CRITICAL --exit-code 1 "$EE_TAG" needs: [build-ee]
push-ee: stage: push image: quay.io/podman/stable:v5.8.2@sha256:663e0dbf407987b7db3f20d3588c283a8228db17b282d2029a482d4d47e36964 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@sha256:b03690aa52bfe94054187142fba24dc54137650682810633901767d8a3e15b31 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.
Mettre en pratique
Section intitulée « Mettre en pratique »Une chaîne de publication ne se juge pas sur son fichier de workflow, mais sur ce qu'elle refuse de laisser passer. Le lab fournit les deux pipelines complets, GitHub Actions et GitLab CI, puis vérifie qu'ils tiennent leurs promesses : actions épinglées par SHA, permissions minimales au niveau du workflow, scan Trivy bloquant et signature cosign effective sur l'image produite.
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.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Idempotence et tuning : Le double passage et le contrôle de changed, la brique de test qui manque souvent au pipeline.
- requirements.yml multi-sources : Verrouiller les sources de collections, condition d'un build reproductible d'une exécution à l'autre.
- Pipeline CI matrice : Le même travail de CI transposé à une collection, avec matrice ansible-core et ansible-test.