SOPS chiffre les valeurs d'un fichier YAML, JSON, ENV ou INI tout en laissant ses clés lisibles, ce qui permet de versionner des secrets dans Git et de relire un diff. Cette leçon s'adresse au DevOps qui doit garder des secrets dans un dépôt, typiquement en GitOps, sans les y laisser en clair. Elle couvre le chiffrement avec age, l'injection sans fichier, le travail en équipe et l'intégration en CI/CD.
Tout est mesuré sur SOPS 3.13.3 et age 1.3.2, dans un conteneur Alpine.
Trois résultats changent la façon de s'en servir. Par défaut, toutes les
valeurs et même les commentaires sont chiffrés, mais les noms de clés, les
types et la longueur des valeurs restent visibles. Retirer un membre avec
updatekeys ne change pas la data key : avec une ancienne version du
fichier tirée de Git, il déchiffre encore les nouvelles valeurs, jusqu'au
sops rotate. Et les clés age post-quantiques fonctionnent déjà.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Installer SOPS et age en vérifiant signature et empreinte
- Chiffrer et déchiffrer des fichiers YAML, JSON, ENV et binaires avec age
- Mesurer ce qu'un fichier chiffré laisse voir : clés, types, longueurs, destinataires
- Injecter des secrets avec
exec-envetexec-file, sans fichier en clair - Partager les secrets en équipe : plusieurs clés,
updatekeys,rotateet seuil de Shamir - Répondre à l'exigence SOCLE-SEC-STO-2 : aucun secret en clair dans les fichiers du projet
Qu'est-ce que SOPS ?
Section intitulée « Qu'est-ce que SOPS ? »SOPS (Secrets OPerationS) est un éditeur de fichiers chiffrés créé par Mozilla, maintenu aujourd'hui par la communauté sous getsops/sops. Il gère les formats YAML, JSON, ENV, INI et binaire, et délègue la protection de sa clé de chiffrement à un backend : age, PGP, AWS KMS, GCP KMS, Azure Key Vault ou un serveur Vault/OpenBao. La version mesurée ici est la 3.13.3, publiée le 23 juillet 2026.
Pourquoi SOPS plutôt qu'autre chose ?
Section intitulée « Pourquoi SOPS plutôt qu'autre chose ? »Le choix se joue sur un arbitrage : acceptez-vous d'exploiter un service centralisé pour vos secrets, ou préférez-vous qu'ils vivent dans le dépôt Git avec le code ? SOPS occupe la case « pas d'infrastructure » tout en gardant le multi-destinataires et des diffs où la structure reste lisible.
| Critère | SOPS + age | Ansible Vault | HashiCorp Vault / OpenBao |
|---|---|---|---|
| Stockage | Fichiers Git | Fichiers Git | Service centralisé |
| Diffs lisibles | Oui, clés en clair | Non pour un fichier entier ; encrypt_string chiffre une variable | Sans objet |
| Infrastructure | Aucune | Aucune | Serveur, haute disponibilité |
| Plusieurs destinataires | Natif, une clé par personne | Mots de passe distincts par vault ID | Politiques d'accès |
| Audit des lectures | Non (sauf journal du KMS) | Non | Oui |
La dernière ligne est la limite à retenir : SOPS protège le fichier au repos, mais ne dit pas qui l'a déchiffré. Un coffre comme OpenBao trace chaque lecture ; avec SOPS, seule une clé KMS apporte un journal (CloudTrail chez AWS).
Comment SOPS chiffre les fichiers ?
Section intitulée « Comment SOPS chiffre les fichiers ? »SOPS pratique le chiffrement d'enveloppe. Une data key propre au fichier
chiffre chaque valeur en AES-256-GCM ; cette data key est ensuite chiffrée
séparément pour chaque destinataire (une clé age, une clé KMS), et les
enveloppes sont rangées dans un bloc sops: avec un MAC qui détecte toute
modification. Le schéma suit ce double niveau, la data key en rouge au centre.
Ajouter un destinataire n'alourdit donc presque pas le fichier : on ajoute une enveloppe, pas une copie des valeurs. Et retirer un destinataire ne change rien aux valeurs chiffrées tant que la data key n'est pas renouvelée, point mesuré plus bas.
Installation
Section intitulée « Installation »SOPS est un binaire statique unique, sans service à démarrer. L'installation
se résume à récupérer ce binaire, vérifier son empreinte et le placer dans
le PATH. Il faut ensuite installer le backend choisi, age dans cette
leçon, car SOPS ne protège pas sa data key tout seul.
Choisir un backend
Section intitulée « Choisir un backend »SOPS ne fabrique pas de clé : il chiffre sa data key avec un backend, à choisir avant la première commande. La documentation officielle recommande age plutôt que PGP quand c'est possible ; les KMS cloud apportent contrôle par IAM et journal des accès.
| Backend | Usage typique | Avantage |
|---|---|---|
| age | Équipes, projets personnels | Simple, hors ligne, pas de service |
| AWS KMS | Projets AWS | Contrôle IAM, journal CloudTrail |
| GCP KMS | Projets GCP | Contrôle IAM |
| Azure Key Vault | Projets Azure | Contrôle Entra ID |
| Vault / OpenBao (Transit) | Coffre déjà en place | Chiffrement délégué au coffre |
Installer SOPS
Section intitulée « Installer SOPS »Le projet publie un binaire par plateforme, un fichier de sommes
sops-v3.13.3.checksums.txt et la signature de ce fichier, produite par
cosign avec l'identité OIDC du workflow de publication. Vérifier la
signature prouve l'origine des sommes ; vérifier la somme prouve
l'intégrité du binaire.
# Binaire, fichier de sommes et signature du fichier de sommescurl -sLO https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.linux.amd64curl -sLO https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.checksums.txtcurl -sLO https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.checksums.sigstore.json
# 1. Le fichier de sommes vient bien du dépôt getsops (cosign 3.1.1)cosign verify-blob sops-v3.13.3.checksums.txt \ --bundle sops-v3.13.3.checksums.sigstore.json \ --certificate-identity-regexp=https://github.com/getsops \ --certificate-oidc-issuer=https://token.actions.githubusercontent.com# Verified OK
# 2. Le binaire correspond à sa sommesha256sum --check --ignore-missing sops-v3.13.3.checksums.txt# sops-v3.13.3.linux.amd64: OK
sudo install -m 0755 sops-v3.13.3.linux.amd64 /usr/local/bin/sopssops --version --disable-version-check# sops 3.13.3# Image officielle Alpine, figée par digest (sans GnuPG ni outils cloud)docker run --rm -v "$PWD":/w -w /w \ ghcr.io/getsops/sops:v3.13.3-alpine@sha256:ae501277bf742f1662e0f881f43dd8fd6798b489a8058e921dbf6cda597140ea \ --version --disable-version-check# macOSbrew install sops# Windows (Scoop)scoop install sopsCes gestionnaires installent la dernière version publiée : notez-la avec
sops --version et alignez-la sur celle de la CI.
Sans option, sops --version interroge GitHub pour signaler une version
plus récente, et SOPS 3.13.3 affiche un avertissement : ce comportement par
défaut est déprécié. L'option --disable-version-check, ou la variable
SOPS_DISABLE_VERSION_CHECK=true, évite cet appel réseau, utile en CI.
Installer age
Section intitulée « Installer age »L'archive Linux d'age 1.3.2 contient age, age-keygen, age-inspect et
trois plugins de compatibilité. Elle n'est pas accompagnée d'un fichier de
sommes : l'empreinte SHA-256 de chaque artefact est affichée sur la page de
la release GitHub, et c'est elle que la commande compare.
curl -sLO https://github.com/FiloSottile/age/releases/download/v1.3.2/age-v1.3.2-linux-amd64.tar.gz
# Empreinte publiée sur la release GitHub v1.3.2echo "cbe24006683f8eb669266162894b9a522a1af52f2665fbc63a4bb032ed26ac10 age-v1.3.2-linux-amd64.tar.gz" \ | sha256sum --check# age-v1.3.2-linux-amd64.tar.gz: OK
tar xzf age-v1.3.2-linux-amd64.tar.gzsudo install -m 0755 age/age age/age-keygen /usr/local/bin/age --version# v1.3.2age post-quantique : pris en charge
Section intitulée « age post-quantique : pris en charge »age 1.3.0 a introduit des clés post-quantiques hybrides (ML-KEM-768 et
X25519), qui commencent par age1pq1. SOPS les gère depuis sa version
3.12.0 (« Age: support hybrid post-quantum identities »). Mesuré avec SOPS
3.13.3 : age-keygen -pq produit une clé publique de 1959 caractères, et le
chiffrement comme le déchiffrement fonctionnent.
Le prix est la taille : le même fichier YAML pèse 5792 octets chiffré pour une clé post-quantique, contre 1548 octets pour une clé age classique. Pour un dépôt qui doit rester lisible, réservez ces clés aux secrets dont la confidentialité doit durer des années.
Chiffrer avec age
Section intitulée « Chiffrer avec age »Le cycle tient en trois commandes : générer une paire de clés, chiffrer avec
la clé publique, déchiffrer avec la clé privée. La clé publique
age1... se partage sans risque et se commite dans .sops.yaml ; la clé privée
reste sur le poste. Aucun service n'intervient, le workflow fonctionne hors
ligne.
Créer une clé age
Section intitulée « Créer une clé age »age-keygen écrit la clé privée dans le fichier indiqué, avec les droits
0600, et affiche la clé publique. SOPS cherche ses identités dans
~/.config/sops/age/keys.txt sous Linux, d'où le chemin retenu.
-
Créer le répertoire des clés
Fenêtre de terminal mkdir -p ~/.config/sops/age -
Générer une clé
Fenêtre de terminal age-keygen -o ~/.config/sops/age/keys.txt# Public key: age1rxzcv6lwdvh0k9xyr442v70j4jpkmldchf4xpx5rqeq0elx44y3sa6thtl -
Noter la clé publique
La clé publique (
age1...) sert à chiffrer ;age-keygen -y keys.txtla réaffiche à tout moment. La clé privée reste danskeys.txt.
Chiffrer un fichier
Section intitulée « Chiffrer un fichier »sops encrypt écrit le résultat sur la sortie standard : sans
redirection, le fichier chiffré s'affiche et l'original reste en clair
sur le disque. Supprimez l'original une fois le fichier chiffré commité, et
ajoutez-le au .gitignore.
cat > secrets.yaml <<'EOF'# Base de productiondatabase: host: db.example.com port: 5432 username: admin password: super-secret-123 # à renouveler tous les 90 jours replicas: - db1.example.comapi: token: sk-1234567890abcdef enabled: trueEOF
sops encrypt --age age1rxzcv6lwdvh0k9xyr442v70j4jpkmldchf4xpx5rqeq0elx44y3sa6thtl \ secrets.yaml > secrets.enc.yamlLe résultat mesuré, abrégé : toutes les valeurs sont chiffrées, host et
port compris, et même les commentaires. Le bloc sops: porte l'enveloppe
age, le destinataire, la date et le MAC.
#ENC[AES256_GCM,data:uyZcSv8N3RJXMbKWMyL+qpFRYA==,iv:...,tag:...,type:comment]database: host: ENC[AES256_GCM,data:UlSv5JPrwd6nBmILQ+U=,iv:...,tag:...,type:str] port: ENC[AES256_GCM,data:fNHrgw==,iv:...,tag:...,type:int] username: ENC[AES256_GCM,data:lI/85LQ=,iv:...,tag:...,type:str] password: ENC[AES256_GCM,data:+31UjqiqVmVp3iNSk06O1w==,iv:...,type:str] #ENC[...,type:comment] replicas: - ENC[AES256_GCM,data:BdeCuyKGCsL6FeSOmnoD,iv:...,tag:...,type:str]api: token: ENC[AES256_GCM,data:JdaMriEP4rBaiSAWZFNWoZvMyw==,iv:...,tag:...,type:str] enabled: ENC[AES256_GCM,data:9uAflw==,iv:...,tag:...,type:bool]sops: age: - enc: | -----BEGIN AGE ENCRYPTED FILE----- ... -----END AGE ENCRYPTED FILE----- recipient: age1rxzcv6lwdvh0k9xyr442v70j4jpkmldchf4xpx5rqeq0elx44y3sa6thtl lastmodified: "2026-10-01T13:31:56Z" mac: ENC[AES256_GCM,data:...,type:str] unencrypted_suffix: _unencrypted version: 3.13.3Ce qui reste en clair dans le fichier
Section intitulée « Ce qui reste en clair dans le fichier »Un fichier SOPS n'est pas une boîte opaque, et c'est voulu : la structure reste lisible pour la revue. Mais cela signifie que le fichier révèle plusieurs informations à quiconque lit le dépôt. La mesure en relève six, à prendre en compte avant de publier un dépôt.
| Ce qui reste visible | Exemple mesuré |
|---|---|
| Noms des clés et hiérarchie | database.password, api.token |
| Type de chaque valeur | type:str, type:int, type:bool, type:comment |
| Nombre d'éléments d'une liste | replicas contient une entrée |
| Longueur de chaque valeur | 1 caractère donne data:dg==, 40 caractères donnent 40 octets chiffrés |
| Destinataires | Clés publiques age, ARN des clés KMS |
| Métadonnées | Date lastmodified, version de SOPS |
La longueur est la fuite la plus sous-estimée : AES-GCM ne remplit pas les
valeurs, et un mot de passe de 8 caractères se distingue d'une clé d'API de
40. Les noms de clés comptent aussi : stripe_live_key dit à un attaquant ce
qu'il cherche. Choisissez des noms neutres pour les dépôts publics.
Déchiffrer un fichier
Section intitulée « Déchiffrer un fichier »Aucune option de clé n'est nécessaire au déchiffrement : la liste des
destinataires est dans le bloc sops:, et SOPS essaie les identités
qu'il trouve. Il les cherche notamment, comme le montrent ses messages d'erreur,
dans SOPS_AGE_SSH_PRIVATE_KEY_FILE, ~/.ssh/id_ed25519, SOPS_AGE_KEY,
SOPS_AGE_KEY_FILE, SOPS_AGE_KEY_CMD et ~/.config/sops/age/keys.txt.
# Avec la clé à l'emplacement par défautsops decrypt secrets.enc.yaml
# Avec un fichier de clé ailleursSOPS_AGE_KEY_FILE=~/keys/ma-cle.txt sops decrypt secrets.enc.yaml
# Avec le contenu de la clé dans une variable (cas d'une CI)SOPS_AGE_KEY="$(grep AGE-SECRET-KEY ~/keys/ma-cle.txt)" sops decrypt secrets.enc.yamlSans aucune clé, la commande sort en code 128 avec
Failed to get the data key required to decrypt the SOPS file et la liste des
emplacements essayés. Avec une clé qui n'est pas destinataire, le
message devient no identity matched any of the recipients.
Éditer un fichier chiffré
Section intitulée « Éditer un fichier chiffré »sops edit est la commande du quotidien : le contenu déchiffré n'existe que
dans un fichier temporaire, supprimé à la fermeture de l'éditeur, et
SOPS rechiffre à l'enregistrement. L'éditeur se choisit avec SOPS_EDITOR,
depuis SOPS 3.10, ou à défaut avec EDITOR.
# Ouvre le fichier déchiffré dans l'éditeur, rechiffre à l'enregistrementSOPS_EDITOR=vim sops edit secrets.enc.yamlAutres formats
Section intitulée « Autres formats »SOPS détecte le format par l'extension du fichier. Le format ENV chiffre
chaque ligne CLÉ=valeur et range ses métadonnées dans des lignes sops_*.
Toute extension inconnue, .pem compris, est traitée comme binaire : le
contenu entier devient une seule valeur data dans un JSON.
cat > secrets.env <<'EOF'DB_HOST=db.example.comDB_PASSWORD=super-secret-123API_TOKEN=sk-1234567890abcdefEOFsops encrypt --age age1... secrets.env > secrets.enc.env# DB_PASSWORD=ENC[AES256_GCM,data:neYlNO1YUTm9+4rwjQoHZw==,iv:...,type:str]# sops_age__list_0__map_recipient=age1...# sops_version=3.13.3sops encrypt --age age1... config.json > config.enc.jsonsops decrypt config.enc.json# Un certificat, une clé SSH : traités comme binairesops encrypt --age age1... certificate.pem > certificate.enc.pem# {# "data": "ENC[AES256_GCM,data:FjaN6z+6od...,type:str]",# "sops": { ... }# }sops decrypt certificate.enc.pem > certificate.pemChiffrement sur place, entrée standard et fichier de sortie
Section intitulée « Chiffrement sur place, entrée standard et fichier de sortie »L'option -i (--in-place) remplace le fichier par sa version chiffrée, ce
qui suppose une copie dans Git avant de la lancer. Depuis SOPS 3.10, SOPS
lit aussi l'entrée standard, à condition de lui dire quel fichier il traite
avec --filename-override, faute de quoi il refuse.
# Chiffrer sur placesops encrypt -i --age age1... secrets.yaml
# Entrée standard : --filename-override fixe le format et la règle .sops.yamlecho '{"secret": "from-stdin"}' | sops encrypt --filename-override secret.json --age age1...# Sans l'option : Error: must specify --filename-override when reading from stdin
# Déchiffrer vers un fichiersops decrypt secrets.enc.yaml --output secrets.yamlLe fichier écrit par --output prend les droits par défaut du système :
mesuré avec un umask de 0022, il est créé en 0644, donc lisible par tous
les utilisateurs de la machine. Préférez les commandes exec-env et
exec-file, qui évitent ce fichier en clair.
Chiffrement sélectif
Section intitulée « Chiffrement sélectif »Par défaut, SOPS chiffre toutes les valeurs. Le chiffrement sélectif garde lisibles celles qui ne sont pas sensibles (nom d'hôte, port, URL publique) pour faciliter la revue. Le revers est réel : chaque valeur laissée en clair est une information de plus pour un attaquant, et une erreur d'expression régulière laisse un secret en clair sans prévenir.
encrypted_regex : ne chiffrer que certaines clés
Section intitulée « encrypted_regex : ne chiffrer que certaines clés »L'option --encrypted-regex ne chiffre que les clés dont le nom correspond
à l'expression régulière. Mesuré : host, port, username et replicas
restent en clair, et le commentaire attaché à password aussi, puisque
seule la valeur correspondante est chiffrée.
sops encrypt --age age1... \ --encrypted-regex '^(password|token)$' \ secrets.yaml > secrets.enc.yaml# Base de productiondatabase: host: db.example.com port: 5432 username: admin password: ENC[AES256_GCM,data:FzSPf3Eu2kjxJpJSjUnNkg==,iv:...,type:str] # à renouveler tous les 90 jours replicas: - db1.example.comapi: token: ENC[AES256_GCM,data:qmzPg5zuWmBv6SITwoVemljqiA==,iv:...,type:str] enabled: trueunencrypted_regex et suffixes
Section intitulée « unencrypted_regex et suffixes »L'option inverse, --unencrypted-regex, chiffre tout sauf les clés
désignées. Le filtrage par suffixe existe aussi, et SOPS en applique un par
défaut : toute clé terminée par _unencrypted reste en clair, comme le
rappelle la ligne unencrypted_suffix du bloc sops:.
# Tout chiffrer sauf host, username et portsops encrypt --age age1... --unencrypted-regex '^(host|username|port)$' secrets.yaml
# Ne chiffrer que les clés terminées par _secretsops encrypt --age age1... --encrypted-suffix '_secret' config.yaml# db_secret: ENC[AES256_GCM,...]# host: xConfigurer dans .sops.yaml
Section intitulée « Configurer dans .sops.yaml »Pour ne pas répéter ces options, déclarez-les dans .sops.yaml, à la
racine du dépôt. SOPS applique la première règle dont path_regex
correspond au chemin du fichier ; aucune règle ne correspond, et la commande
échoue avec no matching creation rules found.
creation_rules: # Production : ne chiffrer que les secrets - path_regex: prod/.* age: age1abc... encrypted_regex: '^(password|token|secret|key)$'
# Tout le reste : tout chiffrer - path_regex: .* age: age1abc...Injecter des secrets sans fichier intermédiaire
Section intitulée « Injecter des secrets sans fichier intermédiaire »sops decrypt > secrets.yaml laisse un fichier en clair sur le disque, en 0644
comme mesuré plus haut. SOPS propose deux commandes qui l'évitent :
exec-env et exec-file. Le secret n'existe alors que dans la
mémoire ou dans un tube nommé du processus lancé.
exec-env : secrets en variables d'environnement
Section intitulée « exec-env : secrets en variables d'environnement »sops exec-env déchiffre un fichier plat et lance la commande avec chaque
clé comme variable d'environnement. La commande est passée entre
apostrophes, sinon votre shell remplacerait $DB_PASSWORD avant que SOPS ne
la lance.
sops exec-env secrets.enc.env 'echo DB_PASSWORD=$DB_PASSWORD'# DB_PASSWORD=super-secret-123Un YAML imbriqué est refusé, mesuré :
cannot use complex value in environment; offending key database, avec un
code de sortie 1. Réservez exec-env aux fichiers .env ou aux YAML
plats.
exec-env --pristine : environnement propre
Section intitulée « exec-env --pristine : environnement propre »L'option --pristine lance la commande sans l'environnement du parent :
seuls les secrets déchiffrés sont transmis. Mesuré, HOME est vide ; PATH
vaut /sbin:/usr/sbin:/bin:/usr/bin, valeur que le shell lancé fixe
lui-même faute d'héritage.
sops exec-env --pristine secrets.enc.env 'echo HOME=$HOME PATH=$PATH DB_PASSWORD=$DB_PASSWORD'# HOME= PATH=/sbin:/usr/sbin:/bin:/usr/bin DB_PASSWORD=super-secret-123L'intérêt est de ne pas transmettre à la commande les variables de votre
session, comme un AWS_SECRET_ACCESS_KEY exporté plus tôt. Cela protège contre
une fuite accidentelle, pas contre un script malveillant qui lirait
d'autres sources.
exec-file : secrets dans un tube nommé
Section intitulée « exec-file : secrets dans un tube nommé »sops exec-file rend le contenu déchiffré disponible à la commande via {}.
Par défaut, mesuré, {} est un tube nommé (FIFO, droits prw-------) :
rien n'est écrit sur le disque. L'option --no-fifo le remplace par un
fichier temporaire en 0600, supprimé à la fin, pour les outils qui relisent
leur fichier.
sops exec-file secrets.enc.yaml 'ls -l {}; head -3 {}'# prw------- 1 root root 0 ... /tmp/.sops544678192/tmp-file# # Base de production# database:# host: db.example.com
# Exemple : appliquer un manifeste Kubernetes chiffrésops exec-file k8s-secrets.enc.yaml 'kubectl apply -f {}'exec-file accepte tous les formats, YAML imbriqué compris. Un tube ne se
lit qu'une fois : un outil qui ouvre deux fois son fichier de
configuration demande --no-fifo.
Modifier un fichier chiffré sans éditeur
Section intitulée « Modifier un fichier chiffré sans éditeur »Les commandes set, unset et decrypt --extract manipulent un fichier
chiffré par programme, sans éditeur. Les chemins suivent une syntaxe
JSON : '["database"]["password"]', et les valeurs aussi, d'où les
guillemets doubles autour d'une chaîne.
set : modifier une valeur
Section intitulée « set : modifier une valeur »sops set modifie le fichier sur place ; une clé absente est créée. La
valeur doit être du JSON valide : une chaîne s'écrit
'"new-password-456"', un nombre 5433.
sops set secrets.enc.yaml '["database"]["password"]' '"new-password-456"'sops decrypt --extract '["database"]["password"]' secrets.enc.yaml# new-password-456Depuis SOPS 3.11, --value-stdin et --value-file évitent que la valeur passe
dans l'historique du shell. La règle du JSON vaut aussi pour eux,
mesuré : une valeur brute est refusée avec
Value for --set is not valid JSON.
# ❌ Valeur brute : refuséeprintf 'new-password' | sops set --value-stdin secrets.enc.yaml '["database"]["password"]'# Value for --set is not valid JSON
# Valeur JSON : acceptéeprintf '"new-password"' | sops set --value-stdin secrets.enc.yaml '["database"]["password"]'unset : supprimer une clé
Section intitulée « unset : supprimer une clé »sops unset retire une clé et sa valeur, sur place. Le parent n'est pas
nettoyé : supprimer les deux clés de api laisse un objet vide, api: {},
visible dans la sortie mesurée.
sops unset secrets.enc.yaml '["api"]["token"]'sops unset secrets.enc.yaml '["api"]["enabled"]'sops decrypt secrets.enc.yaml | tail -2# - db1.example.com# api: {}extract : lire une valeur
Section intitulée « extract : lire une valeur »decrypt --extract ne renvoie que la valeur ou la sous-arborescence
demandée, ce qui convient aux scripts. Une sous-arborescence est rendue avec
ses commentaires déchiffrés.
sops decrypt --extract '["database"]["password"]' secrets.enc.yaml# super-secret-123
sops decrypt --extract '["database"]' secrets.enc.yaml# host: db.example.com# port: 5432# username: admin# # à renouveler tous les 90 jours# password: super-secret-123# ...Vérifier l'état d'un fichier
Section intitulée « Vérifier l'état d'un fichier »sops filestatus indique si un fichier est chiffré par SOPS, en JSON. C'est
la base d'un hook de pré-commit qui refuse un fichier *.enc.yaml resté
en clair, l'erreur la plus fréquente après un sops decrypt -i oublié.
sops filestatus secrets.enc.yaml# {"encrypted":true}sops filestatus secrets.yaml# {"encrypted":false}#!/usr/bin/env bash# .git/hooks/pre-commit : refuse un *.enc.yaml non chiffréset -euo pipefailwhile IFS= read -r file; do if ! sops filestatus "$file" | grep -q '"encrypted":true'; then echo "ERREUR : $file n'est pas chiffré" >&2 exit 1 fidone < <(git diff --cached --name-only --diff-filter=ACM -- '*.enc.yaml')Pour savoir qui peut déchiffrer un fichier, inutile de le déchiffrer : les
destinataires sont en clair dans le bloc sops:. Un
grep recipient secrets.enc.yaml liste les clés publiques age, et les ARN
KMS apparaissent sous la clé kms. L'option --show-all-keys qu'on trouve dans
d'anciens tutoriels n'existe pas : SOPS 3.13.3 répond
flag provided but not defined.
Travailler en équipe : plusieurs clés
Section intitulée « Travailler en équipe : plusieurs clés »Pour une équipe, plusieurs personnes doivent pouvoir déchiffrer. SOPS chiffre la data key pour chaque destinataire : chacun déchiffre avec sa propre clé privée, et chacun peut être retiré sans toucher aux autres.
Ajouter plusieurs destinataires
Section intitulée « Ajouter plusieurs destinataires »Les destinataires se séparent par une virgule, sans espace. Mesuré : le fichier contient alors deux enveloppes, et chacune des deux clés privées déchiffre seule.
sops encrypt --age age1alice...,age1bob... secrets.yaml > secrets.enc.yamlLe fichier .sops.yaml d'équipe
Section intitulée « Le fichier .sops.yaml d'équipe »Pour ne pas répéter les clés, déclarez les destinataires dans .sops.yaml
par chemin. Un key group réunit des clés alternatives : n'importe
laquelle suffit à retrouver la data key. Les comptes AWS ont 12 chiffres
dans les ARN.
creation_rules: # Production : équipe ops, ou clé KMS - path_regex: secrets/prod/.*\.yaml$ key_groups: - age: - age1abc... # Alice (ops) - age1def... # Bob (ops) kms: - arn: arn:aws:kms:eu-west-3:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab
# Développement : équipe dev - path_regex: secrets/dev/.*\.yaml$ age: age1ghi...,age1jkl...# SOPS applique la règle qui correspond au cheminsops encrypt secrets/prod/database.yaml > secrets/prod/database.enc.yamlRetirer un membre : updatekeys ne suffit pas
Section intitulée « Retirer un membre : updatekeys ne suffit pas »Quand quelqu'un quitte l'équipe, on retire sa clé de .sops.yaml, puis
sops updatekeys synchronise le fichier. La sortie mesurée montre le
retrait. Mais la data key ne change pas, et c'est tout le problème.
$ sops updatekeys -y team.enc.yamlSyncing keys for file /w/team.enc.yamlThe following changes will be made to the file's groups:Group 1 age1akfm23rhvguzrnezcw40m3kt6fyhftpgzzsu9fqxt6dhay6g7vxq7djamd--- age1m06ql5n62vepz5ycvp8w3rl5gs7trygldqg5yu6qmxrlwj4sdslstlx9y0File /w/team.enc.yaml synced with new keysAprès cette commande, Bob ne déchiffre plus le fichier à jour (code 128). Mais
les valeurs chiffrées sont identiques octet pour octet, et Bob possède
toujours l'ancienne version du fichier dans l'historique Git. Mesuré : en
recollant l'ancien bloc sops:, qui contient son enveloppe, sur les nouvelles
valeurs, Bob retrouve la data key et déchiffre le nouveau fichier.
# Ancien bloc sops: (avec l'enveloppe de Bob) + valeurs du fichier mis à jourSOPS_AGE_KEY_FILE=bob.txt sops decrypt --ignore-mac hybride.yaml | grep password# password: super-secret-123La parade est sops rotate, qui génère une nouvelle data key et rechiffre
toutes les valeurs. Mesuré : après sops rotate -i, les valeurs chiffrées
changent, et la même manipulation échoue (cipher: message authentication failed). La documentation SOPS donne l'ordre : retirer la clé, puis
renouveler la data key, puis changer les secrets eux-mêmes, que Bob a pu
lire tant qu'il était destinataire.
# 1. Retirer la clé de .sops.yaml, puis synchronisersops updatekeys -y secrets.enc.yaml# 2. Nouvelle data key, toutes les valeurs rechiffréessops rotate -i secrets.enc.yaml# 3. Changer les mots de passe et jetons chez leurs fournisseurssops rotate accepte aussi --add-age et --rm-age pour ajouter ou retirer un
destinataire et changer la data key en une seule commande.
Seuil de Shamir : exiger plusieurs équipes
Section intitulée « Seuil de Shamir : exiger plusieurs équipes »Pour les secrets critiques, SOPS découpe la data key avec le
partage de secret de Shamir : avec shamir_threshold: 2 et trois
key groups, il faut réussir à déchiffrer avec au moins deux groupes sur
trois. Aucune personne seule ne peut alors lire le fichier.
creation_rules: - path_regex: critical/.* shamir_threshold: 2 key_groups: - age: - age1abc... # Équipe ops - age: - age1def... # Équipe sécurité - age: - age1ghi... # Sauvegarde hors ligneMesuré avec une seule clé disponible, le déchiffrement échoue en code 128 :
Group 0: SUCCESSGroup 1: FAILEDGroup 2: FAILED
Recovery failed because the file was encrypted with a Shamir threshold of2, but only 1 part(s) were successfully recovered, one for each successfulkey group. In order for SOPS to recover the file, at least 2 groups have tobe successful.Avec les clés de deux groupes, le fichier se déchiffre. Le seuil protège contre une personne isolée, au prix d'une coordination à chaque lecture : réservez Shamir aux secrets racine.
Chiffrer avec AWS KMS
Section intitulée « Chiffrer avec AWS KMS »Sur un projet AWS, KMS évite de distribuer des clés privées : le droit de
déchiffrer devient une politique IAM, révocable sans toucher aux fichiers,
et chaque appel Decrypt est tracé dans CloudTrail, ce qu'age ne fournit
pas. Cette section suit la documentation SOPS et AWS ; elle n'a pas été rejouée,
faute de compte AWS dans le laboratoire.
-
Créer une clé KMS
key/... aws kms create-key --description "SOPS encryption key" \--tags TagKey=Purpose,TagValue=SOPS -
Chiffrer avec KMS
Fenêtre de terminal sops encrypt --kms arn:aws:kms:eu-west-3:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab \secrets.yaml > secrets.enc.yaml -
Déchiffrer : SOPS utilise les identifiants AWS de la session
Fenêtre de terminal sops decrypt secrets.enc.yaml
La contrepartie est la dépendance réseau : un poste hors ligne ou une région
indisponible bloque le déchiffrement. Placer une clé age hors ligne dans le
même key group que la clé KMS, comme dans le .sops.yaml d'équipe plus
haut, garde une voie de secours.
Intégrations
Section intitulée « Intégrations »SOPS n'est utile que s'il s'insère dans les outils qui consomment les secrets. Le principe reste le même : le fichier chiffré est versionné, et seule la clé privée est fournie à l'exécution, par variable d'environnement ou par rôle IAM. Quatre intégrations couvrent les cas fréquents.
SOPS et Ansible
Section intitulée « SOPS et Ansible »La collection community.sops déchiffre les fichiers SOPS dans les
playbooks. Toute tâche qui manipule la valeur porte no_log: true, sinon
Ansible l'écrit dans sa sortie en mode verbeux ou en cas d'échec.
ansible-galaxy collection install community.sopsexport SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt---- name: Déployer avec secrets SOPS hosts: all vars: secrets: "{{ lookup('community.sops.sops', 'secrets.enc.yaml') | from_yaml }}" tasks: - name: Écrire la configuration de connexion ansible.builtin.template: src: db.conf.j2 dest: /etc/app/db.conf owner: app group: app mode: "0600" no_log: trueSOPS et Kubernetes
Section intitulée « SOPS et Kubernetes »L'opérateur
sops-secrets-operator
transforme un objet SopsSecret, chiffré par SOPS, en Secret
Kubernetes. Sa version 0.21.2 sert l'API isindir.github.com/v1alpha3. La
ressource entière est chiffrée par sops encrypt, bloc sops: compris, et
l'opérateur détient la clé de déchiffrement dans le cluster.
apiVersion: isindir.github.com/v1alpha3kind: SopsSecretmetadata: name: app-secretsspec: secretTemplates: - name: db-credentials stringData: username: ENC[AES256_GCM,data:...,type:str] password: ENC[AES256_GCM,data:...,type:str]# sops:# (bloc ajouté par sops encrypt)SOPS et CI/CD
Section intitulée « SOPS et CI/CD »Un runner n'a pas de clé dans ~/.config : la clé privée arrive par la variable
SOPS_AGE_KEY, qui contient directement le texte de la clé. Les deux
exemples installent SOPS avec vérification d'empreinte, puis lancent le
déploiement avec sops exec-env, sans fichier déchiffré sur le disque.
name: deploy
on: push: branches: [main]
permissions: {}
jobs: deploy: runs-on: ubuntu-24.04 environment: production permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Installer SOPS (empreinte vérifiée) run: | curl -sLO https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.linux.amd64 echo "e5bec3346a873ae91d871550f3e698c1aad962aff462a080e40f25fde17fef6b sops-v3.13.3.linux.amd64" | sha256sum --check sudo install -m 0755 sops-v3.13.3.linux.amd64 /usr/local/bin/sops sops --version --disable-version-check
- name: Déployer avec les secrets déchiffrés en mémoire env: SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} run: sops exec-env secrets/prod.enc.env './deploy.sh'Ce workflow passe actionlint, zizmor 1.30.0 et poutine 1.1.6 sans
constat. Le secret SOPS_AGE_KEY est rangé dans l'environnement
production, que l'on protège par une règle de revue.
stages: - deploy
deploy: stage: deploy image: alpine:3.24.1@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH before_script: - apk add --no-cache curl - curl -sLO https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.linux.amd64 - echo "e5bec3346a873ae91d871550f3e698c1aad962aff462a080e40f25fde17fef6b sops-v3.13.3.linux.amd64" | sha256sum -c - install -m 0755 sops-v3.13.3.linux.amd64 /usr/local/bin/sops script: # SOPS_AGE_KEY : variable CI/CD protégée et masquée, définie dans le projet - sops exec-env secrets/prod.enc.env './deploy.sh'La variable SOPS_AGE_KEY se déclare dans les paramètres CI/CD du projet,
protégée (servie aux seules branches protégées) et masquée. Inutile de
la redéclarer dans variables: : GitLab l'injecte déjà dans le job.
La somme e5bec334... vient du fichier sops-v3.13.3.checksums.txt dont la
signature cosign a été vérifiée plus haut. L'écrire dans le pipeline, plutôt
que retélécharger le fichier de sommes, fige le binaire attendu : une
release modifiée après coup ferait échouer le job au lieu de passer.
Intégration Git diff
Section intitulée « Intégration Git diff »Git peut afficher les valeurs déchiffrées dans ses diffs grâce à un
textconv. Mesuré : après changement du port, git diff montre
- port: 5432 et + port: 5433 au lieu de deux blocs ENC[...].
-
Déclarer le filtre dans
.gitattributes*.enc.yaml diff=sopsdiffer*.enc.json diff=sopsdiffer -
Configurer Git
Fenêtre de terminal git config diff.sopsdiffer.textconv "sops decrypt" -
Vérifier :
git diffaffiche les valeurs en clair pour ces fichiers.
Ce réglage déchiffre les fichiers sur la machine qui lance le diff. Ne l'activez que sur un poste qui détient la clé privée, jamais sur un runner partagé : la revue de la structure n'a pas besoin des valeurs.
Dépannage
Section intitulée « Dépannage »La plupart des erreurs SOPS viennent de trois causes : une clé privée introuvable, un destinataire absent du fichier, ou une modification qui casse le MAC. Le tableau associe chaque message réel, relevé avec SOPS 3.13.3, à sa cause.
| Message mesuré | Cause | Solution |
|---|---|---|
Failed to get the data key required to decrypt the SOPS file | Aucune clé privée trouvée | Vérifier SOPS_AGE_KEY_FILE, SOPS_AGE_KEY ou ~/.config/sops/age/keys.txt |
no identity matched any of the recipients | Votre clé n'est pas destinataire | Se faire ajouter, puis sops updatekeys |
MAC mismatch. File has ..., computed ... | Clé ou ligne retirée à la main | Restaurer depuis Git, modifier avec sops edit ou sops set |
cipher: message authentication failed | Valeur chiffrée altérée ou déplacée | Restaurer depuis Git |
cannot use complex value in environment | YAML imbriqué avec exec-env | Fichier .env, YAML plat, ou exec-file |
no matching creation rules found | Aucune path_regex ne correspond | Corriger .sops.yaml ou le chemin du fichier |
Recovery failed because the file was encrypted with a Shamir threshold of 2 | Pas assez de key groups disponibles | Réunir les clés d'au moins shamir_threshold groupes |
Value for --set is not valid JSON | Valeur brute passée à set | Entourer la chaîne de guillemets doubles |
must specify --filename-override when reading from stdin | Entrée standard sans nom de fichier | Ajouter --filename-override |
Bonnes pratiques
Section intitulée « Bonnes pratiques »Ces règles règlent les problèmes qui apparaissent après quelques mois en équipe : le départ d'un collègue, la perte d'un poste, la divergence des conventions. Les appliquer dès le début coûte quelques minutes ; les rattraper impose de rechiffrer tous les fichiers et de changer les secrets.
- Une clé par personne : chaque membre a sa clé age, ce qui permet de retirer une personne sans toucher aux autres
- Retrait = updatekeys + rotate + nouveaux secrets : l'historique Git garde l'ancienne enveloppe
- Sauvegarder les clés privées : une clé perdue rend les fichiers irrécupérables
.sops.yamlversionné : toute l'équipe applique les mêmes règles- Noms de clés neutres dans les dépôts publics : ils restent lisibles
- KMS en production : contrôle par IAM et journal des déchiffrements, avec une clé age de secours
Structure de dépôt recommandée
Section intitulée « Structure de dépôt recommandée »Séparer dev/ et prod/ par répertoire permet d'écrire des path_regex
distincts dans .sops.yaml, donc des destinataires différents par
environnement. Le suffixe .enc.yaml uniformise le filtrage dans les hooks
Git et dans .gitattributes.
Répertoireproject/
- .sops.yaml configuration des destinataires
Répertoiresecrets/
Répertoiredev/
- database.enc.yaml
Répertoireprod/
- database.enc.yaml
- README.md instructions pour l'équipe
Ressources
Section intitulée « Ressources »Ces documentations officielles complètent les mesures de la leçon. La documentation de SOPS a quitté le README du dépôt pour getsops.io depuis la version 3.13.1 ; les notes de version signalent les changements de comportement d'une version à l'autre.
- Documentation officielle de SOPS : usage, identités, gestion des clés
- getsops/sops : code source, releases et notes de version
- age : outil de chiffrement et format de clés
- community.sops : collection Ansible
- sops-secrets-operator : opérateur Kubernetes
À retenir
Section intitulée « À retenir »- SOPS chiffre les valeurs avec une data key par fichier, elle-même chiffrée pour chaque destinataire (age, KMS).
- Par défaut, tout est chiffré, commentaires compris ; restent visibles les noms de clés, les types, la longueur des valeurs et les destinataires.
- On installe SOPS en vérifiant la signature cosign du fichier de sommes, puis la somme du binaire.
- age est recommandé par la documentation SOPS ; ses clés post-quantiques fonctionnent depuis SOPS 3.12.0, au prix de fichiers presque quatre fois plus gros.
exec-envetexec-file(tube nommé par défaut) évitent le fichier en clair que--outputcrée en 0644.updatekeysne change pas la data key : pour retirer un membre, enchaînerupdatekeys,sops rotate, puis changer les secrets.- Un seuil de Shamir exige plusieurs key groups ; une seule clé donne
Recovery failed. - La preuve attendue par SOCLE-SEC-STO-2 est une inspection montrant qu'aucun secret n'est en clair dans les fichiers du projet ; SOPS ne trace pas les lectures, ce que STO-4 demande.
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 : Politiques d'admission avec Kyverno
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications