Stocker des secrets Kubernetes dans des dépôts Git sans chiffrement est l’une des erreurs de sécurité les plus courantes dans les déploiements cloud-native. Bien que les Secrets Kubernetes utilisent l’encodage base64, cela ne fournit aucune protection cryptographique — toute personne ayant accès au dépôt peut les décoder instantanément. SOPS (Secrets OPerationS) combiné avec le chiffrement Age résout ce problème élégamment, vous permettant de stocker des secrets chiffrés directement dans Git tout en maintenant une compatibilité totale avec les workflows GitOps comme ArgoCD et FluxCD.

Prérequis

  • Un cluster Kubernetes fonctionnel (v1.24+) avec kubectl configuré
  • sops v3.8+ installé
  • age v1.1+ installé
  • Familiarité de base avec les Secrets Kubernetes et les manifestes YAML
  • Dépôt Git pour vos manifestes Kubernetes
  • Optionnel : ArgoCD ou FluxCD pour l’intégration GitOps

Comprendre les Risques des Secrets Kubernetes

Les Secrets Kubernetes sont souvent mal compris comme un mécanisme de stockage sécurisé. En réalité, ils ont des limitations significatives que vous devez comprendre avant de concevoir votre stratégie de secrets.

Base64 N’est Pas du Chiffrement

Un Secret Kubernetes standard stocke les valeurs comme des chaînes encodées en base64 :

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=
  password: cDRzc3cwcmQxMjM=

Décoder ces valeurs est trivial :

echo "cDRzc3cwcmQxMjM=" | base64 -d
# Sortie : p4ssw0rd123

Toute personne qui clone votre dépôt ou obtient un accès en lecture à etcd peut extraire chaque secret de votre cluster.

Préoccupations de Stockage etcd

Par défaut, Kubernetes stocke les Secrets sans chiffrement dans etcd. Bien que vous puissiez activer le chiffrement au repos avec EncryptionConfiguration, cela ne protège que les fichiers de données etcd — pas les réponses du serveur API ni les manifestes stockés dans Git. Vous avez besoin d’une solution qui chiffre les secrets avant qu’ils n’entrent dans votre système de contrôle de versions.

Le Problème Git

GitOps exige que l’état souhaité de votre cluster vive dans Git. Mais commiter des secrets en texte clair dans Git signifie :

  • Chaque développeur ayant accès au dépôt voit les identifiants de production
  • L’historique des secrets persiste dans Git pour toujours, même après suppression
  • Les dépôts divulgués exposent chaque secret jamais commité
  • Les cadres de conformité (SOC 2, PCI-DSS) interdisent les identifiants en texte clair dans le contrôle de code source

Installation de SOPS et Age

Linux

# Installer Age
sudo apt-get install age
# Ou depuis les releases GitHub
curl -LO https://github.com/FiloSottile/age/releases/download/v1.2.0/age-v1.2.0-linux-amd64.tar.gz
tar xzf age-v1.2.0-linux-amd64.tar.gz
sudo mv age/age age/age-keygen /usr/local/bin/

# Installer SOPS
curl -LO https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64
sudo mv sops-v3.9.4.linux.amd64 /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops

macOS

brew install sops age

Vérifiez les deux installations :

sops --version
# sops 3.9.4
age --version
# v1.2.0

Chiffrement des Secrets avec SOPS et Age

Générer une Paire de Clés Age

age-keygen -o age-key.txt
# Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Stockez la clé privée en sécurité. La clé publique peut être partagée et commitée dans votre dépôt :

# Stocker la clé où SOPS peut la trouver
mkdir -p ~/.config/sops/age
mv age-key.txt ~/.config/sops/age/keys.txt

# Ou définir la variable d'environnement
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt

Créer la Configuration .sops.yaml

Créez un fichier .sops.yaml à la racine de votre dépôt pour définir les règles de chiffrement :

creation_rules:
  # Chiffrer tous les fichiers dans le répertoire secrets/
  - path_regex: secrets/.*\.yaml$
    age: >-
      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

  # Chiffrer les secrets de staging avec une clé différente
  - path_regex: envs/staging/secrets/.*\.yaml$
    age: >-
      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p,
      age1lzd99uca0lqtmgahfmxj4gvr2fcswcaxmxnz30fwcmm22hjvrzrqsnqxsl

  # Clé différente pour la production
  - path_regex: envs/production/secrets/.*\.yaml$
    age: >-
      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p,
      age1an5quvgyl8uny5mkmgkzpnpe9wuufrl2vvmqsht4xp3k96s608q0eamcl

Plusieurs destinataires (séparés par des virgules) permettent l’accès d’équipe — n’importe quelle clé listée peut déchiffrer le fichier.

Chiffrer un Secret Kubernetes

Commencez avec votre manifeste de secret en texte clair :

# secrets/db-credentials.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: production
type: Opaque
stringData:
  username: admin
  password: "s3cur3-pr0d-p@ssw0rd"
  connection-string: "postgresql://admin:s3cur3-pr0d-p@ssw0rd@db.internal:5432/myapp"

Chiffrez-le avec SOPS :

sops --encrypt --in-place secrets/db-credentials.yaml

Le fichier chiffré préserve la structure YAML mais chiffre les valeurs :

apiVersion: v1
kind: Secret
metadata:
    name: db-credentials
    namespace: production
type: Opaque
stringData:
    username: ENC[AES256_GCM,data:k8mN3w==,iv:abc...,tag:def...,type:str]
    password: ENC[AES256_GCM,data:dGhpcyBpcyBl...,iv:ghi...,tag:jkl...,type:str]
    connection-string: ENC[AES256_GCM,data:bG9uZ2VyIHN0cmluZw==...,iv:mno...,tag:pqr...,type:str]
sops:
    age:
        - recipient: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
          enc: |
            -----BEGIN AGE ENCRYPTED FILE-----
            ...
            -----END AGE ENCRYPTED FILE-----
    lastmodified: "2026-02-17T10:00:00Z"
    version: 3.9.4

Les clés (apiVersion, kind, metadata) restent lisibles tandis que les valeurs sont chiffrées. Cela signifie que git diff montre quels secrets ont changé sans révéler les valeurs réelles.

Déchiffrer et Appliquer

# Déchiffrer et appliquer en une seule commande
sops --decrypt secrets/db-credentials.yaml | kubectl apply -f -

# Ou déchiffrer vers un fichier temporaire
sops --decrypt secrets/db-credentials.yaml > /tmp/secret.yaml
kubectl apply -f /tmp/secret.yaml
rm -f /tmp/secret.yaml

Intégration avec les Workflows GitOps

ArgoCD avec KSOPS

KSOPS est un plugin Kustomize qui déchiffre les fichiers chiffrés avec SOPS pendant les opérations de synchronisation ArgoCD.

Installez KSOPS dans votre serveur de dépôt ArgoCD :

# Patch argocd-repo-server
apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-repo-server
  namespace: argocd
spec:
  template:
    spec:
      containers:
        - name: argocd-repo-server
          env:
            - name: SOPS_AGE_KEY
              valueFrom:
                secretKeyRef:
                  name: sops-age-key
                  key: age-key.txt
            - name: XDG_CONFIG_HOME
              value: /.config
          volumeMounts:
            - mountPath: /.config/kustomize/plugin/viaduct.ai/v1/ksops
              name: custom-tools
      initContainers:
        - name: install-ksops
          image: viaductoss/ksops:v4.3.2
          command: ["/bin/sh", "-c"]
          args:
            - cp /usr/local/bin/ksops /.config/kustomize/plugin/viaduct.ai/v1/ksops/ksops
          volumeMounts:
            - mountPath: /.config/kustomize/plugin/viaduct.ai/v1/ksops
              name: custom-tools
      volumes:
        - name: custom-tools
          emptyDir: {}

Créez un générateur KSOPS dans votre overlay Kustomize :

# secret-generator.yaml
apiVersion: viaduct.ai/v1
kind: ksops
metadata:
  name: secret-generator
files:
  - secrets/db-credentials.yaml
  - secrets/api-keys.yaml

Intégration Native FluxCD

FluxCD a un support natif de SOPS. Créez un secret de déchiffrement et configurez votre Kustomization :

# Créer le secret de clé Age dans le namespace flux-system
kubectl create secret generic sops-age \
  --namespace=flux-system \
  --from-file=age.agekey=~/.config/sops/age/keys.txt
# flux-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 10m
  path: ./envs/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: my-app
  decryption:
    provider: sops
    secretRef:
      name: sops-age

FluxCD déchiffre automatiquement tout fichier chiffré avec SOPS dans le chemin spécifié lors de la réconciliation.

SOPS+Age vs Sealed Secrets vs Vault vs External Secrets

FonctionnalitéSOPS + AgeSealed SecretsHashiCorp VaultExternal Secrets
Lieu du chiffrementCôté clientCôté contrôleurCôté serveurCôté serveur
Compatible GitOui (YAML chiffré)Oui (ressource custom)Non (références seules)Non (références seules)
Infrastructure requiseAucuneContrôleur dans le clusterServeur VaultOpérateur + backend
Gestion des clésFichiers de clé AgeCertificat du clusterPolitiques VaultVarie selon backend
Multi-environnementRègles .sops.yamlCertificats par clusterNamespaces/politiquesMultiples stores
Intégration GitOpsPlugins ArgoCD/FluxK8s natifDriver CSI/injectorSync opérateur
RotationRe-chiffrer avec SOPSRe-sceller requisSecrets dynamiquesDépend du backend
ComplexitéFaibleFaibleÉlevéeMoyenne
Fonctionne hors ligneOuiNonNonNon
Idéal pourPetites-moyennes équipesClusters individuelsEntreprise/conformitéMulti-cloud

SOPS+Age excelle quand vous voulez la simplicité, la capacité hors ligne et un vrai GitOps sans infrastructure supplémentaire.

Scénario Réel

Vous gérez un déploiement Kubernetes multi-environnement sur des clusters de développement, staging et production. Votre équipe de huit ingénieurs utilise ArgoCD pour le GitOps, et vous avez besoin d’une solution de secrets qui :

  • Permette aux développeurs de créer et mettre à jour des secrets sans accès au cluster
  • Garde les secrets chiffrés dans Git pour l’auditabilité
  • Utilise différentes clés de chiffrement par environnement
  • Permette la rotation des clés sans redéployer chaque secret

Voici comment structurer votre dépôt :

k8s-manifests/
├── .sops.yaml
├── base/
│   ├── deployment.yaml
│   └── service.yaml
├── envs/
│   ├── dev/
│   │   ├── kustomization.yaml
│   │   ├── secret-generator.yaml
│   │   └── secrets/
│   │       └── app-secrets.yaml    # chiffré avec clé dev
│   ├── staging/
│   │   ├── kustomization.yaml
│   │   ├── secret-generator.yaml
│   │   └── secrets/
│   │       └── app-secrets.yaml    # chiffré avec clé staging
│   └── production/
│       ├── kustomization.yaml
│       ├── secret-generator.yaml
│       └── secrets/
│           └── app-secrets.yaml    # chiffré avec clé prod

Chaque environnement a sa propre paire de clés Age. Les développeurs détiennent la clé dev, les chefs d’équipe détiennent dev+staging, et seul le pipeline CI/CD détient la clé de production. Le fichier .sops.yaml dirige le chiffrement vers la bonne clé en fonction du chemin du fichier.

Pièges et Cas Limites

La rotation des clés nécessite un re-chiffrement. Quand vous faites tourner une clé Age, vous devez déchiffrer chaque fichier avec l’ancienne clé et re-chiffrer avec la nouvelle. SOPS fournit sops updatekeys pour cela, mais testez d’abord :

# Mettre à jour les clés pour un seul fichier (utilise les règles .sops.yaml)
sops updatekeys secrets/db-credentials.yaml

# Re-chiffrer tous les secrets en lot
find . -name "*.yaml" -path "*/secrets/*" -exec sops updatekeys {} \;

Chiffrement partiel avec encrypted_regex. Par défaut, SOPS chiffre toutes les valeurs. Utilisez encrypted_regex dans .sops.yaml pour chiffrer uniquement des clés spécifiques :

creation_rules:
  - path_regex: secrets/.*\.yaml$
    encrypted_regex: "^(data|stringData)$"
    age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Les secrets binaires nécessitent d’abord un encodage base64. SOPS opère sur des fichiers texte. Pour les secrets binaires (certificats TLS, keystores), encodez-les en base64 avant de chiffrer :

# Encoder le certificat, puis laisser SOPS chiffrer la chaîne base64
cat tls.crt | base64 -w0 > tls.crt.b64

Ne commitez jamais la clé privée Age. Ajoutez-la au .gitignore immédiatement :

echo "age-key.txt" >> .gitignore
echo "*.agekey" >> .gitignore

La section métadonnées SOPS grandit avec les destinataires. Chaque destinataire Age supplémentaire ajoute ~300 octets de métadonnées. Avec beaucoup de destinataires, envisagez d’utiliser une clé d’équipe partagée distribuée via un canal sécurisé plutôt que des clés individuelles.

Résolution de Problèmes

Erreur : could not find common decryption key La clé privée Age n’est pas disponible. Vérifiez l’emplacement du fichier de clé :

# Vérifier si le fichier de clé existe
ls -la ~/.config/sops/age/keys.txt

# Ou vérifier la variable d'environnement
echo $SOPS_AGE_KEY_FILE

# Vérifier que la clé correspond au destinataire
grep "public key" ~/.config/sops/age/keys.txt

Erreur : failed to decrypt Le fichier a été chiffré avec une clé Age différente. Vérifiez quelle clé a été utilisée :

sops --decrypt --verbose secrets/db-credentials.yaml 2>&1 | grep "recipient"

La synchronisation ArgoCD échoue avec des erreurs KSOPS Assurez-vous que le conteneur init KSOPS s’est terminé avec succès et que le secret de clé Age existe :

kubectl logs deployment/argocd-repo-server -n argocd -c install-ksops
kubectl get secret sops-age-key -n argocd

FluxCD Kustomization bloqué en Not Ready Vérifiez les logs du contrôleur Kustomize :

kubectl logs deployment/kustomize-controller -n flux-system | grep -i sops

Cause courante : le secret sops-age est dans le mauvais namespace ou a le mauvais nom de clé (doit être age.agekey).

Le fichier chiffré affiche une erreur mac mismatch Le fichier a été modifié après le chiffrement sans utiliser sops --set ou sops edit. Re-chiffrez depuis la source en texte clair :

sops --decrypt secrets/db-credentials.yaml > /tmp/plain.yaml
sops --encrypt /tmp/plain.yaml > secrets/db-credentials.yaml
rm -f /tmp/plain.yaml

Résumé

  • Les Secrets Kubernetes utilisent l’encodage base64, pas le chiffrement — ils ne sont pas sécurisés par défaut
  • SOPS chiffre les valeurs YAML tout en gardant les clés lisibles, permettant des diffs Git significatifs
  • Age fournit une gestion de clés plus simple que PGP sans surcharge de configuration
  • Le fichier .sops.yaml définit des règles de chiffrement par chemin pour les configurations multi-environnement
  • ArgoCD s’intègre via le plugin KSOPS ; FluxCD a un support natif de déchiffrement SOPS
  • La rotation des clés nécessite de re-chiffrer tous les fichiers affectés avec sops updatekeys
  • Stockez toujours les clés privées Age en dehors du dépôt et dans le .gitignore
  • Pour les environnements d’entreprise nécessitant des secrets dynamiques, envisagez HashiCorp Vault

Articles Connexes