Aller au contenu
Infrastructure as Code medium

Pipeline CI/CD pour Execution Environments : GitHub Actions, GitLab CI, Trivy, cosign

12 min de lecture

Logo Ansible

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.

  • Pipeline GitHub Actions complet pour builder + push un EE.
  • Équivalent GitLab CI avec stages.
  • Scan Trivy en mode bloquant (exit-code: 1 sur 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.
  • Avoir lu Construire un EE.
  • Compte GitHub ou GitLab.
  • Accès à un registre OCI : GHCR (gratuit avec GitHub) ou GitLab Registry (intégré).
[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.

.github/workflows/build-ee.yml
name: Build & publish Execution Environment
on:
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 }}
  • uses: pinné par SHA 40 caractères (@b4ffde65...), pas par tag — protège contre tag mutation (un attaquant qui repush une action v4.2.2 malicieuse).
  • permissions: {} au workflow — aucune permission par défaut. Permissions élevées uniquement dans le job qui en a besoin.
  • persist-credentials: false sur actions/checkout — bloque l’usage du token Git après le checkout (évite l’exfiltration via un script externe).
  • id-token: write uniquement 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.
.gitlab-ci.yml
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_IMAGE automatique ($CI_PROJECT_URL côté registry).
  • Pas de permissions: au niveau projet — la sécurité passe par les environnements protégés (environment: production).
  • cosign keyless fonctionne aussi via OIDC GitLab depuis 2024.

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 :

  1. Le pipeline CI obtient un token OIDC de GitHub/GitLab (preuve d’identité du job).
  2. Fulcio (CA Sigstore) émet un certificat éphémère (durée : 10 minutes) lié à cette identité.
  3. cosign signe l’image avec ce certificat.
  4. 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 :

Fenêtre de terminal
cosign verify ghcr.io/myorg/my-ee:abc123 \
--certificate-identity-regexp 'https://github.com/myorg/.+' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com

Vé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).

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.

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.

  • 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: false sur actions/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 + poutine lints obligatoires sur les workflows.
  • 4 étapes : build → scan Trivy → push → cosign sign.
  • GitHub Actions : permissions: {} + SHA pinning + id-token: write pour 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.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn