Introduction

Lorsque vous déployez une application sur Kubernetes, il est possible que celle-ci ait besoin de secrets pour fonctionner. Ces secrets peuvent être des mots de passe, des clés SSH, des tokens, etc.

Mais comment gérer ces secrets ? Comment les stocker ? Comment les partager avec les développeurs ou permettre aux développeurs de les créer ?

Par exemple, vous souhaitez définir un secret correspondant à un token d’API. Et dans une démarche GitOps, vous stockez vos manifests Kubernetes dans un dépôt Git. Comment faire pour que le token ne soit pas en clair dans le dépôt Git ?

Il existe de nombreuses solutions pour gérer les secrets dans Kubernetes, je vais vous en présenter une : Sealed-Secret.

Sealed secret

Sealed Secret

Sealed secret est un projet open-source développé par Bitnami. Il permet de chiffrer des secrets Kubernetes via un couple de clé publique/privée. La clé privée est présente sur le cluster Kubernetes et la clé publique est utilisée par les développeurs pour chiffrer les secrets.

Une fois le secret créé et chiffré, seul le cluster Kubernetes peut le déchiffrer. (Ou une personne ayant la clé privée)

Installation

Pour installer Sealed-secret, il faut utiliser Helm. Il est possible de l’installer via la commande suivante :

helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets -n kube-system sealed-secrets/sealed-secrets 

Maintenant que nous avons notre Sealed secret installé, nous pouvons installer Kubeseal qui nous servira à :

  • Créer nos sealed secrets (à partir de la clé publique)
  • Déchiffrer nos sealed secrets (via le cluster, ou via clé privée)
KUBESEAL_VERSION='0.24.0'
wget "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION:?}/kubeseal-${KUBESEAL_VERSION:?}-linux-amd64.tar.gz"
tar -xvzf kubeseal-${KUBESEAL_VERSION:?}-linux-amd64.tar.gz kubeseal
sudo install -m 755 kubeseal /usr/local/bin/kubeseal

Créer un Sealed Secret

Je vais créer mon secret Kubernetes générique avec la syntaxe habituelle. Suite à ça, je vais le chiffrer avec Kubeseal qui communiquera avec le contrôleur Sealed-secret. Je dois lui fournir le nom du service lié au pod controleur ainsi que le namespace où il se trouve.

apiVersion: v1
kind: Secret
metadata:
  name: mon-secret
data:
  user: UXVlbnRpbg==
  slogan: Vml2ZSBsZSBjYWbDqQ==
cat secret.yaml | kubeseal \
    --controller-namespace kube-system \
    --controller-name sealed-secrets \
    --format yaml \
    > sealed-secret.yaml

Avec cette commande : kubeseal va se connecter au service sealed-secrets pour récupérer la clé publique et chiffrer le contenu de notre yaml. Voici le résultat de notre commande :

---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: mon-secret
  namespace: default
spec:
  encryptedData:
    user: AgBdt5uTaeTSpHQ4gDFw9E3vu5ihAJvqEnO3unMK8Q7UroMl+nq/CGdqePocumtDYVAW2U5wGWZFa/KELnK7AOJ8Es4SjloDXmeq620/b3nRbbv825ixdo80vstrzH6RMBUoHKhaagTy/p3C3atACxpW9C++HUXJ/GmBfVTzGWKXUh2OQ3STAx4zDRvwlTs7d3Ot734DSBA6rAK45xzTIiXDGt3rv3TwN7te1kXxM/TMWUPB6iczYVhCb2ruaVfHAdlQnO6ytdbiUO/J5Bd/EdBhVFwcbXjsjcS6wHQeIwFDhQu1mEfnDbd4e9QCH0ef3QVAF/TlnMdwKaq5KWPnakIYABD5wWsMe73XSN9CWG0qbtjirRuZREeCbbGfjetsJFPTEgKSgnXwC/dtfXCppBSmHRN/2y4N5DNh9nzcpQQDX6mENbiL8xmxr7A31/6QHdjwtCMWbPNWpfalvS/Vp3Lr0/ZThV6KHmo73QB6RBVrhcSWjQQYi94CI/RFQWVT+LUxwrWbLBHAa7LnRElcOQ401/rLrvs+AU9eS2ni6B5snMUhYKMw7anhr6UAe21+M+UKuMK/C7n9P91avcfxpEBVPJByYZuTeqyvIsmE6uHjswZcZU7g05rHVqgfjQmuHKCEdj4hmxW4bM2QZowdH7L870Xz89txbzTKc25Alg74S80b0sw/jV7Yct/oAAtO/E34BRN8zFajZQ==
    slogan: AgBanbBAe3Viz11uu5Nc8TWRP/1FHfKjPKvsmS9RSNPkIeoO1QDisN6YB6R81ftbtzhe6YuLEYg9uSdO6UH6kRcrzB9PhouAjqs0jug9FPBQnDDoh3DL3nBfnKwfz8RWoF7upFazREqPApVZP2sJGACxG6mZJNzEPKw+FOuXGGwMDZu/GMDav4dtsjUh8073dAsDZKTzD6AlfAd8xzZ/dh2XWhCt5fL+Dlfbc8lCRh8dtD4KC7iBUJL3Qsv6dfSvLrUbdTIejiZOd16TkD1SccQQiDztDqIJvLxuHcNPxrxn8nnvTC7pqDBl6VpHUa6dzxFhZKG20mx7hSvByAR9RJu3reepd2cTm5N7XXPFJk0MPURsWRh9vsqpvbJF8j/6Bs1gYAblZEE9seC8ySNslzlUaUgLvz+MjZfDbdJ7fHd35O2P7adOEuUEmqrH7j7hXupcs4OJ35+4vUwdSeG7kGz0txlqVa5z3c8kkAMfWG80DFIyxwHrCl7CYlXYult5FUBAXCxuOYntvtDzfMNVdmsXxDPWZ14UezDRdZSWW5hCMeDKEdk5T6h/nm7AFY5+UOYeIDXJeseSoazY7lJRM6keG9xAba1Pn8lq712qqDxWuauR0YZN/TanWSa0iqZEwqoksnMwEF1vuMXo3Sr7ATVjPatWAq84uNh7p2G+DREOKDQUZgxTGBsZblr8aZSN/rD4Zea2ZeSNS0v/GITZLw==
  template:
    metadata:
      creationTimestamp: null
      name: mon-secret
      namespace: default

Astuce

Dans la partie template, nous pouvons rajouter des annotations ou des labels qui seront appliqués au secret généré par le contrôleur.

Ce fichier est uniquement déchiffrable par la clé privée présente sur mon cluster. Dès que nous envoyons le fichier sealed-secret.yaml au cluster, le contrôleur va automatiquement créer un secret générique Kubernetes en utilisant sa clé privée.

Le contrôleur utilisé est celui présent dans le namespace kube-system, il est possible d’en avoir plusieurs sur un même cluster.

Mais qu’en est-il du cas où nous n’avons pas un accès au cluster pour que la commande kubeseal puisse récupérer la clé publique ?

Il est possible de copier la clé présente sur notre kubeseal pour l’envoyer à nos développeurs n’ayant pas accès au cluster.

kubeseal --fetch-cert > cert.pem

Astuce

Il est aussi possible de récupérer ce même fichier à au chemin /v1/cert.pem du serveur web de sealed-secret.

wget http://sealed-secrets-controller.kube-system.svc.cluster.local:8080/v1/cert.pem -O /dev/stdout -q

Dès que nous avons ce fichier, nous pouvons refaire la même commande pour créer notre fichier sealed-secret.yaml mais en précisant --cert plutôt que --controller-(namespace|name).

cat secret.yaml | kubeseal \
    --cert cert.pem \
    --format yaml \
    > sealed-secret.yaml

Le secret généré est identique au précédent, mais sans avoir eu besoin de se connecter au cluster.

Après avoir envoyé le fichier sealed-secret.yaml au cluster, nous pouvons voir que le contrôleur a créé un secret générique Kubernetes en créant ce pod:

apiVersion: v1
kind: Pod
metadata:
  name: petit-pod
spec:
 containers:
   - name: busybox
     image: busybox
     command: ['sh', '-c', 'echo "$SECRET_USER, $SECRET_SLOGAN"']
     env:
       - name: SECRET_USER
         valueFrom:
           secretKeyRef:
             name: mon-secret 
             key: user
       - name: SECRET_SLOGAN
         valueFrom:
           secretKeyRef:
             name: mon-secret 
             key: slogan
 restartPolicy: Never

résultat:

kubectl apply -f pod.yml 
# pod/petit-pod created
kubectl logs pod/petit-pod
# Quentin, Vive le café

Astuce

Rien ne vous empêche d’avoir plusieurs couples de clés utilisées par sealed-secret.

Vous pouvez conserver la clé générée par sealed-secret et ajouter la clé d’un autre cluster. Nous allons voir comment faire dans la partie suivante pour exporter le trousseau de clés.

Sauvegarder ses clés

Si vous souhaitez réinstaller votre cluster, ou avoir la même clé dans 2 clusters différents, vous pouvez exporter la clé utilisée par Sealed-Secret de cette manière:

kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml > sealed-secret-key.yaml

Le fichier généré pourra être importé dans un cluster via la commande kubectl.

kubectl apply -n kube-system -f sealed-secret-key.yaml

Métriques

Suite à mon article sur Prometheus, j’ai pris la résolution de toujours vous montrer les exporters présents dans les outils que j’utilise (Oui ça va vous saouler, oui je vais quand même le faire 😇 ).

Il est possible de récupérer des métriques de sealed-secret via le fameux /metrics.

Conclusion

Sealed-secret est un outil très pratique pour gérer les secrets de votre cluster Kubernetes. Comme je suis beaucoup en déplacement : je l’utilise beaucoup pour travailler en offline.

Je compare beaucoup Sealed-Secret avec le combo Sops-Kustomize (que j’utilise actuellement dans mon projet GitOps) qu’est une très bonne alternative répondant au même besoin : chiffrer des manifests Yaml et les déchiffrer avec une clé privé présente sur le cluster.

Cette page n’est pas aussi complète que celles sur Prometheus ou Consul, mais je pense que Sealed-Secret est un outil à connaître et pratique dans de nombreux cas qui mérite d’être partagé.

J’espère que cet article vous aura plu, n’hésitez pas à me faire un retour sur Twitter ou LinkedIn.