Aller au contenu
Sécurité medium

Fulcio : l'autorité de certification de Sigstore

22 min de lecture

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.

  • 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

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.

La grande innovation de Fulcio est la durée de vie très courte des certificats (10 minutes). Comparé aux certificats traditionnels :

AspectCertificat traditionnelCertificat Fulcio
Durée de validité1 à 3 ans10 minutes
Gestion de la clé privéeVous devez la protéger pendant des annéesDétruite immédiatement après signature
Si la clé est voléeToutes les signatures sont compromisesFenêtre d'attaque de 10 min maximum
RévocationProcessus complexe (CRL, OCSP)Inutile, le certificat expire
Vérification à long termeLe certificat doit rester valideLa 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.

Voici ce qui se passe quand vous signez avec Cosign en mode keyless :

  1. 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é)
  2. 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.

  3. 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)
  4. 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.

  5. 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.

  6. Enregistrement dans Rekor

    La signature et le certificat sont enregistrés dans Rekor, créant une preuve immuable et horodatée.

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.

Flux de signature keyless avec Fulcio : authentification OIDC, émission du certificat éphémère, signature et enregistrement Rekor

Avant d'aller plus loin, clarifiez votre contexte d'utilisation :

ModeQuand l'utiliserCe dont vous avez besoin
Instance publiqueProjets open source, entreprises connectées à InternetRien à déployer, juste utiliser Cosign
Instance privéeEnvironnements air-gapped, confidentialité requise, contrôle totalDé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.

Un certificat Fulcio est un certificat X.509 standard enrichi d'extensions spécifiques contenant les métadonnées de l'identité OIDC.

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-hosted

L'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.

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 :

OIDNomContenu
1.3.6.1.4.1.57264.1.1OIDC IssuerURL du provider (GitHub, Google...)
1.3.6.1.4.1.57264.1.2Workflow TriggerÉvénement déclencheur (push, PR...)
1.3.6.1.4.1.57264.1.3Workflow SHACommit SHA du workflow exécuté
1.3.6.1.4.1.57264.1.4Workflow NameNom du workflow GitHub Actions
1.3.6.1.4.1.57264.1.5RepositoryDépôt source (org/repo)
1.3.6.1.4.1.57264.1.6Workflow RefBranche 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.

L'instance publique est le mode par défaut. Aucune configuration n'est nécessaire.

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.

ProviderIssuer URLUsage
GitHub Actionshttps://token.actions.githubusercontent.comCI/CD GitHub
GitLab CIhttps://gitlab.comCI/CD GitLab
Googlehttps://accounts.google.comDéveloppeurs, GCP
Microsofthttps://login.microsoftonline.com/{tenant}Azure DevOps
KubernetesService Account tokensPods K8s
.github/workflows/sign.yml
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:abc123

Le 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.

Fenêtre de terminal
# Cosign ouvre le navigateur pour l'authentification
cosign sign --yes mon-registry/mon-image:tag

Vous vous authentifiez via Google ou GitHub, et recevez un certificat avec votre email.

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.

Fenêtre de terminal
# Vérification stricte : spécifier l'identité attendue
cosign 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 :

  1. La signature est cryptographiquement valide
  2. Le certificat a été émis par Fulcio (chaîne de confiance)
  3. L'identité dans le certificat correspond exactement
  4. L'issuer OIDC est celui attendu
  5. La signature est enregistrée dans Rekor

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.

Fenêtre de terminal
# Extraire le certificat de la signature, puis le décoder
cosign 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 -noout

Les 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.

Pour les environnements déconnectés ou nécessitant confidentialité, déployez votre propre Fulcio.

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.

Architecture Sigstore privée : Dex (OIDC), Fulcio (CA) et Rekor (CT Log) communiquant avec les clients

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.

Fenêtre de terminal
# Ajouter le dépôt de charts Sigstore
helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update
# Installer Fulcio dans son namespace, version du chart épinglée
helm install fulcio sigstore/fulcio \
--version 2.11.0 \
--namespace fulcio-system --create-namespace \
--values fulcio-values.yaml

Le 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.

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.

fulcio-values.yaml
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: true

Les clients doivent pointer vers votre instance au lieu de l'instance publique :

Fenêtre de terminal
# Variables d'environnement
export COSIGN_FULCIO_URL=https://fulcio.votre-domaine.internal
export COSIGN_REKOR_URL=https://rekor.votre-domaine.internal
export 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 instance
cosign sign --yes mon-registry/mon-image:tag

Pour une configuration permanente, créez un fichier de trust root TUF.

Fulcio s'inscrit dans une hiérarchie PKI :

Hiérarchie PKI Sigstore : Root CA offline, Intermediate CA utilisée par Fulcio, certificat end-entity éphémère

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.

Fenêtre de terminal
# Initialiser le trust root Sigstore
cosign 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.json

Pour une instance privée, vous devez créer et distribuer votre propre trust root TUF.

Avant d'émettre un certificat, Fulcio effectue ces vérifications :

VérificationDescriptionAttaque bloquée
Signature du token JWTLe token est signé par le provider OIDCToken forgé
Expiration du tokenLe claim exp n'est pas dépasséReplay avec vieux token
Issuer autoriséL'issuer est dans la liste blancheProvider OIDC malveillant
Audience correcteLe token est destiné à FulcioToken volé d'une autre app
Preuve de possessionLe demandeur possède la clé privéeInterception de la requête

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.

LimiteImplicationMitigation
Confiance dans le provider OIDCSi GitHub est compromis, les certificats le sontDiversifier les providers, auditer
Pas de révocationUn certificat émis ne peut être annuléDurée de vie de 10 min limite l'impact
Identité = identité OIDCPas d'anonymat possibleUtiliser des identités de service
Dépendance réseau (public)Nécessite InternetInstance privée pour air-gapped
  1. Vérifiez toujours l'issuer OIDC

    Ne faites confiance qu'aux providers attendus. Utilisez --certificate-oidc-issuer exact, pas de regexp trop large.

  2. Utilisez des contraintes d'identité strictes

    Spécifiez --certificate-identity avec le chemin exact du workflow, pas juste le repository.

  3. Activez 2FA sur vos comptes OIDC

    La sécurité de Fulcio repose sur celle de votre provider OIDC.

  4. Surveillez les signatures dans Rekor

    Configurez des alertes sur les signatures inattendues depuis vos repositories.

ConceptRésumé
Rôle de FulcioConvertit une identité OIDC en certificat X.509
Certificats éphémères10 minutes de validité, clé détruite après signature
Instance publiqueGratuite, prête à l'emploi, pour la majorité des usages
Instance privéePour air-gapped, confidentialité, contrôle total
Extensions OIDLe certificat contient les métadonnées du workflow/repo
VérificationToujours spécifier --certificate-identity et --certificate-oidc-issuer

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn