Aller au contenu
Sécurité medium

SOPS : chiffrer vos secrets dans Git

27 min de lecture

Vous devez stocker des credentials dans votre dépôt Git, mais les commiter en clair est impensable ? SOPS résout ce problème en chiffrant uniquement les valeurs de vos fichiers YAML, JSON ou ENV, tout en gardant les clés lisibles. Résultat : vos secrets sont protégés, mais vous pouvez toujours faire des diffs et des reviews sur la structure du fichier.

Contrairement à Ansible Vault qui chiffre le fichier entier (rendant les diffs impossibles), ou à HashiCorp Vault qui nécessite une infrastructure dédiée, SOPS offre un compromis pragmatique : chiffrement fort + workflow Git natif.

  • Installation : installer SOPS et age sur Linux, macOS et Windows
  • Chiffrement : chiffrer des fichiers YAML, JSON, ENV, INI et binaire
  • Chiffrement sélectif : ne chiffrer que certaines clés avec encrypted_regex
  • Injection de secrets : utiliser exec-env et exec-file pour injecter des secrets sans fichier intermédiaire
  • Manipulation : modifier (set/unset), extraire (extract), faire tourner les clés (rotate)
  • Multi-clés : configurer plusieurs clés pour une équipe et les key groups Shamir
  • Intégrations : Ansible, Kubernetes, CI/CD durcis, intégration Git diff

SOPS (Secrets OPerationS) est un éditeur de fichiers chiffrés créé par Mozilla (maintenant maintenu par la communauté sous getsops/sops). Il supporte les formats YAML, JSON, ENV, INI et binaire.

La version actuelle est la v3.12.1 (mars 2026).

CritèreSOPS + ageAnsible VaultHashiCorp Vault
StockageFichiers GitFichiers GitService centralisé
Diffs lisibles✅ Oui (clés visibles)❌ Non (blob chiffré)N/A
InfrastructureAucuneAucuneServeur + HA
Multi-clés✅ Natif❌ 1 mot de passe✅ Policies avancées
Cloud KMSAWS/GCP/Azure✅ Transit

SOPS ne chiffre que les valeurs, pas les clés. Voici un exemple :

database:
host: db.example.com
username: admin
password: super-secret-123

Avantage : vous pouvez reviewer les changements de structure (nouvelles clés, suppressions) sans avoir accès aux secrets.

SOPS nécessite un backend de chiffrement. Les options sont :

BackendUsage recommandéAvantage
ageÉquipes, projets personnelsSimple, pas de service
AWS KMSProjets AWSIAM intégré
GCP KMSProjets GCPIAM intégré
Azure Key VaultProjets AzureAzure AD intégré
HashiCorp VaultEntrepriseTransit encryption
Fenêtre de terminal
# Télécharger le binaire (v3.12.1)
curl -sLO https://github.com/getsops/sops/releases/download/v3.12.1/sops-v3.12.1.linux.amd64
chmod +x sops-v3.12.1.linux.amd64
sudo mv sops-v3.12.1.linux.amd64 /usr/local/bin/sops
# Vérifier
sops --version
# sops 3.12.1 (latest)
Fenêtre de terminal
# Télécharger age v1.3.0 (avec support post-quantum)
curl -sL https://github.com/FiloSottile/age/releases/download/v1.3.0/age-v1.3.0-linux-amd64.tar.gz | tar xz
sudo mv age/age age/age-keygen /usr/local/bin/
rm -rf age
# Vérifier
age --version
# v1.3.0
  1. Créer le répertoire pour les clés

    Fenêtre de terminal
    mkdir -p ~/.config/sops/age
  2. Générer une clé

    Fenêtre de terminal
    age-keygen -o ~/.config/sops/age/keys.txt
    # Sortie :
    # Public key: age1jk54cwqnaw2utp7uwk6nvehr6rsfwrwulhf8kk9lxcw9qy92cf0qfu6nam
  3. Sauvegarder la clé publique

    Notez la clé publique (age1...), vous en aurez besoin pour chiffrer. La clé privée est dans keys.txt.

Fenêtre de terminal
# Créer un fichier de test
cat > secrets.yaml << 'EOF'
database:
host: db.example.com
username: admin
password: super-secret-123
api:
token: sk-1234567890abcdef
EOF
# Chiffrer avec votre clé publique age
sops encrypt --age age1jk54cwqnaw2utp7uwk6nvehr6rsfwrwulhf8kk9lxcw9qy92cf0qfu6nam \
secrets.yaml > secrets.enc.yaml
# Vérifier le résultat
cat secrets.enc.yaml
Fenêtre de terminal
# SOPS cherche automatiquement dans ~/.config/sops/age/keys.txt
sops decrypt secrets.enc.yaml
# Ou spécifier le fichier de clé
SOPS_AGE_KEY_FILE=~/keys/ma-cle.txt sops decrypt secrets.enc.yaml
Fenêtre de terminal
# Ouvre le fichier déchiffré dans $EDITOR, re-chiffre à la sauvegarde
sops edit secrets.enc.yaml

SOPS gère nativement les formats JSON, ENV, INI et binaire. Le format est détecté automatiquement par l’extension du fichier.

Fenêtre de terminal
# Chiffrer un fichier JSON
sops encrypt --age age1... config.json > config.enc.json
# Déchiffrer
sops decrypt config.enc.json

L’option -i (ou --in-place) modifie le fichier directement, sans créer de second fichier. C’est pratique pour les scripts d’automatisation.

Fenêtre de terminal
# Chiffrer sur place
sops encrypt -i --age age1... secrets.yaml
# Déchiffrer sur place
sops decrypt -i secrets.enc.yaml

Depuis SOPS 3.10, vous pouvez chiffrer et déchiffrer via des pipes. C’est utile pour intégrer SOPS dans des scripts sans fichier intermédiaire.

Fenêtre de terminal
# Chiffrer depuis stdin (JSON)
echo '{"secret": "from-stdin"}' | \
sops encrypt --input-type json --output-type json \
--age age1... /dev/stdin
# Écrire le résultat dans un fichier
sops decrypt secrets.enc.yaml --output secrets.yaml

Par défaut, SOPS chiffre toutes les valeurs d’un fichier. Mais souvent, certaines valeurs ne sont pas sensibles (noms d’hôtes, URLs publiques). Le chiffrement sélectif garde ces valeurs lisibles pour faciliter les reviews Git.

L’option --encrypted-regex permet de ne chiffrer que les clés qui correspondent à une expression régulière.

Fenêtre de terminal
# Ne chiffrer que les clés "password" et "token"
sops encrypt --age age1... \
--encrypted-regex '^(password|token)$' \
secrets.yaml > secrets.enc.yaml

Résultat : host et username restent en clair, seuls password et token sont chiffrés.

database:
host: db.example.com # En clair
username: admin # En clair
password: ENC[AES256_GCM,data:...,type:str] # Chiffré
api:
token: ENC[AES256_GCM,data:...,type:str] # Chiffré

L’inverse de encrypted_regex : tout est chiffré sauf les clés correspondant au pattern.

Fenêtre de terminal
# Tout chiffrer sauf host et username
sops encrypt --age age1... \
--unencrypted-regex '^(host|username)$' \
secrets.yaml > secrets.enc.yaml

Vous pouvez aussi filtrer par suffixe de clé. C’est utile quand vos conventions de nommage distinguent déjà les données sensibles.

Fenêtre de terminal
# Ne chiffrer que les clés qui se terminent par "_secret"
sops encrypt --age age1... \
--encrypted-suffix '_secret' \
config.yaml > config.enc.yaml

Pour éviter de passer ces options à chaque commande, déclarez-les dans .sops.yaml :

creation_rules:
# Prod : ne chiffrer que les secrets
- path_regex: 'prod/.*'
age: age1abc...
encrypted_regex: '^(password|token|secret|key)$'
# Dev : tout chiffrer
- path_regex: '.*'
age: age1abc...

L’un des risques de sops decrypt > secrets.yaml est que le fichier déchiffré reste sur disque. SOPS propose deux commandes pour éviter ce problème : exec-env et exec-file.

exec-env : secrets comme variables d’environnement

Section intitulée « exec-env : secrets comme variables d’environnement »

sops exec-env déchiffre un fichier et injecte chaque clé/valeur comme variable d’environnement pour la commande spécifiée. Les secrets ne transitent jamais par un fichier.

Fenêtre de terminal
# Injecter les secrets comme variables d'environnement
sops exec-env secrets.enc.env 'echo DB_PASSWORD=$DB_PASSWORD'
# DB_PASSWORD=super-secret-123

Le flag --pristine lance la commande dans un environnement vierge, contenant uniquement les secrets déchiffrés. Aucune variable de l’environnement parent n’est héritée ($HOME, $PATH, etc. sont absents).

Fenêtre de terminal
# Environnement propre : seuls les secrets sont disponibles
sops exec-env --pristine secrets.enc.env \
'echo HOME=$HOME DB_PASSWORD=$DB_PASSWORD'
# HOME= DB_PASSWORD=super-secret-123

C’est utile pour éviter les fuites de contexte : un script malveillant ne peut pas lire $AWS_ACCESS_KEY_ID ou d’autres variables de votre session.

sops exec-file déchiffre le fichier dans un fichier temporaire (ou un FIFO nommé) et le passe à la commande via {}. Le fichier est supprimé automatiquement après exécution.

Fenêtre de terminal
# Le fichier déchiffré est accessible via {}
sops exec-file secrets.enc.yaml 'cat {}'
# database:
# host: db.example.com
# username: admin
# password: super-secret-123
# Exemple concret : passer à kubectl
sops exec-file k8s-secrets.enc.yaml \
'kubectl apply -f {}'

exec-file fonctionne avec tous les formats, y compris le YAML imbriqué (contrairement à exec-env).

Les commandes set, unset et extract permettent de manipuler un fichier chiffré par programme, sans ouvrir d’éditeur interactif.

Fenêtre de terminal
# Modifier le mot de passe dans le fichier chiffré
sops set secrets.enc.yaml '["database"]["password"]' '"new-password-456"'
# Vérifier
sops decrypt --extract '["database"]["password"]' secrets.enc.yaml
# new-password-456

La syntaxe de chemin utilise le format JSON path : ["clé"]["sous-clé"]. La valeur doit être entre guillemets doubles (c’est du JSON).

Fenêtre de terminal
# Supprimer la clé api.token
sops unset secrets.enc.yaml '["api"]["token"]'
# Vérifier : la clé n'existe plus
sops decrypt secrets.enc.yaml
# database:
# host: db.example.com
# username: admin
# password: super-secret-123
# api: {}

extract déchiffre et retourne uniquement la valeur demandée, sans le reste du fichier. C’est idéal pour les scripts.

Fenêtre de terminal
# Extraire une seule valeur
sops decrypt --extract '["database"]["password"]' secrets.enc.yaml
# super-secret-123
# Extraire une sous-arborescence complète
sops decrypt --extract '["database"]' secrets.enc.yaml
# host: db.example.com
# username: admin
# password: super-secret-123

La commande filestatus indique si un fichier est chiffré ou non. C’est utile dans les scripts de CI pour éviter de commiter des secrets en clair.

Fenêtre de terminal
sops filestatus secrets.enc.yaml
# {"encrypted":true}
sops filestatus secrets.yaml
# {"encrypted":false}

Exemple d’utilisation dans un pre-commit hook :

.git/hooks/pre-commit
#!/bin/bash
for file in $(git diff --cached --name-only -- '*.enc.yaml'); do
status=$(sops filestatus "$file" | python3 -c \
"import sys,json; print(json.load(sys.stdin)['encrypted'])")
if [ "$status" != "True" ]; then
echo "ERREUR: $file n'est pas chiffré !"
exit 1
fi
done

Pour une équipe, vous voulez que plusieurs personnes puissent déchiffrer les secrets. SOPS supporte plusieurs clés dans un même fichier.

Fenêtre de terminal
# Chiffrer pour 2 personnes
sops encrypt \
--age age1abc...,age1def... \
secrets.yaml > secrets.enc.yaml

Chaque personne peut déchiffrer avec sa propre clé privée.

Pour éviter de spécifier les clés à chaque commande, créez .sops.yaml à la racine du projet :

.sops.yaml
creation_rules:
# Secrets de production : équipe ops + KMS
- path_regex: secrets/prod/.*\.yaml$
key_groups:
- age:
- age1abc... # Alice (ops)
- age1def... # Bob (ops)
kms:
- arn: arn:aws:kms:eu-west-1:123456:key/xxx
# Secrets de dev : équipe dev
- path_regex: secrets/dev/.*\.yaml$
age: age1ghi...,age1jkl...
# Par défaut : clé personnelle
- age: age1mno...

Avec cette configuration :

Fenêtre de terminal
# SOPS applique automatiquement les bonnes clés
sops encrypt secrets/prod/database.yaml > secrets/prod/database.enc.yaml

Mettre à jour les clés d’un fichier (updatekeys)

Section intitulée « Mettre à jour les clés d’un fichier (updatekeys) »

Quand un membre quitte l’équipe ou qu’une nouvelle personne arrive, utilisez updatekeys pour synchroniser les clés du fichier avec la configuration .sops.yaml.

Fenêtre de terminal
# Mettre à jour les clés sans confirmation interactive
sops updatekeys -y secrets.enc.yaml
# Sortie :
# Syncing keys for file secrets.enc.yaml
# Group 1
# age1abc... (existant)
# +++ age1xyz... (ajouté)
# --- age1old... (retiré)

SOPS re-chiffre la data key pour les nouveaux destinataires et supprime l’accès pour les clés retirées. Les valeurs chiffrées ne changent pas (seul l’enveloppe change).

La commande rotate génère une nouvelle data key et re-chiffre toutes les valeurs du fichier. C’est une bonne pratique après un incident de sécurité ou lors d’une rotation planifiée.

Fenêtre de terminal
# Rotation de la data key
sops rotate -i secrets.enc.yaml

Après la rotation, les anciennes data keys sont obsolètes. Si un attaquant avait capturé le fichier avant la rotation, il ne peut pas déchiffrer la nouvelle version même s’il possédait l’ancienne data key.

Pour les secrets critiques, SOPS supporte le partage de secret de Shamir (Shamir’s Secret Sharing). Le principe : la data key est découpée en N parts, et il faut au minimum M parts pour la reconstruire (M ≤ N).

Exemple : 3 key groups avec un seuil de 2. Il faut pouvoir déchiffrer avec au moins 2 groupes sur 3 pour récupérer le secret.

.sops.yaml
creation_rules:
- path_regex: 'critical/.*'
shamir_threshold: 2
key_groups:
- age:
- age1abc... # Équipe ops
- age:
- age1def... # Équipe sécurité
- age:
- age1ghi... # Backup offline
Fenêtre de terminal
# Chiffrer : SOPS crée 3 parts
sops encrypt critical/master-key.yaml > critical/master-key.enc.yaml
# Déchiffrer : il faut accès à au moins 2 des 3 clés privées
sops decrypt critical/master-key.enc.yaml

Pour les projets AWS, utilisez KMS pour bénéficier de l’intégration IAM.

  1. Créer une clé KMS

    Fenêtre de terminal
    aws kms create-key \
    --description "SOPS encryption key" \
    --tags TagKey=Purpose,TagValue=SOPS
    # Noter l'ARN : arn:aws:kms:eu-west-1:123456:key/xxx-xxx
  2. Chiffrer avec KMS

    Fenêtre de terminal
    sops encrypt \
    --kms arn:aws:kms:eu-west-1:123456:key/xxx-xxx \
    secrets.yaml > secrets.enc.yaml
  3. Déchiffrer (les credentials AWS sont utilisés automatiquement)

    Fenêtre de terminal
    sops decrypt secrets.enc.yaml

Vous pouvez utiliser plusieurs backends pour la redondance :

.sops.yaml
creation_rules:
- path_regex: .*\.yaml$
key_groups:
- age:
- age1abc... # Backup local
kms:
- arn: arn:aws:kms:eu-west-1:123456:key/xxx

Si KMS est indisponible, la clé age permet toujours de déchiffrer.

La collection community.sops permet de déchiffrer les fichiers SOPS directement dans vos playbooks.

  1. Installer la collection

    Fenêtre de terminal
    ansible-galaxy collection install community.sops
  2. Utiliser le lookup

    ---
    - name: Déployer avec secrets SOPS
    hosts: all
    tasks:
    - name: Lire les secrets
    ansible.builtin.debug:
    msg: "{{ lookup('community.sops.sops', 'secrets.enc.yaml') }}"
    - name: Utiliser une valeur spécifique
    ansible.builtin.debug:
    msg: "Password: {{ (lookup('community.sops.sops', 'secrets.enc.yaml') | from_yaml).database.password }}"
  3. Exporter la clé pour Ansible

    Fenêtre de terminal
    export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
    ansible-playbook playbook.yml

L’opérateur sops-secrets-operator déchiffre automatiquement les SopsSecret en Secret Kubernetes.

apiVersion: isindir.github.com/v1alpha3
kind: SopsSecret
metadata:
name: my-secret
spec:
secretTemplates:
- name: my-k8s-secret
stringData:
username: ENC[AES256_GCM,data:...]
password: ENC[AES256_GCM,data:...]
name: Deploy
on:
push:
branches: [main]
# Permissions minimales au niveau du workflow
permissions: {}
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
# Action épinglée par SHA (pas de tag mutable)
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false
- name: Install SOPS
run: |
curl -sLO https://github.com/getsops/sops/releases/download/v3.12.1/sops-v3.12.1.linux.amd64
chmod +x sops-v3.12.1.linux.amd64
sudo mv sops-v3.12.1.linux.amd64 /usr/local/bin/sops
sops --version
- name: Decrypt secrets
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
run: |
sops decrypt secrets.enc.yaml > secrets.yaml

SOPS peut s’intégrer à git diff pour afficher les valeurs déchiffrées dans les diffs, au lieu des blobs ENC[AES256_GCM,...]. C’est utile pour les code reviews.

  1. Configurer .gitattributes

    *.enc.yaml diff=sopsdiffer
    *.enc.json diff=sopsdiffer
  2. Configurer Git pour utiliser SOPS

    Fenêtre de terminal
    git config diff.sopsdiffer.textconv "sops decrypt"
  3. Vérifier : git diff affiche maintenant les valeurs en clair pour les fichiers chiffrés par SOPS.

ProblèmeCauseSolution
no such file: keys.txtChemin de clé incorrectVérifier SOPS_AGE_KEY_FILE ou créer ~/.config/sops/age/keys.txt
could not decryptClé privée non correspondanteVérifier que votre clé publique est dans le fichier chiffré
failed to create writer for recipient: pq pluginClé PQ age 1.3.0SOPS ne supporte pas les clés PQ, utilisez age1... standard
yaml: unmarshal errorsFormat invalideVérifier la syntaxe YAML avant chiffrement
MAC mismatchFichier corrompu ou modifié manuellementRestaurer depuis Git
cannot use complex value in environmentYAML imbriqué avec exec-envUtiliser un fichier .env ou YAML plat
no matching creation rules found.sops.yaml sans règle correspondanteVérifier les path_regex dans .sops.yaml
Recovery failed (Shamir)Pas assez de key groups disponiblesAvoir accès à au moins shamir_threshold clés privées
Fenêtre de terminal
# Voir qui peut déchiffrer
sops decrypt --show-all-keys secrets.enc.yaml 2>&1 | grep -E "age|kms"
# Vérifier si un fichier est chiffré
sops filestatus secrets.enc.yaml
# {"encrypted":true}
  • Une clé par personne : chaque membre de l’équipe a sa propre clé age, ce qui facilite la révocation quand quelqu’un part
  • Sauvegarder les clés : stockez les clés privées dans un gestionnaire de mots de passe (perte de clé = secrets irrécupérables)
  • .sops.yaml versionné : committez ce fichier pour que toute l’équipe utilise les mêmes règles
  • KMS en production : combinez age (backup) + KMS (rotation automatique, audit)
  • Répertoireproject/
    • .sops.yaml Configuration des clés
    • Répertoiresecrets/
      • Répertoiredev/
        • database.enc.yaml
      • Répertoireprod/
        • database.enc.yaml
    • README.md Instructions pour l’équipe
  1. SOPS chiffre les valeurs, pas les clés : les diffs Git restent lisibles
  2. age est recommandé : simple, pas de service, compatible partout
  3. PGP est déprécié : migrez vers age pour les nouveaux projets
  4. age 1.3.0 post-quantum : non supporté par SOPS (janvier 2026), utilisez les clés standard
  5. Chiffrement sélectif : encrypted_regex garde les données non sensibles lisibles
  6. exec-env / exec-file : injectez les secrets sans fichier intermédiaire sur disque
  7. set / unset / extract : manipulez les fichiers chiffrés par programme
  8. Multi-clés natif : chaque membre de l’équipe a sa clé, révocation via updatekeys
  9. Key groups Shamir : aucune personne seule ne peut déchiffrer les secrets critiques
  10. .sops.yaml : centralisez la configuration des clés et les règles de chiffrement dans le repo
  11. KMS pour la prod : audit, rotation automatique, intégration IAM

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn