Sealed Secrets : Stocker avec sécurité vos secrets dans Git

Rédigé par Nathanaël Hannebert le 3/05/2024
Temps de lecture: 6 minutes
Logo de Sealed Secrets

Chez Cockpit io, nous recommandons d’utiliser autant que possible du code pour piloter l’infrastructure et notamment les clusters Kubernetes. En suivant la philosophie GitOps, ce code est stocké et versionné dans des dépôts git. Pour fonctionner, la plupart des applications ont besoin de paramètres ou d’éléments de configuration sensibles, que nous appelons des “secrets”. Citons par exemple le mot de passe pour qu’une application se connecte à une base de données. Or la sensibilité de telles informations implique qu’il est dangereux de les stocker en clair dans le code d’infrastructure. Une solution est donc de chiffrer ces informations. Nous allons voir comment faire cela avec Selead Secrets de Bitnami.

1. Pré-requis

  • Un cluster Kubernetes
  • Kubectl
  • Helm

2. Présentation de Sealed Secrets

Sealed Secrets est un projet open source, maintenu par Bitnami, composé de deux parties : un contrôleur qui tourne dans le cluster Kubernetes, et une application en ligne de commande (cli), kubeseal à installer sur le poste de l’opérateur. Les secrets sont créés par ce-dernier, qui va utiliser la cli kubeseal pour les chiffrer, puis déployer les secrets chiffrés dans le cluster Kubernetes. C’est le contrôleur qui va se charger de déchiffrer les secrets, depuis le cluster, et de créer les secrets en clair1 dans le cluster.

Le chiffrement est asymétrique : une clé publique et une clé privée sont générées lors de l’installation de Sealed Secrets. La clé privée reste dans le cluster et seule la clé publique est utilisée par l’opérateur. Avec cette clé publique, il va pouvoir chiffrer les secrets. Ceux-ci ne pourront être déchiffrés qu’avec la clé privée. D’autres paires de clé publique et privée peuvent être ajoutées.

Ainsi, un opérateur qui dispose de la clé publique ne peut pas déchiffrer lui-même des secrets chiffrés.

3. Installer Selead Secrets sur Kubernetes

L’installation est proposée par le déploiement d’un manifeste Kubernetes, qui peut être surchargé avec Kustomize, ou par le déploiement d’un Helm Chart.

Pour l’exemple nous allons déployer un Helm Chart :

1helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
2helm install sealed-secrets sealed-secrets/sealed-secrets

4. Installer la cli sur son poste

La cli kubeseal peut être installée sur Linux et Mac. Il nous faut installer une version qui soit identique à celle du contrôleur déployé sur le cluster Kubernetes.

Dans notre exemple, nous avons déployé la version 0.26.2 du contrôleur, alors voici comment installer la cli adaptée sur Linux (amd64) :

1KUBESEAL_VERSION='0.26.2'
2wget "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION:?}/kubeseal-${KUBESEAL_VERSION:?}-linux-amd64.tar.gz"
3tar -xvzf kubeseal-${KUBESEAL_VERSION:?}-linux-amd64.tar.gz kubeseal
4sudo install -m 755 kubeseal /usr/local/bin/kubeseal

5. Chiffrer un secret

Commençons par générer un manifeste contenant un secret générique, avec deux mots de passe :

 1$ kubectl create secret generic my-secret --from-literal=key1=supersecret \
 2--from-literal=key2=topsecret --dry-run=client -o yaml | tee mysecret.yaml
 3
 4apiVersion: v1
 5data:
 6  key1: c3VwZXJzZWNyZXQ=
 7  key2: dG9wc2VjcmV0
 8kind: Secret
 9metadata:
10  creationTimestamp: null
11  name: my-secret

6. Déployer un manifeste contenant un secret chiffré

Maintenant nous pouvons chiffrer ce secret avec la cli kubeseal

1$ kubeseal -f mysecret.yaml -w mysecret-sealed.yaml -o yaml \
2--controller-name sealed-secrets --controller-namespace default

Cette commande crée un objet de type kind: SealedSecret dans l’apiVersion: bitnami.com/v1alpha1, en voici le manifeste :

 1cat mysecret-sealed.yaml
 2---
 3apiVersion: bitnami.com/v1alpha1
 4kind: SealedSecret
 5metadata:
 6  creationTimestamp: null
 7  name: my-secret
 8  namespace: default
 9spec:
10  encryptedData:
11    key1: xxxxxxxxxxxxx
12    key2: xxxxxxxxxxxxx
13  template:
14    metadata:
15      creationTimestamp: null
16      name: my-secret
17      namespace: default

Nous retrouvons bien la structure de données de notre secret initial, le nom de la ressource est identique, my-secret, et il y a bien deux clés, key1 et key2, dont les valeurs sont désormais chiffrées. Appliquons ce manifeste :

 1$ kubectl apply -f mysecret-sealed.yaml
 2
 3sealedsecret.bitnami.com/my-secret created
 4
 5$ kubectl get SealedSecret
 6NAME        STATUS   SYNCED   AGE
 7my-secret            True     5s
 8
 9$ kubectl get secret
10NAME                  TYPE            DATA   AGE
11my-secret             Opaque          2      41s

Dès que la ressource SealedSecret est créée, le contrôleur sealed-secrets (qui s’exécute sur le cluster Kubernetes) la déchiffre et crée le secret. Nous pouvons vérifier cela en consultant les logs :

1kubectl logs deploy/sealed-secrets
2
3time=2024-04-23T14:49:41.881Z level=INFO msg="Event(v1.ObjectReference{Kind:\"SealedSecret\", Namespace:\"default\", Name:\"my-secret\", UID:\"3346b241-2d24-4b47-8873-d4cf7970900e\", APIVersion:\"bitnami.com/v1alpha1\", ResourceVersion:\"92259\", FieldPath:\"\"}): type: 'Normal' reason: 'Unsealed' SealedSecret unsealed successfully"

Voici le secret créé par le contrôleur :

 1$ kubectl get secret my-secret -o yaml
 2apiVersion: v1
 3data:
 4  key1: c3VwZXJzZWNyZXQ=
 5  key2: dG9wc2VjcmV0
 6kind: Secret
 7metadata:
 8  creationTimestamp: "2024-04-23T14:49:41Z"
 9  name: my-secret
10  namespace: default
11  ownerReferences:
12  - apiVersion: bitnami.com/v1alpha1
13    controller: true
14    kind: SealedSecret
15    name: my-secret
16    uid: 3346b241-2d24-4b47-8873-d4cf7970900e
17  resourceVersion: "92260"
18  uid: 84b8bf63-754d-41df-9d2f-60a155406c54
19type: Opaque

Le contrôleur de Sealed Secrets est capable de suivre le cycle de vie des secrets : la mise à jour ou la suppression des ressources de type SealedSecret sont reflétées respectivement par la mise à jour ou la suppression de ressources de type secret.

7. GitOps

Avec Sealed Secrets, nous pouvons chiffrer de manière sûre les manifestes contenant des secrets. Ainsi, il est possible de versionner ces manifestes dans un dépôt git et de continuer à travailler avec la méthodologie GitOps.

Par exemple, les secrets chiffrés peuvent être déployés automatiquement par les solutions ArgoCD ou FluxCD. Une fois ce déploiement effectué sur le cluster Kubernetes, le contrôleur de Sealed Secrets se chargera de créer les secrets équivalents en clair1 au sein du même cluster. Les secrets n’existeront en clair1 qu’au sein du cluster Kubernetes.

Par ailleurs, les secrets étant chiffrés avec une paire de clés asymétriques, nous pouvons accorder à une personne la possibilité de chiffrer des secrets en lui fournissant la clé publique nécessaire, mais celle-ci ne sera pas en mesure de déchiffrer elle-même les secrets chiffrés préexistants, si elle n’a ni clé privée ni la possibilité de la récupérer depuis le cluster Kubernetes.

8. Pour aller plus loin

a. Sauvegarder les clés privées

Une des particularités de Sealed Secrets est son fonctionnement avec des clés publiques et privées, et la conservation des clés privées dans le cluster Kubernetes. Comme dit plus haut, c’est seulement grâce à ces clés privées qu’il est possible de déchiffrer les secrets. Dans le cas où nous voudrions pouvoir accéder à des secrets chiffrés manuellement, par exemple en cas de perte du cluster Kubernetes, ces clés privées seraient nécessaires. Ainsi, il serait prudent d’en conserver une copie, en un lieu de stockage sûr, accessible seulement en cas de nécessité.

Voici comment obtenir les clés privées :

 1kubectl get secret -n default -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml
 2apiVersion: v1
 3items:
 4- apiVersion: v1
 5  data:
 6    tls.crt: xxxxxxxxxxxxxxxxxxxxx
 7    tls.key: xxxxxxxxxxxxxxxxxxxxx
 8  kind: Secret
 9  metadata:
10    creationTimestamp: "2024-04-22T15:03:19Z"
11    generateName: sealed-secrets-key
12    labels:
13      sealedsecrets.bitnami.com/sealed-secrets-key: active
14    name: sealed-secrets-keyl59jb
15    namespace: default
16    resourceVersion: "73121"
17    uid: 9a424dd5-8c50-4b8a-939b-befe7f5e788d
18  type: kubernetes.io/tls
19kind: List
20metadata:
21  resourceVersion: ""

b. Rotation des clés et secrets

Une des bonnes pratiques lorsque nous utilisons des secrets est de les changer régulièrement, c’est ce que nous appelons la rotation des secrets. L’utilisation de Sealed Secrets ne nous prémunie pas d’opérer cette rotation des secrets. En complément, le contrôleur de Sealed Secrets génère tous les 30 jours (cette durée peut être personnalisée) une nouvelle paire de clés publique et privée.


  1. Dans les clusters Kubernetes, les secrets sont stockés durablement dans la base de données etcd. Nous vous recommandons fortement d’activer le chiffrement au repos pour ces secrets (et toutes les autres ressources qui pourraient contenir des données sensibles). Ainsi, ces secrets n’existeront en clair, que durant le fonctionnement du cluster, et seront conservés chiffrés sur le support de stockage utilisé par etcd↩︎ ↩︎ ↩︎