Campagne internationale pour la sécurité informatique
Il y a quelques jours, la Maison-Blanche demande un audit sur la sécurité de ses logiciels, tout en reconnaissant l’utilisation massive (et indispensable) de l’écosystème open source pour les services de l’État.
Le sujet de l’audit est large (et très technique), et a pour objectif d’améliorer drastiquement la sécurité des logiciels. Les pistes sont bien connues, mais là, c’est écrit noir sur blanc par la Maison-Blanche.
- Sécurisation des fournisseurs de paquets (logiciels et bibliothèques)
- Mis en place d’outils pour quantifier la qualité et la sécurité du code
- Remplacement des logiciels codé avec des langages sans gestion stricte de la mémoire (memory safe).
Github fournit des outils sur ce thème depuis quelque temps, aucune surprises, mais entendre le président des États-Unis dauber sur le C/C++, c’est une première. Oui, le document de la Maison-Blanche cite le nom de Biden à tour de bras, j’imagine que c’est l’usage dans ce pays, mais hors US, on a l’impression que c’est une attaque personnelle.
Le document est plein d’intentions, on attend le résultat de l’audit pour avoir des éléments concrets, mais il contient quand même plein de liens bien plus concrets comme une roadmap sur la sécurité open source
Comme le sujet est touffu, et plein d’acronymes, le billet sera coupé en trois, en suivant le découpage de la Maison-Blanche :
- Sécuriser la chaine d’approvisionnement (ce billet)
- Quantifier la qualité du code
- Protéger l’accès à la mémoire
Dépendances logicielles
Une application est composée de code pris sur l’étagère, sous forme de source ou de paquets (gros binaire, paquet système, image Docker…) ou de code développé maison qui va utiliser des bibliothèques existantes.
Pour ne pas réinventer la roue (et la maintenir), une application va dépendre de paquets (au sens large) gérés par des tiers.
C’est un des aspects principaux de l’open source.
Le sujet est pris en main par la Linux Foundation, via l’Opensource Security Foundation, via Alpha-Omega fondé par les gros du cloud (Amazon, Google et Microsoft) pour supporter les outils essentiels de l’open source, du début à la fin (la ref ⍺ - Ω, vous l’avez?).
Les cascades de fondations et de comités, avec pour l’instant des zones floues ou redondantes, laissent présager moult réunions et visioconférences. Mais trêve de mauvaise langue, ils ont déjà produit des choses concrètes : code, données et financement de projets existants. L’adoubement de la fondation Linux devrait calmer la fascination de la dispersion et des projets qui n’aboutissent jamais.
Chaine d’approvisionnement
Gérer la dépendance à des produits tiers se joue sur la durée, il peut se passer plein de choses durant ce voyage.
Failles officielles
Découvrir une chouette faille de sécurité est un titre de gloire, surtout si ça permet de faire des choses créatives et imprévues.
Pour éviter les doublons et pour que tout le monde parle de la même chose, l’usage est d’enregistrer sa vulnérabilité auprès de CVE, Common Vulnerabilities and Exposures géré par l’organisme Mitre, soutenu par le département de la sécurité intérieure des Etats-Unis. CVE est le standard de fait, mais sa gestion est problématique, cf Curl conteste certains de ses CVEs. L’API de CVE est peu pratique, et ses données, très peu formatées, sont dures à exploiter.
L’OpenSSF propose un nouveau format, développé conjointement avec la communauté open source : l’OSV (pour Open Source Vulnerability).
Les paquets sont identifiés par un écosystème (le dépôt officiel), un nom et une version, ou directement par un commit hash.
OSV agrège de multiples sources.
OSV fournit une API REST et un cli (en go) qui sait lire la liste de dépendances gelées de différents formats ainsi que les listes de paquets installés de Alpine ou Debian, les images Docker basées sur Debian.
Tout l’outillage d’OSV est lié à GCE, avec des services pénibles comme AppEngine, ou l’utilisation de GCS (du S3, quoi) comme source. La documentation ressemble à un publireportage GCE, ce qui est dommage. D’un autre côté, proposer un schéma de table, agréger les failles, lire des listes de paquets n’est pas un challenge technique, mais surtout un travail politique pour convaincre et devenir la référence.
Paquets malhonnêtes
Au-delà des failles (involontaires), il existe aussi des paquets volontairement dangereux qui peuvent faire par exemple ce genre de choses.
Le projet package-analysis effectue l’analyse dynamique de paquet, en le lançant dans un environnement isolé ( gVisor, un conteneur paranoïaque, pour observer les fichiers qu’il tripote, les adresses où il se connecte.
Les paquets scélérats, compromis, ou avec des noms trompeurs (aka typosquatting) sont répertoriés par le projet malicious-packages pour créer des OSV.
Paquets de confiance
Il arrive qu’un paquet légitime soit corrompu durant son cycle de vie.
L’attaque peut être effectuée à différents niveaux.
- Des patchs malicieux peuvent être proposés, prétendant améliorer un point alors qu’il en fragilise un autre. Un étudiant a réussi à faire passer un patch malicieux dans le kernel Linux avant de s’en vanter. Sa fac, l’université du Minnesota, s’est fait bannir avec lui.
- En truandant l’authentification d’un mainteneur, il est possible de publier une version scélérate d’un paquet sur un dépôt.
- Via l’intégration continue, il est possible de modifier ou de publier des bêtises.
- Il est tentant de modifier le cache d’un dépôt, bien que les signatures soit censées empêcher ce genre d’attaque.
Dépôts pour bibliothèques de langage
Les langages disposent de bibliothèques de code disponibles via des dépôts spécifiques.
La sensibilisation à la sécurité pour la publication de bibliothèque a été bien trop tardive. Même si la possibilité de signer un paquet est arrivé tôt, sans prosélytisme, ça a eut du mal à prendre, et il faut une chaine de confiance, de bout en bout, pour que ce soit utilisable.
Python Pypi
Dans leur liste de diffusion, les paqueteurs Python évoquent le drame du faible taux de signature en 2016, de toute façons, seul le format binaire, les whl, gèrent les signatures.
Python a des spécifications pour l’empaquetage, Pypa, un hangar qui progresse bien, Warehouse, mais côté client, c’est très confus.
Il y a plusieurs formats pour définir les dépendances, rien de bien précis pour le verrouillage, que n’utilise pas l’outil par défaut, pip. Les challengers, poetry et unearth manquent d’ambitions, alors que uv rêve de la perfection de rust.
Plutôt que de batailler avec des clés de développeurs, Python se dirige vers la notion de diffuseur de confiance. On indique à Pypi qu’un build précis d’une CI (projet, branche…) est de confiance. La CI va publier vers Pypi l’artéfact, avec le token OIDC contenant les détails du build, si le token est valide, et si les détails correspondent à ce qui a été paramétré, Pypi accepte l’artéfact.
Même si cette approche semble simple, fonctionne pour une personne ou une CI, elle ne survit pas à la compromission du serveur Pypi, personne ne pourra savoir quels paquets ont été bidouillés.
Il faut que les paquets aient une signature auditable, qui soit faite en dehors du serveur Pypi.
Golang
Golang ne prévoit rien pour signer, il délègue toute la confiance au dépôt de source distant (Github, quoi). Par contre, Golang maintient une base de hash pour les différentes versions d’un paquet, avec un journal transparent, ce qui permet d’utiliser un miroir pour télécharger un paquet.
Les applications Golang sont livrées sous forme binaire, que vous pouvez signer de manière classique.
Ruby Gem
Ruby documente l’utilisation de certificat asymétrique avec ses gems mais avoue ne pas savoir comment gérer une chaine de confiance, et qu’ils en discutent.
Par contre, une gem peut faire confiance à une source OIDC avec l’exemple d’une action de la CI Github (et ils citent la documentation pypi pour comprendre le fonctionnement).
Même fragilité que Pypi en cas de compromission du serveur.
Node npm
Longtemps mauvais élève, Npm vante maintenant la notion de provenance avec Sigstore et propose de vérifier la signature du dépôt utilisé : npm audit signatures
.
En signant les paquets en dehors du dépôt et en utilisant un journal public, ce que permet l’utilisation de Sigstore, le dépôt npm est moins fragile que pypi et rubygem.
Les concurrents de Nodejs ne sont pas très ambitieux sur la sécurité:
- Deno se contente d’écrire les hashs des dépendances dans un fichier .
- Bun utilise le registre de npm, sans rien ajouter.
Rust crates
Pour l’instant, les paquets publiés sur crate.io ne sont pas signés, mais il y a en préparation une RFC pour intégrer Sigstore. La documentation parle d’intégration continue, mais pas de déploiement continu. Pour l’instant, crate.io authentifie les utilisateurs en OAuth2 avec Github, et permet la création de token pour publier, on a donc un lien entre un crate publié et un compte Github, mais c’est un peu peu.
Paquets Linux
La gestion de paquets des distributions Linux est mature depuis bien longtemps, et toutes gèrent les signatures (sauf Slackware).
La plupart des distributions utilisent gpg pour signer les paquets (sauf Alpine qui se distinguent avec de la clé openssl nue).
ArchLinux prend la peine d’établir un vraie rituel de signature :
- 5 clés maitres ont le pouvoir signer les clés des mainteneurs.
- 5 personnes détiennent une clé maitre ainsi que la clé de résiliation d’une autre clé maitre.
- Une clé de mainteneur est légitime pour signer un paquet si elle est signée par au moins 3 clés maitres.
Les autres distributions se contentent de fournir un trousseau avec des personnes de confiance.
Même si les distributions Linux disposent d’une PKI pour signer leurs paquets, et empaquetent une partie des bibliothèques de langage, les paquets Linux ne sont pas utilisable pour développer des applications métiers.
- Le cycle de vie d’un paquet Linux est lié à une version de la distribution, seuls les mises à jour de sécurité seront rétro-appliquées (backportées, quoi), les bugs fonctionnels resteront en l’état, et certaines distributions se permettent des patchs qui ne seront pas supportés par le mainteneur de la bibliothèque.
- Une bibliothèque emballée dans un paquet Linux sera disponible dans une unique version majeure, utilisé par tous les paquets qui en ont besoin.
- Même si les langages compilés modernes créent des binaires statiques, les paquets Linux doivent être reproductibles, et donc les bibliothèques des sources doivent être empaquetées. Cette règle empêche d’avoir des paquets officiels pour des applications agressives sur les versions de bibliothèques, comme Mongodb, Clickhouse ou même Nodejs.
- Le vendoring, largement utilisé dans le développement contemporain, en gérant de manière isolée (et redondante) les dépendances logicielles est un tabou des distributions Linux. Il empêche la rationalisation et le travail de correction des mainteneurs.
- Les formats de paquets historiques sont vieillissants, confus et la somme d’un empilement d’outils. Leur cout est négligeable par rapport à la valeur de la cohérence d’une distribution, mais hors de prix pour des applications métiers.
- Les distributions Linux récentes sont tentées par la livraison continue, rolling release, sans jamais figer de version. Cette approche favorise la fraicheur des paquets, mais ne garanti plus la constance du comportement des applications ni même la possibilité d’avoir des mises à jour espacées dans le temps. Conçu pour les postes de travail, ce genre de distribution impose des stratégies de mises à jour rigoureuses sur un serveur, comme l’immutabilité
Paquets Linux universels
Fournir des paquets Linux pour un éditeur est une tannée.
En ciblant des serveurs, il est imaginable d’imposer des distributions majeures comme Redhat, Debian ou Ubuntu, mais pour des applications grand public, il y aura trop de variations possibles. S’ajoutent à ça les soucis de dépendances à des versions précises de bibliothèques.
Pour l’utilisateur, installer une application non disponible dans sa distribution, sans forcément avoir accès aux sources n’est pas une très bonne idée d’un point de vue sécurité. Un paquet Linux classique s’installe avec les droits de super utilisateur et fait bien ce qu’il veut.
Depuis l’avènement des conteneurs, Linux sait correctement isoler des applications, même si l’intégration à un bureau ne fait pas partie des objectifs d’un Docker (même si Jessfraz a relevé le challenge).
Freedesktop a effectué un travail de titan pour normaliser les points d’entrées d’un bureau Linux, permettant une intégration élégante d’applications disparates. Il faut donc que les applications, bien que bordées, puissent profiter de Freedesktop.
Flatpack
Flatpak se positionne comme solution universelle de déploiement d’application sur Linux.
Il s’appuie sur Bubble Wrap pour l’isolation (namespace et seccomp), cgroup étant géré par systemd.
Plutôt que de bricoler un chroot obèse comme image de base, FlatPack propose des runtimes qui seront mutualisés entre les différents paquets (et mis à jour indépendamment de l’application, en s’engageant à garder stable l’API).
Le dédoublonnage, et le versionnage des fichiers sont gérés par ostree.
Une liste compréhensible de droits permet de choisir ce que peut faire une application.
Pour les mises à jour, un outil permet de le faire en ligne de commande, ou un plugin permet à Flatpack de s’incruster dans le gestionnaire de paquets de Gnome.
Snap
Snap voit plus loin que le bureau Linux (il a été conçu pour l’IOT), mais son choix d’utiliser Apparmor brime la famille de distributions Linux qui activent SELinux automatiquement : Redhat (qui pousse Flatpack, le concurrent). SELinux et Apparmor se branchent au même endroit dans le noyau Linux : ils sont exclusifs.
Les images de bases, appelées core sont basées sur Ubuntu.
Quand Ubuntu a décidé d’utiliser snap quand on installe un navigateur web avec un paquet deb, ça a un peu fait grincer des dents. Ce choix permet d’avoir les navigateurs dans leur version courante, dans différentes versions d’Ubuntu, même les très stables LTS (support à long terme). Je ne souhaite à personne de backporter les patchs de sécurités dans les bases de code tentaculaires des navigateurs webs.
Un Snap utilise SquashFS (un système de fichier compressé) comme format de paquet.
Snap se met à jour automatiquement, avec tout le confort moderne : livraison par delta, modification atomique, retour arrière possible.
Les interfaces permettent de réclamer des droits avec le système, et même d’en créer de nouveaux pour que les snaps discutent entre eux.
La documentation ne l’évoque que partiellement, mais Snap utilise des signatures à tous les étages, avec des chaines de confiances entre le publieur, le dépôt, le paquet.
Distributions Linux immuables
Les distributions Linux peuvent être considérées comme un unique paquet, immuable, avec des stratégies de mises à jour atomiques, et la possibilité de rétro-déploiement (rollback), on parle alors de distributions immuables.
Ne laissant aucun choix d’installation de paquets, il n’y a pas de multiples combinaisons à tester pour valider une nouvelle version.
Il est possible de composer une distribution immuable à partir de paquets Linux (et de profiter des patchs, des mises à jour et de la cohérence entre eux).
Ces distributions peuvent avoir des stratégies de mises à jour agressive, ayant la garantie de pouvoir revenir à un état stable en cas d’incident de mises à jour ou de régression. Le déploiement des mises à jour est différentiel (pour limiter le temps de téléchargement), et surtout signé, pour avoir un confort et une sécurité comparable à un OS classique. Démarrer sur une partition en lecture seule améliore grandement la sécurité de l’OS, il sera impossible de corrompre cette couche (les couches supérieures devront elles, faire attention).
Les OS immuables utilisent souvent un système de paquet d’applications neutre, comme FlatPack ou des conteneurs, ne fournissant que des services de bases.
Le pionnier du Linux immuable, CoreOS a été racheté par RedHat qui propose maintenant des variantes de RedHat/Fedora immuables (et forcément spécialisé) et fournit OStree comme outil de base pour créer des arborescences de fichiers immuables. Au moment du rachat, Coreos a été forké en FlatCar.
Images conteneurs
Les conteneurs, popularisés par Docker, permettent le déploiement d’applications indépendamment de l’OS hôte qui va principalement fournir un noyau Linux (et donc des ressources matérielles), et contrôler son utilisation.
Les conteneurs sont isolés les uns des autres (namespace, cgroup, seccomp…), et si un conteneur est compromis ce sera compliqué pour atteindre les autres conteneurs.
Les conteneurs permettent d’avoir le même environnement sur le poste du développeur, dans l’intégration continue et en pré-production puis production.
Les conteneurs adorent les signatures, et savent les utiliser de bout en bout.
Une image conteneur va embarquer tous les fichiers nécessaires pour lancer une application. Les arborescences de fichiers sont composés de couches, seule la plus haute permet l’écriture, permettant ainsi la mutualisation des couches (en lecture seule, donc) permettant un gain de place certain.
La plupart des images sont construites avec des paquets système, avant d’attaquer la couche applicative qui utilisera des paquets logiciels. Un conteneur ne doit faire qu’une seule chose, et donc ne devrait pas nécessiter trop de paquets.
Chaque conteneur pourra contenir ses propres versions de paquets, et un serveur pourra contenir moult conteneurs, multipliant ainsi les failles possibles et les besoins de mises à jour. Il existe de multiples outils pour découvrir les systèmes de paquets utilisés, puis de confronter leurs versions à des bases de failles, comme osv-scanner ou d’autres services payants.
Dans une vision naïve, l’image de base contiendra le contenu complet d’une installation Linux; une copie de tous les fichiers, on sait jamais. 120Mo pour une Debian 12.
Ensuite, en activant quelques options (comme ne jamais déployer la documentation) et en dégraissant la liste des paquets d’une installation de base, on arrive aux distributions dites slim. 75Mo pour une Debian 12 Slim.
Google propose de faire mieux avec Distroless, toujours avec une Debian. 2 Mo pour une version static qui n’utilise que 3 paquets, même pas de libc, juste de quoi lancer une application statique. La version base pèse 20Mo, avec une libc et openssl, 15Mo sans openssl. Des variantes spécifiques à des runtimes sont aussi fourni : python, node et java.
Au-delà de l’exploit, ce minimalisme drastique améliore la sécurité de l’image en limitant la surface d’attaque, mais surtout va limiter les faux positifs dans les analyses de paquets.
Les conteneurs sont issus des travaux de Google pour son Borg, qui a fourni au noyau Linux les fonctions de base pour isoler les processus, ce qui a permis la création de Docker qui a assumé l’évangélisation, puis de lancer Kubernetes (le Borg pour le reste du monde). Kubernetes a sorti de la boucle Docker en créant l’Open Container Initiative et en normalisant la couche juste en dessous de Docker : Containerd.
RedHat en a profité du coup de mou de Docker pour venger cet affront innovation en créant Podman qui utilise poliment les normes existantes et recrée tout l’écosystème de Docker :
- Podman est basé sur cri-o l’outil de bas niveau utilisé par Containerd
- Podman utilise les images OCI
- Podman n’utilise pas de démon lui préférant systemd
- Podman croit très fort aux conteneurs démarré sans droit d’admin (axa rootless), même si tout ne fonctionne de manière optimal pour l’instant
Notaire
Pour signer des commits de code ou des paquets, il faut utiliser un système de clé privée/public, avec une chaine de confiance permettant de diffuser les clés publiques signées.
GPG
GnuPG tente depuis longtemps de démocratiser la cryptographie asymétrique. Le concept de chaine de confiance signé entre pairs n’a jamais fonctionné comme il faut. L’écosystème est bancal, mal maintenu :
- le serveur de clé est un bricolage
- la révocation d’une clé est une tannée
- le format de clé est confus, personne ne sait quel type de clé il utilise et si la taille de la clé est suffisante.
- le vaillant mainteneur est seul, avec des micro-contributions d’autres personnes
Keybase a fait un gros travail de pédagogie en fournissant des outils agréables à utiliser. Keybase se fiche de la chaine de confiance pour crédibiliser une clé, mais propose de la lier à différente présence sur Internet, avec un système de challenge (compte de réseaux sociaux, site web personnel, porte monnaie de crypto…). Keybase a été racheté par Zoom qui avait besoin de se payer une réputation suite à différents incidents, puis laissé en l’état, en stagnation.
Debian utilise gpg et met à disposition son trousseau, pour avoir un moyen simple de vérifier une signature, avec une politique de cooptation pour les nouveaux arrivants.
Git permet de signer avec gpg un commit ou un tag, et le kernel Linux, l’utilisateur initial de git, publie aussi son trousseau.
Je pense que l’on peut en 2024 affirmer que GPG n’a pas tenu ses promesses, surtout son idée de chaine de confiance.
The Update Framework
Le CNCF (Cloud Native Computing Foundation) a proposé une norme pour la mise à jour de logiciels : TUF (The Update Framework). Les travaux initiaux sont basés sur le système de mises à jour de TOR, et ont commencé en 2009.
TUF propose un framework pour sécuriser le procédure de mise à jour de paquets, en se focalisant sur les métadatas, sans empiéter sur la gestion de paquets.
TUF utilise une autorité de certification, avec la clé privée qui est hors ligne, la clé publique est fourni avec le client. Tous les efforts sont focalisés sur diminuer les dégâts en cas de compromissions de clés, pour ça TUF utilise une délégation de confiance avec des rôles précis, et des certificats de courtes durées.
TUF a beaucoup plus à l’industrie automobile qui l’utilise via le projet uptane.
Docker a implémenté TUF avec Notary, avant de le sortir de la marque Docker. Notary utilise une très classique PKI pour signer/vérifier, mais surtout lie aux images une police de confiance, un ensemble de règles disant qui peut signer où, avec quelle autorité de certification.
Python proscranise depuis 10 ans avec sa PEP-0480 pour savoir si ils vont faire du TUF. Pourtant, l’implémentation de référence est en Python.
Sigstore
En s’appuyant sur les concepts de TUF, Sigstore propose un système de signature sans clé (keyless signing, mais le terme enthousiaste cache une clé à courte vie), pour tout l’écosystème open source, en espérant une adoption massive et une sécurité accrue, comme l’a fait Let’s Encrypt pour le TLS.
Sigstore est directement sous l’égide la Linux Foundation, sans passer par le CNCF (qui dépend de la Linux Foundation aussi).
Pour afficher son côté neutre, les clés d’autorités sont des clés matérielles, détenues par 5 experts, venus de l’industrie et de l’université, pour une durée d’à peu près 18 mois, avec renouvèlement des signatures tous les 4 mois.
Le principe de fonctionnement de Sigstore est décrit dans une publication scientifique Sigstore: Logiciel signé pour tout le monde, signé par deux devs de chez Chainguard et un chercheur de l’université de Purdue. L’un des développeurs publie des trucs sur TOR, assez à cheval sur la sécurité, les chaines de confiance et les mises à jours nickels.
Chainguard, startup de la supply chain, a été fondé par des gens qui ont bossé chez Microsoft, Google, dans l’écosystème Kubernetes, avec KNative par exemple. Il est donc logique que l’on retrouve du Github (qui appartient à Microsoft) et du Google à tous les étages de Sigstore.
Deux services isolés
Sigstore s’appuie sur deux services :
- Fulcio, la PKI qui va signer la clé temporaire utilisée pour signer le paquet, après que l’utilisateur se soit authentifié avec OpenID Connect (chez Github ou Google par exemple), en promettant d’utiliser de l’authentification à deux facteurs (OIDC ne précise pas l’utilisation de 2FA dans son token).
- Rekor, va enregistrer les étapes de la signature d’un paquet dans un journal public qui n’efface rien (reprenant la logique de publicité des autorités de certification de Certificate Transparency.
OIDC
sigstore.dev utilise Dex pour l’authentification OIDC, avec Github, Google et Azur comme cibles, ce qui permet de choisir depuis une page web sans avoir à préciser un OIDC tiers lors de la signature.
Clients
La discussion avec Sigstore se fait en REST, avec les outils de SSL. Vous pouvez reproduire la danse de Sigstore en bricolant sur un coin de table avec curl
, openssl
, jq
et base64
.
Sigstore fournit un client de référence, cosign écrit en go. Sa bibliothèque Python, sigstore-python, propose aussi un cli (capable de deviner le token OIDC dans une CI).
Github (même s’il ne fait que de l’Oauth2 et pas de l’OIDC) et Gitlab fournissent les outils pour utiliser Sigstore (vérification et signature) dans l’intégration continue, dans le but d’atteindre le niveau 3 de la certification SLSA.
La doc de Sigstore évoque même Jenkins, il n’y a donc pas d’inquiétudes sur la possibilité d’intégrer Sigstore à n’importe quelle CI.
Validation
La procédure de validation d’un paquet est tout à fait classique, avec du json, du base64 et des certificats x509.
- Validation de la clé publique :
- signature par l’autorité de certification
- émetteur crédible
- source OIDC crédible
- auteur crédible (un mail ou un build depuis une CI)
- l’horodatage signé est dans la période de validité du certificat
- Vérification que la création de la clé apparait comme il faut dans le journal Rekor
- Calcul du hash de l’artéfact et validation de sa signature avec cette clé publique éphémère
Signature
- Création d’une paire de clé publique/privée
- Création d’un certificat X509 avec des attributs confirmables par le token OIDC
- Le certificat contient un challenge (aléatoire) et la signature effectuée avec la clé privée.
- Récupération d’un token OIDC, via sa CI, ou via Fulcio qui est une application OAuth2
- Envoie du certificat éphémère public et du token OIDC à Fulsio, qui vérifie la signature du token, puis la cohérence des champs entre le certificat et le token. Fulcio raconte tout ça à Rekor, renvoie le certificat signé pour 10 minutes
- Calcul du hash de l’artéfact et signature avec la clé privée
- Création d’un horodatage signée selon le RFC3161 avec un service tiers comme freeTSA ou Sigstore Timestamp Authority
- Envoi de la signature, du hash et du certificat sur Rekor
- Envoi de l’horodatage signé sur Rekor
- Création d’un bundle, un json avec des blobs en base64 à fournir avec l’artéfact
Le certificat pourra être utilisé plusieurs fois pendant sa durée de vie. La clé privée sera détruite, sans jamais avoir touché le disque dur.
Fulcio préconise l’utilisation de gestionnaire de clés (matériel, services KMS des clouds, Vault d’Hashicorp) et en dernier recours, d’une clé privée sur un fichier.
Pour héberger un service TSA, il faut prévoir du hardware pour avoir une horloge rigoureuse, comme un GPS.
Paquets
Sigstore fournit des outils spécifiques aux gestionnaires de paquets suivant :
- cosign sait publier la signature d’une image de conteneur sur un dépôt OCI (comme Docker)
- Golang
- Python
- Rust (instable, en version 0.8 pour l’instant)
- Java
- Javascript
Il est possible de signer un commit git via Sigstore, avec gitsign, qui utilise pour ça GPG avec des documents au format x509. Oui, git sait utiliser une PKI comme tout le monde.
Pour les paquets, plus que le nom de la personne qui a signé, ou le numéro de série du build, c’est la notion de provenance qui importe.
Dans ce fouillis de cascade de normes, il ne faut non plus oublier in-toto (sour l’égide de la Linux Foundation), pour spécifier les méta-datas d’un paquet.
OpenPubKey
Un autre consortium, composé de BastionZero et de Docker, propose leur propre norme pour signer des clés publiques après une authentification OIDC : OpenPubKey.
OpenPubKey est aussi un projet de la Linux Foundation, et il est utilisé par le registre public de Docker.
Ils ont publié leur white paper expliquant leur démarche.
L’idée d’OpenPubkey est de créer une paire de clés publique/privée et un défi (un nombre aléatoire), de le signer avec la clé privé. La clé publique, le défi, la signature soit utilisé comme nonce
pour demander un jeton OIDC. Normalement, un nonce
est un nombre aléatoire utilisé lors de l’échange OAuth2, en utilisant le triplet défi-clé-signature, avec un défi de taille suffisante, ça ne remettra pas en cause l’entropie lors de l’échange.
Pour un jeton OIDC émis lors d’une CI, il n’y a pas de nonce
, et ce sera donc aud
qui sera détourné pour faire signer sa clé par OIDC.
Le jeton ne permettra pas de faire grand chose (son scope est “openid profile email”) et sera rapidement périmé. Lors de la vérification de la clé publique, le champs exp
du token ne sera pas pris en compte (il est périmé comme jeton OIDC, mais pas comme preuve), et il sera possible d’utiliser la date d’émission (iat
) comme date fiable d’émission de la preuve.
Le jeton peut être vérifié à partir de la clé publique du service OIDC, mais il y aura une rotation (entre 15 jours et 3 mois pour les gros services OIDC), et le papier évoque l’utilisation d’un journal de signature, pour avoir une conservation sur un temps long.
Sigstore cause de OpenPubKey dans son blog, reconnait la simplicité de leur approche, mais doute fort de la possibilité de vérifier une clé sur un temps long, et hurle sur l’idée de laisser trainer un token JWT n’importe où (oui, ce n’est clairement pas orthodoxe).
Mises à jour des dépendances
Il est important d’avoir une politique fluide de mises à jour de paquets, et de faire des déploiements d’un service sans nouvelles fonctionnalités/corrections, juste pour des mises à jour.
Pour la chasse aux paquets troués, tâche ô combien rébarbatives, il faut un robot et une bonne couverture de tests. Tous les gestionnaires de paquets ont une API distante et un cli pour faire ça, mais des outils comme le dependabot de Github, bien que non libre, a le bon gout de fluidifier les mises à jour, en créant une demande de fusion (pull request), qui va déclencher l’intégration continue, et donc les tests.
Il ne faut pas que cette tâche dépende de la disponibilité ou de l’humeur d’un développeur, c’est un travail de robot.
Sécuriser la chaine d’approvisionnement
Pour ce point, il faut des gestionnaires de paquets en bon état, de l’authentification forte pour les contributeurs, une PKI pour signer un peu partout, la possibilité d’auditer les signatures et de l’automatisation pour les montées en version.
Tout ça existe.