Cosign 2 en 2026 : signer et vérifier ses images de conteneurs dans un pipeline CI/CD
Guide pratique 2026 pour signer et vérifier vos images de conteneurs avec Cosign 2 : signature keyless Sigstore, attestations SBOM et SLSA, policy-controller Kubernetes, intégration GitHub Actions et GitLab CI. Retours d'expérience et corrections des erreurs courantes.
Cosign 2 est l'outil de signature du projet Sigstore qui vous permet de signer, vérifier et attester des images OCI depuis un pipeline CI/CD sans gérer de clé privée. La signature keyless s'appuie sur un jeton OIDC (GitHub Actions, GitLab, Buildkite) échangé contre un certificat éphémère émis par Fulcio et inscrit dans le journal de transparence Rekor. Honnêtement, en 2026, avec la sortie de Cosign 2.4 et l'exigence croissante de conformité au SLSA v1.1 et au Cyber Resilience Act européen, signer ses images en CI/CD n'est plus optionnel : c'est le maillon central d'une chaîne d'approvisionnement logicielle vérifiable.
Cosign 2.4 (juin 2026) impose la vérification stricte du certificat par défaut : le drapeau --insecure-ignore-tlog est désormais refusé en production.
La signature keyless via OIDC élimine la gestion de clés privées : Fulcio délivre un certificat éphémère de 10 minutes lié à l'identité de votre runner CI.
Un pipeline complet coûte moins de 8 secondes : cosign sign, cosign attest (SBOM SPDX), puis cosign verify en amont du déploiement Kubernetes.
Le policy-controller Sigstore (0.9+) refuse à l'admission toute image non signée par une identité approuvée. Un cluster peut ainsi ne faire tourner que du code signé.
Pour les entités régulées, le mode BYOK avec AWS KMS, GCP KMS ou HashiCorp Vault Transit reste supporté et audité.
Pourquoi signer ses images de conteneurs en 2026 ?
La signature d'images répond à trois menaces concrètes que j'ai rencontrées en audit chez plusieurs clients ces deux dernières années : la substitution d'image (typosquatting sur un registre public), la compromission d'un registre privé où un attaquant remplace un tag latest, et la promotion en production d'une image de développement non validée par le pipeline officiel. La signature cryptographique lie une image à une identité (humaine ou machine) et à un pipeline précis. Sans elle, le hash SHA-256 ne prouve rien d'autre que l'intégrité du transport.
Le Cyber Resilience Act (CRA) adopté par l'UE en octobre 2024 rend obligatoire, à partir de son entrée en vigueur en décembre 2027, la traçabilité vérifiable des artefacts logiciels commerciaux. Sigstore est explicitement cité par l'ENISA comme implémentation de référence dans son rapport 2025 sur les attaques de chaîne d'approvisionnement. Concrètement, si vous distribuez du logiciel en Europe en 2028, vous aurez besoin d'un mécanisme de signature auditable. Cosign coche toutes les cases : identité OIDC vérifiable, journal transparent, attestations SLSA.
Sur le plan opérationnel, la signature s'intègre aussi bien avec un scanner comme Trivy pour l'analyse de vulnérabilités et la génération de SBOM : Trivy produit le SBOM, Cosign le signe et l'attache comme attestation à l'image. La combinaison rend le SBOM aussi digne de confiance que l'image elle-même.
Installer Cosign 2 sur Linux
Cosign est distribué comme un binaire Go statique (pas de dépendances système à gérer), ce qui est un vrai plus dans les conteneurs de build minimalistes que j'utilise pour mes pipelines. Sur Debian 12 et Ubuntu 24.04, un dépôt officiel est disponible via GitHub Releases. Voici la procédure d'installation vérifiable que j'inclus systématiquement dans les images de mes runners CI :
# Installer Cosign 2.4.1 sur Linux x86_64 avec vérification du checksum
COSIGN_VERSION="2.4.1"
curl -sSLo /tmp/cosign \
"https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64"
curl -sSLo /tmp/cosign.sig \
"https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64.sig"
curl -sSLo /tmp/cosign.pem \
"https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64-keyless.pem"
# Vérifier la signature keyless du binaire lui-même (méta-vérification Sigstore)
cosign verify-blob \
--certificate /tmp/cosign.pem \
--signature /tmp/cosign.sig \
--certificate-identity-regexp "https://github.com/sigstore/cosign/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
/tmp/cosign
install -m 0755 /tmp/cosign /usr/local/bin/cosign
cosign version # → GitVersion: v2.4.1
Pour Alpine Linux et les runners musl, remplacez cosign-linux-amd64 par le binaire compilé statiquement. Sur Fedora 42 et RHEL 10, le paquet cosign est disponible dans epel-release depuis avril 2026, mais je préfère quand même l'installation par binaire pour maîtriser précisément la version dans mes pipelines et éviter les mises à jour surprise pendant un build.
Signature keyless avec Sigstore : le mode par défaut
Le mode keyless est le vrai changement de paradigme de Sigstore. Au lieu de générer une clé RSA de 4096 bits et de la stocker dans un secret CI qu'il faudra faire tourner tous les 90 jours, Cosign récupère un jeton OIDC auprès du fournisseur d'identité du runner (GitHub Actions, GitLab, Buildkite, Google Cloud Build, Kubernetes projected tokens…) et l'échange contre un certificat X.509 éphémère émis par Fulcio et valable seulement 10 minutes. Chaque signature est ensuite consignée dans Rekor, un journal Merkle append-only public.
La vérification n'utilise donc plus une clé publique, mais une identité (par exemple « le workflow .github/workflows/release.yml du dépôt acme/api »). Cette identité est bien plus expressive qu'une clé : elle vous permet d'écrire des politiques du type « n'accepte que les images signées par le pipeline de production sur la branche main ».
Notez la permission id-token: write : sans elle, GitHub Actions ne délivre pas de jeton OIDC et la signature échoue avec l'erreur cryptique fetching id token: getting token from workflow: no ACTIONS_ID_TOKEN_REQUEST_URL. C'est franchement l'erreur la plus fréquente que je vois quand quelqu'un active Cosign pour la première fois.
Comment signer une image de conteneur avec Cosign ?
La signature s'applique au digest, jamais au tag. Un tag, c'est un pointeur mutable ; un digest est le hash SHA-256 immuable du manifeste. Signer un tag reviendrait à signer un chèque en blanc que l'attaquant pourrait ré-associer à une autre image plus tard. La règle : après docker push, récupérez toujours le digest et signez-le explicitement.
# Récupérer le digest immuable après un push
IMAGE="registry.example.com/api:1.4.2"
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
# → registry.example.com/api@sha256:5c9d...
# Signature keyless (mode par défaut en 2026, Rekor + Fulcio publics)
cosign sign --yes "$DIGEST"
# Alternative : signature avec une clé locale (compatibilité legacy)
cosign generate-key-pair
cosign sign --key cosign.key --yes "$DIGEST"
# Vérifier que la signature est bien inscrite dans Rekor
cosign tree "$DIGEST"
La sortie de cosign tree affiche l'ensemble des artefacts liés à l'image dans le registre : signatures, attestations SBOM, attestations SLSA. Ces artefacts sont stockés comme des images OCI supplémentaires avec un suffixe .sig, .sbom ou .att ; c'est ce qu'on appelle la spécification de co-location OCI 1.1.
Vérifier une image signée dans un pipeline CI/CD
La vérification est symétrique à la signature, mais elle exige que vous exprimiez explicitement qui vous acceptez comme signataire. Refuser cette étape et vérifier « tout ce qui est signé » revient à faire confiance à quiconque possède un compte GitHub. Voici le pattern que j'utilise systématiquement avant un déploiement :
# Étape de vérification à placer avant helm upgrade / kubectl apply
IMAGE="registry.example.com/api@sha256:5c9d..."
cosign verify "$IMAGE" \
--certificate-identity-regexp "^https://github\.com/acme/api/\.github/workflows/release\.yml@refs/tags/v[0-9]+" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--output json | jq '.[0].optional.Bundle.Payload.integratedTime'
Deux points importants dans cette commande : le certificate-identity-regexp limite l'acceptation au workflow release.yml sur un tag versionné, donc un attaquant qui pousserait une image depuis une pull-request ou une branche de feature ne passerait pas la vérification. L'certificate-oidc-issuer évite qu'une identité GitHub soit imitée par un autre fournisseur OIDC (attaque de confusion d'émetteur).
Pour l'intégration Kubernetes, j'accompagne toujours cette étape d'une politique côté cluster avec le policy-controller, décrit plus bas. Combiné à un IDS comme celui décrit dans mon guide sur la sécurité runtime eBPF avec Falco et Tetragon, vous couvrez les deux extrémités du cycle : signature au build, détection au runtime.
Attacher un SBOM et des attestations SLSA
Une signature seule ne dit rien du contenu de l'image ni de la manière dont elle a été construite. Cosign supporte les attestations : des prédicats structurés (JSON) signés et attachés à l'image. Les deux prédicats que je pousse dans tous mes pipelines en 2026 sont le SBOM (SPDX ou CycloneDX) et le prédicat SLSA Provenance v1.0.
La beauté de cette approche : l'attestation SBOM est chiffrée avec la même identité que la signature d'image, donc un attaquant ne peut ni remplacer l'image sans casser la signature, ni substituer un faux SBOM sans casser l'attestation. Pour aller plus loin sur le pilier SLSA, j'ai détaillé la démarche complète dans mon guide de sécurisation de la chaîne d'approvisionnement Linux avec SBOM, Sigstore et SLSA.
Forcer la vérification sur Kubernetes avec le policy-controller
Signer ses images sans les vérifier au déploiement, c'est comme installer une alarme sans jamais l'armer. Le composant Sigstore policy-controller (version 0.9.1 en juillet 2026) est un ValidatingAdmissionWebhook Kubernetes qui refuse à l'admission toute image ne satisfaisant pas une politique cryptographique.
# Installer le policy-controller Sigstore (Helm)
helm repo add sigstore https://sigstore.github.io/helm-charts
helm install policy-controller sigstore/policy-controller \
--version 0.9.1 \
--namespace cosign-system \
--create-namespace
# Politique : n'accepter que les images signées par le pipeline release
cat <<'EOF' | kubectl apply -f -
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-release-signature
spec:
images:
- glob: "registry.example.com/acme/*"
authorities:
- name: github-release-workflow
keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://token.actions.githubusercontent.com
subjectRegExp: ^https://github\.com/acme/[^/]+/\.github/workflows/release\.yml@refs/tags/v[0-9.]+$
ctlog:
url: https://rekor.sigstore.dev
EOF
# Activer la politique sur un namespace
kubectl label namespace production policy.sigstore.dev/include=true
Une image non signée qui tenterait de s'exécuter dans le namespace production sera refusée avec un message clair du webhook. En pratique, je conseille de déployer d'abord en mode warn (via mode: warn dans le spec) pendant deux semaines pour tracker les images legacy, puis de basculer en enforce une fois toutes les équipes migrées.
Signer avec une clé KMS ou HSM (bring your own key)
Le keyless couvre 90 % des cas, mais dans les environnements régulés (banques, santé, défense) l'auditeur veut voir une clé racine dans un HSM certifié FIPS 140-3 ou eIDAS. Cosign supporte nativement AWS KMS, Google Cloud KMS, Azure Key Vault et HashiCorp Vault Transit sans plugin externe. Les URIs sont normalisés :
Pour les architectures 100 % Vault, mon guide de déploiement d'un cluster OpenBao avec Raft et auto-unseal détaille comment provisionner un backend Transit pour Cosign. Point important : même en mode BYOK, activez la publication Rekor (--tlog-upload=true, valeur par défaut). L'audit trail public reste utile même si votre confiance repose sur une clé privée.
Erreurs Cosign fréquentes et comment les corriger
En trois ans d'intégration de Cosign chez des clients, ces cinq erreurs représentent 80 % des tickets. Je les documente ici parce qu'elles ne sont pas dans la doc officielle et me font gagner beaucoup de temps quand je les vois passer sur un chat DevOps.
« no ACTIONS_ID_TOKEN_REQUEST_URL »
Vous avez oublié permissions: id-token: write dans votre workflow GitHub Actions. Sans cette permission, aucun jeton OIDC n'est délivré et le keyless est impossible.
« signature invalid »
Vous vérifiez un tag mutable alors que l'image a été retagée depuis. Vérifiez toujours par digest, pas par tag. Bonus : docker pull devrait aussi utiliser le digest pour garantir l'immutabilité de bout en bout.
« could not verify certificate identity »
Votre regex --certificate-identity-regexp ne colle pas à l'identité du signataire. Faites cosign verify --insecure-ignore-tlog=false une fois en mode debug pour voir l'identité réelle du certificat, puis ajustez la regex.
« rate limited by Rekor »
Depuis mars 2026, Rekor applique 200 requêtes par heure et par IP en accès anonyme. Pour un CI intensif, utilisez un compte Sigstore authentifié (limite : 5000/h) ou hébergez votre propre instance Rekor + Fulcio en interne. La doc de déploiement de Rekor privé couvre le cas.
« unexpected EOF while pulling attestation »
Certains registres (Nexus 3 < 3.68, Artifactory < 7.90) ne supportent pas encore la spécification OCI 1.1 requise pour les attestations. Mettez à jour ou utilisez un registre compatible (GHCR, Harbor 2.11+, Docker Hub, GAR, ECR).
Questions fréquentes
Cosign est-il gratuit et open source ?
Oui. Cosign est distribué sous licence Apache 2.0 par la CNCF. L'infrastructure publique Sigstore (Fulcio, Rekor, TUF) est également gratuite et hébergée par la Linux Foundation, financée par les mainteneurs Google, GitHub, Chainguard et Red Hat.
Quelle est la différence entre Cosign et Notation ?
Cosign fait partie de Sigstore et privilégie la signature keyless via OIDC. Notation (CNCF, projet séparé) suit la spécification OCI et se concentre sur les signatures par clé traditionnelle avec un modèle de confiance basé sur X.509. En 2026, Cosign domine côté développeurs (GitHub Actions, GitLab) tandis que Notation reste préféré dans les registres commerciaux (Azure, AWS Signer).
Peut-on signer une image Docker sans la pousser sur un registre ?
Pas directement. Cosign signe le manifeste OCI tel qu'il existe dans un registre, identifié par son digest. Pour signer localement, poussez d'abord vers un registre local (registry:2 en Docker Compose) ou utilisez cosign sign-blob qui signe un fichier arbitraire mais ne s'attache pas à une image OCI.
La signature Cosign expire-t-elle ?
Le certificat éphémère de Fulcio expire au bout de 10 minutes, mais l'entrée Rekor est horodatée par un journal Merkle append-only et reste vérifiable indéfiniment. La signature est donc perpétuellement valable, et c'est tout l'intérêt du journal de transparence.
Cosign fonctionne-t-il avec les registres privés on-premise ?
Oui. Cosign fonctionne avec tout registre compatible OCI 1.1 : Harbor 2.11+, GitLab Container Registry, Nexus 3.68+, Artifactory 7.90+ et registre Docker autohébergé. Pour un déploiement 100 % on-premise, vous pouvez aussi héberger votre propre Fulcio et Rekor privés.
Guide pratique pour sécuriser la chaîne d'approvisionnement logicielle sur Linux en 2026 : SBOM avec Syft, analyse de vulnérabilités avec Grype, signature avec Cosign/Sigstore, provenance SLSA et conformité CRA.