Cosign est l'outil en ligne de commande du projet Sigstore qui
signe une image conteneur et vérifie cette signature avant de la
déployer. Il répond à une question précise : l'image @sha256:… que le cluster
s'apprête à lancer sort-elle bien de votre pipeline, sans modification ?
Cette leçon s'adresse à un DevOps qui veut signer en CI et faire refuser par
Kubernetes ce qui n'est pas signé.
Tout est mesuré le 1er octobre 2026 avec Cosign v3.1.3 : signature par clé
sur un registre local, vérification keyless de deux images publiques, et
une politique Kyverno 1.19.1 rejouée sur un cluster kind. Trois résultats
structurent la leçon : cosign verify sort en code 10 quand aucune
signature n'existe, en code 1 quand la signature vient d'une autre
identité, et Kyverno refuse un pod dont la signature ne correspond pas.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Expliquer ce qu'une signature prouve, et le rôle de Fulcio et Rekor.
- Installer Cosign en version épinglée, binaire vérifié par empreinte.
- Signer une image par digest, avec une clé puis en keyless depuis GitHub Actions.
- Vérifier une signature en contraignant l'identité du signataire, et lire ses codes de sortie.
- Bloquer à l'admission Kubernetes les images non signées par la bonne CI.
- Produire la preuve de SOCLE-REL-GEN-1, la signature des artefacts publiés, et de SOCLE-REL-VER-1, leur vérification avant usage.
Pourquoi signer vos images conteneurs ?
Section intitulée « Pourquoi signer vos images conteneurs ? »Un registre n'authentifie personne : il stocke ce qu'on lui pousse et sert ce qu'on lui demande. Tant qu'aucune signature n'accompagne l'image, le cluster ne distingue pas l'artefact produit par votre pipeline d'un artefact substitué en chemin. Les deux sous-parties posent d'abord le risque, puis ce que la signature cryptographique apporte en retour.
Le problème : registres compromis et images altérées
Section intitulée « Le problème : registres compromis et images altérées »Sans signature, rien ne prouve qu'une image order-service:2.1.0 a été
construite par votre CI, qu'elle n'a pas été modifiée après le build,
ni qu'elle correspond au code versionné. Le scénario redouté est simple : un
attaquant obtient le droit de pousser dans le registre, remplace l'image
sous le même tag, et les clusters la déploient en toute confiance. Signer par
digest et vérifier avant le déploiement ferme cette porte.
La solution : signature cryptographique
Section intitulée « La solution : signature cryptographique »Une signature Cosign établit trois propriétés, que la vérification contrôle toutes les trois. Aucune ne dit que l'image est sans vulnérabilité : une image signée peut contenir une faille, la signature prouve seulement d'où elle vient.
- Authenticité : l'image provient de l'identité déclarée, une clé ou un workflow de CI.
- Intégrité : l'image n'a pas changé depuis la signature, puisque la signature porte sur son digest.
- Traçabilité : en mode keyless, la signature est inscrite dans le journal public Rekor, consultable par tous.
L'écosystème Sigstore
Section intitulée « L'écosystème Sigstore »Cosign fait partie de Sigstore, un projet de l'OpenSSF qui fournit une infrastructure publique de signature. Trois services travaillent avec lui, et le schéma montre leurs rôles : Cosign est le client, Fulcio délivre un certificat lié à une identité OIDC, Rekor enregistre chaque signature, et le registre garde la signature à côté de l'image.
| Composant | Rôle | Guide |
|---|---|---|
| Cosign | Client qui signe et vérifie images, fichiers et attestations | Cette leçon |
| Fulcio | Autorité de certification qui émet un certificat de courte durée pour une identité OIDC | Fulcio |
| Rekor | Journal de transparence, en ajout seul, de toutes les signatures | Rekor |
| Policy Controller | Contrôleur d'admission Kubernetes du projet Sigstore | Section Kubernetes ci-dessous |
Keyless ou clés : quel mode choisir ?
Section intitulée « Keyless ou clés : quel mode choisir ? »Cosign v3 privilégie le mode keyless : pas de clé privée à garder, mais une identité vérifiable. Le mode par clé reste utile hors ligne ou quand la politique de l'entreprise impose ses propres clés. Les deux modes produisent la même chose dans le registre, une signature liée au digest.
Signature keyless (recommandée en CI)
Section intitulée « Signature keyless (recommandée en CI) »En mode keyless, Cosign génère une paire de clés éphémère, présente le jeton OIDC du job à Fulcio, qui renvoie un certificat valable dix minutes portant l'identité du workflow. La signature est inscrite dans Rekor, puis la clé privée est jetée. Le schéma suit l'échange dans l'ordre :
Ce que l'on vérifie ensuite n'est plus une clé mais une identité : le
chemin du workflow (https://github.com/<org>/<dépôt>/.github/workflows/release.yml@refs/tags/v1.0.0)
et l'émetteur du jeton (https://token.actions.githubusercontent.com).
Aucun secret n'est à stocker, et une signature volée ne sert à rien hors de
cette identité.
Signature avec clés (hors ligne, KMS)
Section intitulée « Signature avec clés (hors ligne, KMS) »Les clés restent le bon choix pour un environnement isolé sans accès à l'instance publique de Sigstore, pour une politique qui impose des clés gérées par l'organisation, ou pour s'appuyer sur un KMS existant (AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault). La clé privée devient alors un secret critique : sa fuite permet de signer n'importe quelle image.
Installer Cosign en version épinglée
Section intitulée « Installer Cosign en version épinglée »Cosign est un binaire Go unique. Comme tout outil qui entre dans la chaîne
de livraison, il s'installe en version fixée, et le binaire téléchargé se
contrôle contre l'empreinte publiée avec la release. Mesuré pour la v3.1.3 :
sha256sum --check répond cosign-linux-amd64: OK.
VERSION=v3.1.3curl -sSfLO "https://github.com/sigstore/cosign/releases/download/${VERSION}/cosign-linux-amd64"curl -sSfLO "https://github.com/sigstore/cosign/releases/download/${VERSION}/cosign_checksums.txt"sha256sum --check --ignore-missing cosign_checksums.txt# cosign-linux-amd64: OKsudo install -m 0755 cosign-linux-amd64 /usr/local/bin/cosigncosign version | grep GitVersion# GitVersion: v3.1.3L'image officielle se fige par digest
(ghcr.io/sigstore/cosign/cosign:v3.1.3@sha256:9e5c2f2edc34351160407ca3416c61855bdf9403c3c5936e0f0be7fc261611b8),
et en CI l'action sigstore/cosign-installer accepte l'entrée cosign-release
pour fixer la version installée :
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 with: cosign-release: v3.1.3Signer et vérifier avec une clé
Section intitulée « Signer et vérifier avec une clé »Le mode par clé se rejoue entièrement en local, sans compte ni réseau public,
ce qui en fait le meilleur terrain pour comprendre la vérification. Toutes
les commandes suivantes ont tourné contre un registre local registry:3
servi en HTTP, d'où l'option --allow-insecure-registry, et avec une
configuration de signature vide pour ne rien publier sur l'instance
publique de Sigstore.
Générer une paire de clés
Section intitulée « Générer une paire de clés »cosign generate-key-pair écrit cosign.key, chiffrée par une phrase de
passe, et cosign.pub. En CI, la phrase de passe se fournit par la variable
COSIGN_PASSWORD. La clé privée reste un secret à ranger dans un
gestionnaire dédié, jamais dans le dépôt.
cosign generate-key-pair# Private key written to cosign.key# Public key written to cosign.pubSigner par digest
Section intitulée « Signer par digest »Une signature porte sur un digest, jamais sur un tag. Cosign v3.1.3 accepte encore un tag, mais affiche un avertissement explicite et annonce la fin de cette possibilité :
cosign signing-config create --out signing-config.json # aucun service public
cosign sign --key cosign.key --signing-config signing-config.json \ --allow-insecure-registry -y \ localhost:5000/python:3.13-slim@sha256:afe189875f1d2f9b45e287834fb9f2c273a5d59d354ae4050ab9affbf0a6ba06# Signing artifact...# Pushing signature to: localhost:5000/python
cosign sign --key cosign.key --signing-config signing-config.json \ --allow-insecure-registry -y localhost:5000/python:3.13-slim# WARNING: Image reference localhost:5000/python:3.13-slim uses a tag, not a digest,# to identify the image to sign. [...] The ability to refer to images by tag# will be removed in a future release.Sans --signing-config, Cosign v3 utilise la configuration publique de
Sigstore et chercherait à inscrire la signature dans Rekor. L'ancienne
option --tlog-upload=false est dépréciée : mesuré, Cosign v3.1.3 la
refuse avec la configuration par défaut et demande une configuration de
signature sans journal.
Vérifier, et lire les codes de sortie
Section intitulée « Vérifier, et lire les codes de sortie »cosign verify --key cosign.pub contrôle la signature contre la clé
publique. Trois cas mesurés résument ce qu'il faut savoir pour l'intégrer à un
script : le code de sortie distingue l'absence de signature d'une
signature qui ne correspond pas.
| Situation mesurée | Code | Message |
|---|---|---|
| Image signée, bonne clé | 0 | The signatures were verified against the specified public key |
| Image jamais signée | 10 | Error: no signatures found |
| Image signée avec une autre clé | 1 | accepted signatures do not match threshold, Found: 0, Expected 1 |
cosign verify --key cosign.pub --insecure-ignore-tlog=true --allow-insecure-registry \ localhost:5000/python:3.13-slim@sha256:afe189875f1d2f9b45e287834fb9f2c273a5d59d354ae4050ab9affbf0a6ba06La sortie standard est un JSON dont le champ type vaut
https://sigstore.dev/cosign/sign/v1 en v3, et dont docker-manifest-digest
reprend le digest vérifié. L'option --insecure-ignore-tlog=true n'a de
sens ici que parce qu'aucun journal n'a été utilisé à la signature.
Où Cosign v3 range-t-il la signature ?
Section intitulée « Où Cosign v3 range-t-il la signature ? »Cosign v3 ne pousse plus une image .sig : il écrit un bundle Sigstore
(signature, certificat, preuve Rekor) dans un manifeste OCI dont le champ
subject pointe vers l'image. cosign tree le montre :
cosign tree --allow-insecure-registry \ localhost:5000/python:3.13-slim@sha256:afe189875f1d2f9b45e287834fb9f2c273a5d59d354ae4050ab9affbf0a6ba06# └── https://sigstore.dev/cosign/sign/v1 artifacts via OCI referrer: localhost:5000/python@sha256:acf10e37...Un registre qui ne sert pas l'API referrers reste compatible. Mesuré
sur GHCR : GET /v2/openvex/vexctl/referrers/<digest> répond 404, mais
l'image ghcr.io/openvex/vexctl:v0.4.4 porte un tag sha256-<digest> qui
indexe le bundle, et cosign verify la vérifie sans difficulté. L'idée,
répandue, que les signatures sur GHCR n'existeraient « que dans Rekor » est
fausse. La commande cosign triangulate, qui servait à trouver ce tag, est
dépréciée au profit de cosign tree et oras discover.
Signer en keyless depuis GitHub Actions
Section intitulée « Signer en keyless depuis GitHub Actions »En CI, la signature keyless ne demande que la permission id-token: write
sur le job, pour obtenir le jeton OIDC. Le workflow ci-dessous construit,
pousse, puis signe par digest la sortie de docker/build-push-action, et
vérifie immédiatement la signature contre l'identité exacte du workflow,
construite à partir de github.workflow_ref.
name: release
on: push: tags: ["v*"]
permissions: {}
jobs: build-sign: runs-on: ubuntu-24.04 permissions: contents: read packages: write # pousser l'image et sa signature dans GHCR id-token: write # obtenir le jeton OIDC pour Fulcio steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Construire et pousser id: build uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: push: true tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 with: cosign-release: v3.1.3
- name: Signer puis vérifier par digest env: IMAGE: ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }} IDENTITE: ${{ github.server_url }}/${{ github.workflow_ref }} run: | cosign sign --yes "${IMAGE}" cosign verify "${IMAGE}" \ --certificate-identity "${IDENTITE}" \ --certificate-oidc-issuer https://token.actions.githubusercontent.comCe workflow passe actionlint 1.7.12, zizmor 1.30.0 et poutine
1.1.6 sans aucun constat. Les valeurs issues du contexte passent par env:,
jamais directement dans le script, ce qui écarte l'injection de modèle
que zizmor signale. Une signature keyless réelle exige une identité OIDC : ce
workflow n'a pas été exécuté pour la leçon, la vérification keyless a été
mesurée sur des images publiques, ci-dessous.
Vérifier une signature keyless
Section intitulée « Vérifier une signature keyless »Une vérification keyless exige deux contraintes : l'identité du signataire
(--certificate-identity ou sa variante -regexp) et l'émetteur OIDC
(--certificate-oidc-issuer). Sans elles, Cosign refuse de vérifier, mesuré
dans la leçon sur le SBOM. Deux images publiques, signées par leur propre CI,
servent de démonstration, en lecture seule.
Une image signée par un workflow GitHub Actions
Section intitulée « Une image signée par un workflow GitHub Actions »L'image de vexctl est signée par le workflow de release du projet. La
vérification contre l'organisation openvex réussit, et une identité fausse
échoue en code 1 en affichant l'identité réellement trouvée :
cosign verify ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7e \ --certificate-identity-regexp '^https://github.com/openvex/vexctl/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com# Verification for ghcr.io/openvex/vexctl@sha256:ce15c9c8... --# - The cosign claims were validated# - Existence of the claims in the transparency log was verified offline# - The code-signing certificate was verified using trusted certificate authority certificates
cosign verify ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7e \ --certificate-identity-regexp '^https://github.com/attaquant/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com# Error: no matching attestations: failed to verify certificate identity: [...]# got "https://github.com/openvex/vexctl/.github/workflows/release.yaml@refs/tags/v0.4.4"Une identité qui n'est pas GitHub
Section intitulée « Une identité qui n'est pas GitHub »L'image de Cosign elle-même n'est pas signée par GitHub Actions mais par un compte de service Google. La vérifier avec un motif GitHub échoue ; la bonne identité et le bon émetteur réussissent. C'est la raison pour laquelle il faut relever l'identité réelle d'un éditeur avant d'écrire sa politique, plutôt que la supposer.
cosign verify ghcr.io/sigstore/cosign/cosign:v3.1.3@sha256:9e5c2f2edc34351160407ca3416c61855bdf9403c3c5936e0f0be7fc261611b8 \ --certificate-identity keyless@projectsigstore.iam.gserviceaccount.com \ --certificate-oidc-issuer https://accounts.google.com# code de sortie 0, "type": "https://sigstore.dev/cosign/sign/v1"Préférez --certificate-identity, qui attend une valeur exacte, dès que
l'identité est connue. Un motif -regexp trop large, comme .* ou une
organisation entière, accepte la signature de n'importe quel workflow qui
correspond, y compris celui d'un dépôt de test.
Attester un SBOM ou un VEX
Section intitulée « Attester un SBOM ou un VEX »Au-delà de l'image, Cosign signe des attestations : un document
(SBOM, VEX, provenance) enveloppé dans une déclaration in-toto dont le
sujet est le digest. La commande est cosign attest --type <type>, et
cosign verify-attestation --type <type> exige le même type. Les types
reconnus par Cosign v3.1.3 sont slsaprovenance, slsaprovenance02,
slsaprovenance1, link, spdx, spdxjson, cyclonedx, vuln, openvex,
custom, ou une URI. Les mesures complètes sont dans les leçons sur le
SBOM et le
VEX.
La commande cosign attach sbom, encore citée dans de nombreux exemples,
est dépréciée, comme toute la famille cosign attach, dont Cosign v3.1.3
annonce le retrait en v4.0.0.
Signer avec un KMS
Section intitulée « Signer avec un KMS »Avec un KMS (Key Management Service), la clé privée ne quitte jamais le
coffre : Cosign lui délègue l'opération de signature, et la même URI
--key sert à signer et à vérifier. Cette section n'a pas été rejouée, faute de
compte cloud dédié ; la syntaxe est celle de la documentation de Cosign.
cosign generate-key-pair --kms awskms:///alias/cosign-releasecosign sign --key awskms:///alias/cosign-release \ ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7ecosign verify --key awskms:///alias/cosign-release \ ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7eLes préfixes reconnus sont awskms://, gcpkms://, azurekms://,
hashivault:// et k8s:// pour un secret Kubernetes. Le contrôle
d'accès du KMS devient alors la vraie frontière : qui peut appeler l'opération de
signature peut signer.
Vérifier les signatures dans Kubernetes
Section intitulée « Vérifier les signatures dans Kubernetes »Signer ne protège rien tant que le cluster accepte les images non signées. Le contrôle se place à l'admission, l'étape où l'API Kubernetes accepte ou refuse un pod avant sa création. Deux implémentations dominent : Kyverno et le Policy Controller de Sigstore.
Avec Kyverno
Section intitulée « Avec Kyverno »Kyverno 1.19 vérifie les signatures avec une ressource
ImageValidatingPolicy. La politique suivante a été rejouée sur un cluster
kind jetable : l'image vexctl signée par sa CI est admise, la même image
est refusée dès que l'identité attendue ne correspond plus.
apiVersion: policies.kyverno.io/v1kind: ImageValidatingPolicymetadata: name: verify-image-signaturesspec: validationActions: [Deny] webhookConfiguration: timeoutSeconds: 15 matchConstraints: resourceRules: - apiGroups: [""] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["pods"] matchImageReferences: - glob: "ghcr.io/openvex/*" attestors: - name: cosign cosign: keyless: identities: - issuer: "https://token.actions.githubusercontent.com" subjectRegExp: "^https://github.com/openvex/" ctlog: url: "https://rekor.sigstore.dev" validations: - expression: >- images.containers.map(image, verifyImageSignatures(image, [attestors.cosign])).all(e, e > 0) message: "Image non signée par la CI de l'organisation openvex."kubectl run vexctl-signe --restart=Never \ --image ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7e# pod/vexctl-signe created
# Même image, politique attendant l'identité ^https://github.com/attaquant/# Error from server: admission webhook "ivpol.validate.kyverno.svc-fail-finegrained-verify-image-signatures"# denied the request: Policy verify-image-signatures failed: Image non signée par la CI de l'organisation openvex.Un détail décide de tout : la validation doit appeler
verifyImageSignatures(), la fonction qui déclenche la vérification. Mesuré, une politique dont la validation se réduit
à expression: "true" ne vérifie rien et bloque tout : Kyverno refuse le
pod avec « no policy performed a signature or attestation check on it », y
compris pour une image correctement signée.
Avec Sigstore Policy Controller
Section intitulée « Avec Sigstore Policy Controller »Le Policy Controller est maintenu par le projet Sigstore. Une fois
installé, il ne bloque encore rien : il faut étiqueter les namespaces à
protéger (policy.sigstore.dev/include=true) et déclarer une ressource ClusterImagePolicy qui dit quelles images
vérifier et sous quelle identité. Il n'a pas été rejoué pour cette leçon ; la version 0.10.8 est la dernière
publiée du chart au 1er octobre 2026.
helm repo add sigstore https://sigstore.github.io/helm-chartshelm install policy-controller sigstore/policy-controller \ -n cosign-system --create-namespace --version 0.10.8Sécurité : ce que la signature ne couvre pas
Section intitulée « Sécurité : ce que la signature ne couvre pas »Une signature ne vaut que ce que vaut le pipeline qui la produit : un workflow compromis émet des signatures parfaitement valides sur des images malveillantes. Les points suivants portent donc sur la chaîne autour de Cosign.
id-token: writeau niveau du job, jamais du workflow entier : un job de tests, plus exposé, n'a pas à pouvoir signer.- Actions épinglées par SHA de commit, avec la version en commentaire :
uses: actions/checkout@v4(# ❌ à éviter) laisse l'éditeur de l'action changer le code exécuté. - Vérification immédiate après la signature, dans le même job, contre l'identité exacte : une signature non vérifiée ne prouve rien.
- Identité exacte à l'admission : une politique qui accepte toute une organisation accepte aussi ses dépôts de test.
- Provenance en plus de la signature : la signature dit qui a signé, la provenance SLSA dit comment l'artefact a été construit. Signer une image ne suffit pas à atteindre un niveau SLSA Build.
Dépannage
Section intitulée « Dépannage »Les échecs de Cosign se ramènent presque toujours à trois causes : une identité qui ne correspond pas, une permission OIDC absente du job, ou une signature cherchée au mauvais endroit. Le code de sortie aide à trancher : 10 signifie qu'aucune signature n'a été trouvée, 1 qu'une signature existe mais ne correspond pas.
| Symptôme | Cause | Solution |
|---|---|---|
no signatures found, code 10 | Image non signée, ou digest différent de celui signé | Vérifier le digest avec cosign tree |
expected SAN value to match regex ..., got "...", code 1 | Identité attendue différente de l'identité réelle | Lire la valeur après got et corriger la politique |
accepted signatures do not match threshold, code 1 | Signature faite avec une autre clé | Vérifier avec la bonne cosign.pub |
--certificate-identity or --certificate-identity-regexp is required | Vérification keyless sans contrainte d'identité | Ajouter identité et émetteur |
--tlog-upload=false is not supported | Option dépréciée en v3 | Utiliser --signing-config sans journal |
| Échec d'obtention du jeton OIDC en CI | Permission id-token: write absente du job | Ajouter la permission au job qui signe |
Kyverno refuse tout avec no policy performed a signature or attestation check | Validation sans verifyImageSignatures() | Appeler la fonction dans validations |
À retenir
Section intitulée « À retenir »- Cosign signe une image par son digest et vérifie ensuite qui l'a signée ; il ne dit rien de ses vulnérabilités.
- Le mode keyless remplace la clé par une identité OIDC : Fulcio certifie, Rekor enregistre, la clé éphémère est jetée.
- Une vérification keyless exige l'identité et l'émetteur ; préférez
--certificate-identityexacte à un motif trop large. cosign verifysort en 10 sans signature et en 1 quand la signature ne correspond pas : un script peut distinguer les deux.- Cosign v3 range un bundle lié au digest, via l'API referrers ou un tag
sha256-<digest>sur les registres comme GHCR. - Installez une version ≥ 3.0.4 (avis GHSA-whqx-f9j3-ch6m), épinglée et contrôlée par empreinte.
- À l'admission, Kyverno doit appeler
verifyImageSignatures(): sans cet appel, la politique bloque tout. - La preuve attendue par SOCLE-REL-GEN-1 est la signature des artefacts publiés ; SOCLE-REL-VER-1 attend leur vérification par les consommateurs avant usage, ici à l'admission Kubernetes.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : in-toto : attestations pour la supply chain logicielle
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Les questions ci-dessous reprennent les doutes les plus courants sur Cosign : mode keyless, vérification d'identité, GHCR et Kubernetes, avec la même version épinglée que la leçon.
Ce que la signature garantit
- Authenticité : l'image provient de l'identité déclarée, une clé ou un workflow de CI.
- Intégrité : l'image n'a pas changé depuis la signature, qui porte sur son digest.
- Traçabilité : en mode keyless, la signature est inscrite dans le journal public Rekor.
Exemple mesuré (Cosign v3.1.3)
cosign verify ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7e \
--certificate-identity-regexp '^https://github.com/openvex/vexctl/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
# code de sortie 0
Keyless ou clés
| Aspect | Keyless | Avec clés |
|---|---|---|
| Secret à garder | Aucun | Clé privée ou accès KMS |
| Ce qu'on vérifie | Une identité (workflow) et un émetteur OIDC | Une clé publique |
| Durée du certificat | 10 minutes (Fulcio) | Durée de vie de la clé |
| Journal | Rekor public | Optionnel |
| Hors ligne | Non, sauf instance Sigstore privée | Oui |
Keyless
Cosign génère une clé éphémère, présente le jeton OIDC du job à Fulcio, reçoit un certificat qui porte l'identité du workflow, inscrit la signature dans Rekor, puis jette la clé privée.Avec clés
cosign generate-key-pair
cosign sign --key cosign.key <image>@sha256:<digest>
cosign verify --key cosign.pub <image>@sha256:<digest>
Utile pour un environnement isolé, une politique qui impose des clés gérées par l'organisation, ou un KMS existant.Les étapes du job
permissions:
contents: read
packages: write # pousser l'image et sa signature
id-token: write # jeton OIDC pour Fulcio
steps:
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
with:
cosign-release: v3.1.3
- name: Signer puis vérifier par digest
env:
IMAGE: ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
IDENTITE: ${{ github.server_url }}/${{ github.workflow_ref }}
run: |
cosign sign --yes "${IMAGE}"
cosign verify "${IMAGE}" \
--certificate-identity "${IDENTITE}" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
Points clés
id-token: writeau niveau du job, pas du workflow.- Signer par digest, sortie
digestdedocker/build-push-action. - Les valeurs du contexte passent par
env:, pour éviter l'injection de modèle. - Le workflow complet de la leçon passe actionlint, zizmor et poutine sans constat.
Keyless : identité et émetteur obligatoires
cosign verify ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7e \
--certificate-identity-regexp '^https://github.com/openvex/vexctl/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
Sans ces deux options, Cosign v3.1.3 refuse : « --certificate-identity or --certificate-identity-regexp is required ».Par clé
cosign verify --key cosign.pub <image>@sha256:<digest>
Codes de sortie mesurés
| Situation | Code |
|---|---|
| Signature valide | 0 |
| Aucune signature trouvée | 10 |
| Signature d'une autre identité ou d'une autre clé | 1 |
Lire une identité inattendue
En cas d'échec d'identité, le message donne la valeur réelle aprèsgot, par exemple https://github.com/openvex/vexctl/.github/workflows/release.yaml@refs/tags/v0.4.4. C'est elle qu'on reporte dans la politique, plutôt qu'un motif .* qui accepterait n'importe qui.Ce qui a été mesuré
GET /v2/openvex/vexctl/referrers/<digest>sur GHCR répond 404 : l'API referrers n'est pas servie.- L'image
ghcr.io/openvex/vexctl:v0.4.4porte pourtant un tagsha256-<digest>, un index qui pointe vers un manifeste dont l'artifactTypeestapplication/vnd.dev.sigstore.bundle.v0.3+jsonet dont lesubjectest l'image. cosign verifysur cette image sort en code 0.
Conséquence
L'idée que les signatures sur GHCR n'existeraient « que dans Rekor » est fausse. Pour voir ce qui est attaché à une image :cosign tree ghcr.io/openvex/vexctl:v0.4.4@sha256:ce15c9c85ffc66cb630c0ce9c50a85e9241be77ca4e6e841ab14a0bf2a96bf7e
cosign triangulate, qui servait à trouver ce tag, est déprécié et sera retiré en v4.0.0. L'ancienne variable COSIGN_EXPERIMENTAL n'a plus d'effet depuis Cosign v2.Attester
syft scan registry:<image>@sha256:<digest> -o cyclonedx-json > sbom.cdx.json
cosign attest --key cosign.key --type cyclonedx --predicate sbom.cdx.json \
<image>@sha256:<digest>
Vérifier
cosign verify-attestation --key cosign.pub --type cyclonedx \
<image>@sha256:<digest>
Mesuré sur un registre de test, avec les options décrites dans la leçon : la déclaration vérifiée porte le predicateType https://cyclonedx.org/bom et le digest de l'image comme sujet. Un --type spdxjson sur une image qui ne porte qu'un SBOM CycloneDX échoue avec « none of the attestations matched the predicate type ».Types reconnus par Cosign v3.1.3
slsaprovenance, slsaprovenance02, slsaprovenance1, link, spdx, spdxjson, cyclonedx, vuln, openvex, custom, ou une URI.cosign attach sbom est déprécié : utilisez l'attestation.L'avis
GHSA-whqx-f9j3-ch6m (CVE-2026-22703), sévérité modérée (CVSS 5.5), publié le 13 janvier 2026 : « Cosign verification accepts any valid Rekor entry under certain conditions ». Une vérification pouvait accepter une entrée Rekor qui ne référençait pas la bonne signature.Versions
| Branche | Touchées | Corrigée |
|---|---|---|
| v2.x | jusqu'à 2.6.1 | 2.6.2 |
| v3.x | jusqu'à 3.0.3 | 3.0.4 |
Se protéger
Installer une version corrigée, épinglée et contrôlée par empreinte :VERSION=v3.1.3
curl -sSfLO "https://github.com/sigstore/cosign/releases/download/${VERSION}/cosign-linux-amd64"
curl -sSfLO "https://github.com/sigstore/cosign/releases/download/${VERSION}/cosign_checksums.txt"
sha256sum --check --ignore-missing cosign_checksums.txt
# cosign-linux-amd64: OK
Avec Kyverno 1.19 (rejoué sur kind)
apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
name: verify-image-signatures
spec:
validationActions: [Deny]
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
matchImageReferences:
- glob: "ghcr.io/openvex/*"
attestors:
- name: cosign
cosign:
keyless:
identities:
- issuer: "https://token.actions.githubusercontent.com"
subjectRegExp: "^https://github.com/openvex/"
ctlog:
url: "https://rekor.sigstore.dev"
validations:
- expression: >-
images.containers.map(image, verifyImageSignatures(image, [attestors.cosign])).all(e, e > 0)
message: "Image non signée par la CI de l'organisation openvex."
Le piège mesuré
Une validation réduite àexpression: "true" ne vérifie rien et bloque tout : Kyverno répond « no policy performed a signature or attestation check on it », même pour une image signée.Avec Sigstore Policy Controller
helm repo add sigstore https://sigstore.github.io/helm-charts
helm install policy-controller sigstore/policy-controller \
-n cosign-system --create-namespace --version 0.10.8
Il faut ensuite étiqueter les namespaces (policy.sigstore.dev/include=true) et déclarer une ClusterImagePolicy.Principe
Avec un KMS, la clé privée reste dans le coffre : Cosign lui délègue l'opération de signature. La même URI sert à signer et à vérifier, et le contrôle d'accès du KMS devient la vraie frontière.cosign generate-key-pair --kms awskms:///alias/cosign-release
cosign sign --key awskms:///alias/cosign-release <image>@sha256:<digest>
cosign verify --key awskms:///alias/cosign-release <image>@sha256:<digest>
Préfixes reconnus
| Fournisseur | Préfixe |
|---|---|
| AWS KMS | awskms:// |
| GCP KMS | gcpkms:// |
| Azure Key Vault | azurekms:// |
| HashiCorp Vault | hashivault:// |
| Secret Kubernetes | k8s:// |
Comparaison
| Critère | Cosign (Sigstore) | Notation (Notary Project) |
|---|---|---|
| Mode principal | Keyless, identité OIDC | Certificats X.509 |
| Journal de transparence | Rekor public | Pas de journal public par défaut |
| Gestion des clés | Aucune en keyless, clé ou KMS sinon | PKI de l'organisation |
| Gouvernance | OpenSSF | CNCF |
Quand choisir Cosign
- Pipelines GitHub Actions ou GitLab CI avec jeton OIDC.
- Besoin d'une trace publique des signatures.
- Pas de PKI à exploiter.
Quand choisir Notation
- PKI interne et certificats X.509 déjà en place.
- Environnement isolé sans accès à l'instance publique de Sigstore.