Dans l'écosystème Sigstore, Fulcio est l'autorité de certification (CA) qui rend possible la signature sans clé (keyless). Au lieu de gérer vos propres clés cryptographiques, vous prouvez votre identité via un provider OIDC (GitHub, Google, GitLab...) et Fulcio vous délivre un certificat éphémère pour signer vos artefacts.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre pourquoi un certificat de 10 minutes remplace une clé de signature à conserver des années
- Lire le contenu réel d'un certificat Fulcio et ses extensions OID propres à Sigstore
- Signer et vérifier avec l'instance publique
fulcio.sigstore.dev, depuis GitHub Actions ou depuis votre poste - Déployer une instance privée avec le chart Helm officiel pour un environnement déconnecté
- Situer Fulcio dans le modèle de menaces de la chaîne d'approvisionnement décrit dans Sigstore
Qu'est-ce que Fulcio exactement ?
Section intitulée « Qu'est-ce que Fulcio exactement ? »Fulcio est une autorité de certification (CA) spécialisée pour la signature de code. Son rôle : convertir une preuve d'identité OIDC en un certificat X.509 éphémère.
Pourquoi des certificats éphémères ?
Section intitulée « Pourquoi des certificats éphémères ? »La grande innovation de Fulcio est la durée de vie très courte des certificats (10 minutes). Comparé aux certificats traditionnels :
| Aspect | Certificat traditionnel | Certificat Fulcio |
|---|---|---|
| Durée de validité | 1 à 3 ans | 10 minutes |
| Gestion de la clé privée | Vous devez la protéger pendant des années | Détruite immédiatement après signature |
| Si la clé est volée | Toutes les signatures sont compromises | Fenêtre d'attaque de 10 min maximum |
| Révocation | Processus complexe (CRL, OCSP) | Inutile, le certificat expire |
| Vérification à long terme | Le certificat doit rester valide | La preuve Rekor suffit |
La courte durée de vie n'est pas un problème car chaque signature est enregistrée dans Rekor avec un timestamp cryptographique. On peut vérifier indéfiniment que la signature a été faite pendant la validité du certificat.
Comment fonctionne Fulcio
Section intitulée « Comment fonctionne Fulcio »Voici ce qui se passe quand vous signez avec Cosign en mode keyless :
-
Authentification OIDC
Cosign vous redirige vers un provider OIDC (GitHub, Google, etc.). Vous vous authentifiez et recevez un token JWT contenant votre identité.
Token JWT contient :- iss: "https://token.actions.githubusercontent.com" (qui a émis le token)- sub: "repo:mon-org/mon-app:ref:refs/heads/main" (sujet)- email: "workflow@github.com" (identité) -
Génération d'une paire de clés éphémère
Côté client, Cosign génère une paire de clés (publique + privée) qui ne sera utilisée qu'une seule fois.
-
Demande de certificat à Fulcio
Cosign envoie à Fulcio :
- Le token OIDC (preuve d'identité)
- La clé publique (à inclure dans le certificat)
- Une preuve de possession de la clé privée (signature du token)
-
Vérification et émission par Fulcio
Fulcio vérifie :
- Le token OIDC est valide (signature, expiration)
- L'issuer OIDC est dans sa liste autorisée
- La preuve de possession est correcte
Puis il génère un certificat X.509 contenant l'identité extraite du token OIDC.
-
Signature de l'artefact
Cosign utilise la clé privée pour signer l'artefact, puis détruit immédiatement la clé. Le certificat est attaché à la signature.
-
Enregistrement dans Rekor
La signature et le certificat sont enregistrés dans Rekor, créant une preuve immuable et horodatée.
Diagramme du flux
Section intitulée « Diagramme du flux »Le schéma ci-dessous condense les six étapes précédentes. Un point mérite votre attention : la clé privée ne quitte jamais le poste ou le runner, elle n'est transmise ni à Fulcio ni à Rekor. Fulcio ne reçoit que la clé publique et le token OIDC. C'est la propriété qui rend le modèle acceptable, un compromis de l'autorité de certification ne livre aucune clé de signature existante.
Deux façons d'utiliser Fulcio
Section intitulée « Deux façons d'utiliser Fulcio »Avant d'aller plus loin, clarifiez votre contexte d'utilisation :
| Mode | Quand l'utiliser | Ce dont vous avez besoin |
|---|---|---|
| Instance publique | Projets open source, entreprises connectées à Internet | Rien à déployer, juste utiliser Cosign |
| Instance privée | Environnements air-gapped, confidentialité requise, contrôle total | Déployer Fulcio + Rekor + (optionnel) Dex |
Sigstore opère gratuitement une instance publique accessible à tous sur
fulcio.sigstore.dev. C'est le mode par défaut quand vous utilisez Cosign.
Avantages :
- Rien à installer ni maintenir
- Haute disponibilité, sauvegardé par la communauté
- Compatible GitHub Actions, GitLab CI, Google Cloud Build...
Inconvénients :
- Nécessite une connexion Internet
- Vos signatures sont visibles publiquement dans Rekor
- Vous dépendez de l'infrastructure Sigstore
Pour qui : la majorité des utilisateurs, surtout pour les projets open source.
Vous déployez votre propre stack Sigstore (Fulcio + Rekor + optionnellement Dex comme provider OIDC).
Avantages :
- Fonctionne en environnement air-gapped (déconnecté)
- Signatures et certificats restent privés
- Contrôle total sur les policies et providers OIDC
- Conformité pour les environnements réglementés
Inconvénients :
- Infrastructure à déployer et maintenir
- Gestion de votre propre trust root (PKI)
- Plus complexe à mettre en place
Pour qui : entreprises avec des contraintes de sécurité fortes, défense, finance, ou environnements déconnectés.
Que contient un certificat Fulcio ?
Section intitulée « Que contient un certificat Fulcio ? »Un certificat Fulcio est un certificat X.509 standard enrichi d'extensions spécifiques contenant les métadonnées de l'identité OIDC.
Exemple de certificat
Section intitulée « Exemple de certificat »Deux détails surprennent à la première lecture. D'abord le champ Subject est vide : contrairement à un certificat TLS, l'identité ne s'y trouve pas, elle est portée par le Subject Alternative Name et par les extensions OID. Ensuite, pour un signataire GitHub Actions, ce SAN est de type URI et non email, il désigne le fichier de workflow et la référence Git exacte. L'extrait ci-dessous provient d'un certificat réel, celui de l'image ghcr.io/sigstore/policy-controller:v0.13.0, décodé avec openssl x509 -text -noout.
Certificate: Issuer: O = sigstore.dev, CN = sigstore-intermediate Validity Not Before: Jul 2 08:25:55 2025 GMT Not After : Jul 2 08:35:55 2025 GMT <- exactement 10 minutes Subject: X509v3 extensions: X509v3 Extended Key Usage: Code Signing X509v3 Subject Alternative Name: critical URI:https://github.com/sigstore/policy-controller/.github/workflows/release.yaml@refs/tags/v0.13.0 1.3.6.1.4.1.57264.1.1: <- OIDC Issuer https://token.actions.githubusercontent.com 1.3.6.1.4.1.57264.1.5: <- GitHub Repository sigstore/policy-controller 1.3.6.1.4.1.57264.1.6: <- GitHub Workflow Ref refs/tags/v0.13.0 1.3.6.1.4.1.57264.1.11: <- Runner Environment github-hostedL'usage étendu Code Signing est ce qui interdit de recycler ce certificat pour servir du TLS, et la criticité du SAN force tout vérificateur conforme à le prendre en compte.
Extensions OID Sigstore
Section intitulée « Extensions OID Sigstore »Fulcio utilise des OID (Object Identifiers) personnalisés pour stocker les métadonnées. Le tableau ci-dessous couvre le bloc historique 1.1 à 1.6, celui que vous rencontrerez dans la plupart des exemples en ligne :
| OID | Nom | Contenu |
|---|---|---|
1.3.6.1.4.1.57264.1.1 | OIDC Issuer | URL du provider (GitHub, Google...) |
1.3.6.1.4.1.57264.1.2 | Workflow Trigger | Événement déclencheur (push, PR...) |
1.3.6.1.4.1.57264.1.3 | Workflow SHA | Commit SHA du workflow exécuté |
1.3.6.1.4.1.57264.1.4 | Workflow Name | Nom du workflow GitHub Actions |
1.3.6.1.4.1.57264.1.5 | Repository | Dépôt source (org/repo) |
1.3.6.1.4.1.57264.1.6 | Workflow Ref | Branche ou tag (refs/heads/main) |
Ces extensions permettent une vérification très précise : vous pouvez exiger qu'une image soit signée par un workflow spécifique, depuis une branche spécifique, d'un dépôt spécifique.
Un point d'attention pour vos scripts d'audit : la documentation amont marque
ces six OID comme dépréciés. Ils restent émis par Fulcio, comme le montre
le certificat plus haut, mais les extensions à privilégier sont désormais les
OID génériques 1.3.6.1.4.1.57264.1.8 à 1.24, qui ne sont plus
spécifiques à GitHub. Les plus utiles au quotidien sont 1.8 (Issuer V2),
1.11 (Runner Environment, avec la valeur github-hosted ou self-hosted),
1.12 (Source Repository URI) et 1.14 (Source Repository Ref). La liste
complète et faisant foi est publiée dans
oid-info.md.
Utiliser l'instance publique
Section intitulée « Utiliser l'instance publique »L'instance publique est le mode par défaut. Aucune configuration n'est nécessaire.
Providers OIDC supportés
Section intitulée « Providers OIDC supportés »L'issuer URL de ce tableau est la valeur que vous retrouverez dans l'extension 1.3.6.1.4.1.57264.1.1 du certificat et que vous passerez à --certificate-oidc-issuer lors de la vérification. Retenez la distinction pratique : les providers GitHub Actions, GitLab CI et Kubernetes fournissent un token de façon automatique dans un pipeline, sans interaction ; Google et Microsoft servent surtout la signature interactive depuis un poste, avec ouverture du navigateur.
| Provider | Issuer URL | Usage |
|---|---|---|
| GitHub Actions | https://token.actions.githubusercontent.com | CI/CD GitHub |
| GitLab CI | https://gitlab.com | CI/CD GitLab |
https://accounts.google.com | Développeurs, GCP | |
| Microsoft | https://login.microsoftonline.com/{tenant} | Azure DevOps |
| Kubernetes | Service Account tokens | Pods K8s |
Exemple : signer dans GitHub Actions
Section intitulée « Exemple : signer dans GitHub Actions »name: Build and Sign
on: push: branches: [main]
jobs: sign: runs-on: ubuntu-24.04 permissions: contents: read id-token: write # ← Requis pour obtenir le token OIDC
steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Install Cosign uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3
- name: Sign image run: | cosign sign --yes ghcr.io/mon-org/mon-app@sha256:abc123Le token OIDC est automatiquement disponible. Cosign contacte fulcio.sigstore.dev,
obtient un certificat, signe l'image, et enregistre dans Rekor, tout cela en
une commande.
Exemple : signer en local (interactif)
Section intitulée « Exemple : signer en local (interactif) »# Cosign ouvre le navigateur pour l'authentificationcosign sign --yes mon-registry/mon-image:tagVous vous authentifiez via Google ou GitHub, et recevez un certificat avec votre email.
Vérifier une signature
Section intitulée « Vérifier une signature »La vérification est l'étape qui donne sa valeur à toute la chaîne : une signature qu'on ne contrôle pas ne protège de rien. Les deux drapeaux obligatoires sont --certificate-identity et --certificate-oidc-issuer. Sans eux, vous confirmez seulement qu'une signature Fulcio existe, pas qu'elle provient de l'auteur attendu ; un attaquant capable de signer avec son propre compte GitHub passerait le test. Le piège classique consiste à relâcher l'identité avec une expression trop large.
# Vérification stricte : spécifier l'identité attenduecosign verify ghcr.io/mon-org/mon-app@sha256:abc123 \ --certificate-identity="https://github.com/mon-org/mon-app/.github/workflows/build.yml@refs/heads/main" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com"Cette commande vérifie :
- La signature est cryptographiquement valide
- Le certificat a été émis par Fulcio (chaîne de confiance)
- L'identité dans le certificat correspond exactement
- L'issuer OIDC est celui attendu
- La signature est enregistrée dans Rekor
Examiner le certificat d'une signature
Section intitulée « Examiner le certificat d'une signature »Cette manipulation sert à l'investigation, pas au contrôle : on cherche à savoir qui a signé, quand et depuis quel workflow, avant de rédiger la règle de vérification stricte. Les expressions .* ci-dessous n'ont donc de sens que dans ce contexte exploratoire. Ne les recopiez jamais dans une politique d'admission, elles acceptent n'importe quel signataire.
La sortie JSON de cosign verify porte le certificat encodé dans le champ publicKey.content du corps Rekor. En le décodant, vous obtenez le PEM que vous pouvez donner à openssl pour lire les extensions montrées plus haut.
# Extraire le certificat de la signature, puis le décodercosign verify ghcr.io/sigstore/policy-controller/policy-controller:v0.13.0 \ --certificate-identity-regexp=".*" \ --certificate-oidc-issuer-regexp=".*" 2>/dev/null \ | jq -r '.[0].optional.Bundle.Payload.body' \ | base64 -d \ | jq -r '.spec.signature.publicKey.content' \ | base64 -d > cert.pem
openssl x509 -in cert.pem -text -nooutLes extensions Sigstore apparaissent en clair dans la sortie openssl, sous leur numéro OID. Si openssl refuse le fichier avec unable to load certificate, c'est presque toujours que le double décodage base64 a été interrompu par une erreur silencieuse de cosign verify : relancez la commande sans la redirection 2>/dev/null pour lire le message.
Déployer une instance privée
Section intitulée « Déployer une instance privée »Pour les environnements déconnectés ou nécessitant confidentialité, déployez votre propre Fulcio.
Architecture d'une instance privée
Section intitulée « Architecture d'une instance privée »Trois composants forment le socle minimal, et l'ordre de dépendance compte. Le provider OIDC (Dex ou un Keycloak existant) doit répondre avant Fulcio, puisque Fulcio valide chaque token contre lui. Rekor enregistre ensuite les signatures. Le schéma ci-dessous montre ces flux ; retenez que Fulcio n'a de sens qu'accompagné de Rekor, une instance Fulcio seule ne fournit aucune trace vérifiable dans le temps.
Installation avec Helm
Section intitulée « Installation avec Helm »Sigstore publie un chart Helm officiel par composant. Le dépôt et la commande d'installation sont stables ; épinglez la version du chart avec --version pour que votre déploiement soit reproductible, sans quoi Helm tire la dernière révision publiée et votre configuration peut casser à la prochaine montée de version.
# Ajouter le dépôt de charts Sigstorehelm repo add sigstore https://sigstore.github.io/helm-chartshelm repo update
# Installer Fulcio dans son namespace, version du chart épingléehelm install fulcio sigstore/fulcio \ --version 2.11.0 \ --namespace fulcio-system --create-namespace \ --values fulcio-values.yamlLe chart fulcio version 2.11.0 déploie Fulcio 1.8.8. La commande crée le namespace et applique votre fichier de valeurs. Vérifiez ensuite le démarrage avec kubectl get pods -n fulcio-system ; le pod fulcio-server doit passer à l'état Running.
Configuration de base (fulcio-values.yaml)
Section intitulée « Configuration de base (fulcio-values.yaml) »Le point le plus souvent mal recopié depuis d'anciens tutoriels est l'emplacement des providers OIDC. Dans ce chart, ils ne se déclarent pas sous server, mais dans l'objet config.contents, qui est sérialisé tel quel en fichier config.json de Fulcio. La clé OIDCIssuers associe chaque URL d'issuer à son IssuerURL, son ClientID et son Type. Les valeurs ci-dessous sont volontairement minimales et à adapter à votre domaine.
config: contents: OIDCIssuers: # Provider interne Dex "https://dex.votre-domaine.internal": IssuerURL: "https://dex.votre-domaine.internal" ClientID: "sigstore" Type: "email"
server: # Autorité de certification : fileca lit sa clé et son certificat # depuis un Secret. Voir le bloc createcerts plus bas. args: certificateAuthority: fileca
# Exposition HTTP via Ingress ingress: http: enabled: true className: nginx hosts: - host: fulcio.votre-domaine.internal path: / tls: - secretName: fulcio-tls hosts: - fulcio.votre-domaine.internal
# Journal de transparence des certificats (Certificate Transparency)ctlog: enabled: true
# Dev / test uniquement : ce Job génère une CA fileca autosignée.# En production, désactivez-le et fournissez votre propre Secret# fulcio-server-secret (clé privée + certificat de CA).createcerts: enabled: trueConfigurer les clients pour l'instance privée
Section intitulée « Configurer les clients pour l'instance privée »Les clients doivent pointer vers votre instance au lieu de l'instance publique :
# Variables d'environnementexport COSIGN_FULCIO_URL=https://fulcio.votre-domaine.internalexport COSIGN_REKOR_URL=https://rekor.votre-domaine.internalexport COSIGN_OIDC_ISSUER=https://dex.votre-domaine.internal
# Trust root personnalisé (si vous avez votre propre CA)export SIGSTORE_ROOT_FILE=/path/to/your/root.json
# Signer avec votre instancecosign sign --yes mon-registry/mon-image:tagPour une configuration permanente, créez un fichier de trust root TUF.
Chaîne de confiance et Trust Root
Section intitulée « Chaîne de confiance et Trust Root »Architecture PKI de Sigstore
Section intitulée « Architecture PKI de Sigstore »Fulcio s'inscrit dans une hiérarchie PKI :
Distribution via TUF
Section intitulée « Distribution via TUF »Les métadonnées de confiance (clés publiques, certificats racine) sont distribuées via TUF (The Update Framework), un protocole sécurisé contre les attaques de rollback et de compromission partielle.
# Initialiser le trust root Sigstorecosign initialize
# Les métadonnées sont téléchargées sous ~/.sigstore/root/ls ~/.sigstore/root/tuf-repo-cdn.sigstore.dev/# root.json snapshot.json targets targets.json timestamp.jsonPour une instance privée, vous devez créer et distribuer votre propre trust root TUF.
Sécurité de Fulcio
Section intitulée « Sécurité de Fulcio »Ce que Fulcio vérifie
Section intitulée « Ce que Fulcio vérifie »Avant d'émettre un certificat, Fulcio effectue ces vérifications :
| Vérification | Description | Attaque bloquée |
|---|---|---|
| Signature du token JWT | Le token est signé par le provider OIDC | Token forgé |
| Expiration du token | Le claim exp n'est pas dépassé | Replay avec vieux token |
| Issuer autorisé | L'issuer est dans la liste blanche | Provider OIDC malveillant |
| Audience correcte | Le token est destiné à Fulcio | Token volé d'une autre app |
| Preuve de possession | Le demandeur possède la clé privée | Interception de la requête |
Limites et considérations
Section intitulée « Limites et considérations »Fulcio déplace le problème de confiance plutôt qu'il ne le supprime, et il faut en avoir conscience avant de l'adopter. La ligne la plus importante du tableau est la première : la sécurité de votre chaîne de signature ne dépasse jamais celle de votre provider OIDC. Si le compte GitHub d'un mainteneur est piraté, l'attaquant obtient des certificats Fulcio parfaitement valides. L'absence de révocation n'est pas un défaut mais un choix assumé, compensé par la durée de vie de dix minutes et par l'horodatage Rekor.
| Limite | Implication | Mitigation |
|---|---|---|
| Confiance dans le provider OIDC | Si GitHub est compromis, les certificats le sont | Diversifier les providers, auditer |
| Pas de révocation | Un certificat émis ne peut être annulé | Durée de vie de 10 min limite l'impact |
| Identité = identité OIDC | Pas d'anonymat possible | Utiliser des identités de service |
| Dépendance réseau (public) | Nécessite Internet | Instance privée pour air-gapped |
Bonnes pratiques
Section intitulée « Bonnes pratiques »-
Vérifiez toujours l'issuer OIDC
Ne faites confiance qu'aux providers attendus. Utilisez
--certificate-oidc-issuerexact, pas de regexp trop large. -
Utilisez des contraintes d'identité strictes
Spécifiez
--certificate-identityavec le chemin exact du workflow, pas juste le repository. -
Activez 2FA sur vos comptes OIDC
La sécurité de Fulcio repose sur celle de votre provider OIDC.
-
Surveillez les signatures dans Rekor
Configurez des alertes sur les signatures inattendues depuis vos repositories.
À retenir
Section intitulée « À retenir »| Concept | Résumé |
|---|---|
| Rôle de Fulcio | Convertit une identité OIDC en certificat X.509 |
| Certificats éphémères | 10 minutes de validité, clé détruite après signature |
| Instance publique | Gratuite, prête à l'emploi, pour la majorité des usages |
| Instance privée | Pour air-gapped, confidentialité, contrôle total |
| Extensions OID | Le certificat contient les métadonnées du workflow/repo |
| Vérification | Toujours spécifier --certificate-identity et --certificate-oidc-issuer |