Secrets GitOps légers pour Kubernetes

Chaque jour chez Sokube, nous prônons GitOps comme moyen de simplifier, d’améliorer et d’accélérer les opérations numériques, et nous accompagnons nos clients dans cette démarche. Avec l’abondance des briques technologiques qui composent aujourd’hui le paysage “Cloud Native”, ce qui paraissait difficile et hasardeux il y a quelques années se réduit désormais simplement au choix de la combinaison de produits / plateformes la plus adaptée à un cas d’usage.
Cependant, lorsqu’il s’agit de préserver la confidentialité d’informations sensibles dans une démarche GitOps, les choix ne sont ni nombreux ni simples. Avec son encodage base64 réversible, Kubernetes Secrets reste le talon d’Achille de la plateforme en matière de sécurité ou de confidentialité de l’information, que le RBAC soit correctement configuré ou non. L’utilisation native de Kubernetes Secrets pour stocker des informations confidentielles ou sensibles devrait être purement proscrite sur des systèmes de production, à minima.
Plusieurs alternatives compatibles GitOps préservant la confidentialité ont naturellement émergé comme solutions de contournement : la première partie de cet article se concentrera sur un rapide panorama de l’existant. Si Hashicorp Vault reste une solution réellement sécurisée, riche en fonctionnalités et largement adoptée, nous pensons qu’elle se positionne plutôt comme une solution transverse à l’échelle de l’entreprise ou de l’organisation pour l’information confidentielle. Dans le contexte de GitOps toutefois, elle viendrait fausser le principe du “git comme source unique de vérité”. Enfin, un déploiement pertinent de HashiCorp Vault nécessite souvent des changements organisationnels adéquats ainsi qu’un budget conséquent pour son exploitation.
Dans la seconde partie de l’article, nous nous concentrerons sur deux solutions légères qui offrent exactement ce dont on a besoin pour GitOps sans bouleverser l’organisation, le projet ni les budgets : Bitnami Sealed Secrets et Kamus de Soluto.
Panorama des solutions de secrets Kubernetes
La plupart de nos cas d’usage concernent des installations exclusivement on-premise. Ainsi, si Amazon, Azure et Google proposent généralement leur propre gestion de secrets pour leur plateforme, nous nous sommes concentrés en priorité sur les solutions on-premise, et nous les avons évaluées selon les critères suivants :
- Complexité : nous avons globalement estimé l’effort nécessaire pour prendre en main la solution, l’installer et l’exploiter.
- Maturité : basée sur le nombre et la fréquence des versions, l’ancienneté de la solution, les exemples concrets trouvés sur le web démontrant son adoption.
- Portée : classifie la portée de validité des secrets ; organisation signifie qu’il n’existe aucune restriction spécifique de validité du secret, contrairement à cluster ou compte de service, où le secret ne peut pas être déchiffré sur un autre cluster spécifique ou avec un autre compte de service.
- Dépendance : c’est un aspect important de la solution qui la rend plus ou moins applicable dans des contextes particuliers.
| Solution | Catégorie | Complexité | Maturité | Portée des secrets | Dépendance | Note |
|---|---|---|---|---|---|---|
| Helm Secrets | On Premise | MEDIUM | Abandonné | Organisation | Aucune | Abandonné, fork jkroepke/helm-secrets |
| Kamus | On Premise | LOW | Alpha | Compte de service / Cluster | Aucune | Prometteur |
| External Secrets Operator | On Premise (nécessite un backend KMS cloud) | LOW | Alpha | Organisation | Nécessite un backend KMS | Très jeune, nécessite un KMS derrière |
| aws-secret-operator | On Premise (nécessite un backend AWS SM) | LOW | Alpha | Organisation | Nécessite AWS SM derrière | Nécessite AWS Secrets Manager |
| KSOPS | On Premise | LOW | Alpha | Organisation | SOPS Kustomize | Peut s’intégrer avec ArgoCD |
| Kapitan avec Tesoro | On Premise | LOW | Alpha | Organisation | Backend KMS Kapitan | Sympa mais n’a de sens qu’avec kapitan |
| Bitnami Sealed Secrets | On Premise | VERY LOW | Stable | Cluster | Aucune | Simple mais nécessite de copier les clés entre clusters |
| Godaddy Kubernetes External Secrets | On Premise (nécessite un backend KMS cloud) | LOW | Stable | Organisation | Nécessite un backend KMS | Bon wrapper mais nécessite un KMS derrière |
| Hashicorp Vault | On Premise Hosted | HIGH | Stable | Organisation | Aucune | Solution éprouvée mais coût de mise en œuvre / gestion ÉLEVÉ |
| Banzai Cloud Bank-Vaults | On Premise | MEDIUM | Stable | Organisation | Hashicorp Vault | Simplifie Vault mais nécessite toujours Vault |
| Kustomize secret generator plugins | On Premise | HIGH | Stable | Organisation | Nécessite un service derrière | Nécessite de développer un plugin et un service derrière |
Bitnami Sealed Secrets
La solution Bitnami Sealed Secrets repose sur un contrôleur qui s’exécute dans le cluster cible, où une paire de clés privée/publique spécifique au cluster est générée.
Chiffrement
La clé publique est utilisée par un binaire client (kubeseal) pour chiffrer les données à la volée (sous la forme de ressources personnalisées nommées SealedSecret). Cette opération peut également être réalisée hors ligne (sans avoir besoin d’un cluster avec un contrôleur bitnami) en utilisant le certificat du contrôleur (clé publique) qui peut être téléchargé depuis le cluster. La ressource résultante est un SealedSecret qu’il est sûr de stocker dans un dépôt git.

Déchiffrement
Le contrôleur détecte la présence de ressources SealedSecret et déchiffre automatiquement le contenu en une ressource Secret standard équivalente.

Installation
L’installation de la solution est relativement simple :
$ kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.13.1/controller.yaml
L’outil client (kubeseal) permettant d’interagir avec le contrôleur à l’intérieur du cluster cible est un binaire autonome qui peut être téléchargé séparément :
$ wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.13.1/kubeseal-linux-amd64 -O kubeseal
Usage
Nous allons utiliser un namespace dédié :
|
Le kubeseal de Bitnami peut être utilisé comme une commande en pipe, et lorsqu’il est combiné avec les capacités de dry-run de kubectl, nous pouvons générer des sealed secrets sans même de ressources intermédiaires stockées localement sous forme de fichiers :
|
Le SealedSecret résultant peut être stocké en toute sécurité dans un dépôt git :
|
Application de la ressource :
|
Vérification de la ressource :
|
Remarques
Important à savoir : chaque sealed secret est chiffré avec sa propre clé asymétrique aléatoire, spécifique au nom et au namespace du sealed secret. Copier-coller les données chiffrées pour un autre secret ou dans un autre namespace ne fonctionnera pas.
Par défaut, les SealedSecrets générés pour un cluster ne fonctionnent pas avec un autre cluster (l’installation du contrôleur Bitnami crée une nouvelle paire de clés publique/privée), ce qui peut rendre les opérations globales de mise en staging GitOps fastidieuses (chaque environnement devrait disposer de ses propres SealedSecrets personnalisés). Une solution consiste à exporter/importer le même jeu de paires de clés d’un cluster à l’autre. Toutefois, une clé compromise sur un cluster donnerait également accès à tous les sealed secrets de tous les clusters utilisant la même paire de clés.
Résumé
➕ Simplicité de mise en place et d’usage
➕ Solution mature et bien maintenue
➖ Les Secrets classiques restent exposés
➖ Aucun usage en dehors des Secrets (par exemple ConfigMap)
Kamus
La solution Kamus propose deux mécanismes différents de génération de secrets. L’un est similaire à Bitnami Sealed Secrets, l’autre est une véritable solution de gestion de secrets en zero-trust.
Premier mode : KamusSecret
Les KamusSecret tendent à imiter Bitnami Sealed Secrets : un contrôleur dans le cluster détecte les KamusSecrets et fournit une version déchiffrée sous forme de Secret.
La partie encodage est un peu plus fastidieuse, car le kamus-client ne permet pas de passer une ressource Secret entière mais fonctionne plutôt comme un service de chiffrement brut : chaque élément doit être chiffré séparément avec le client, puis agrégé manuellement dans une définition KamusSecret sous forme d’“enveloppe”.

Comme avec Bitnami, le contrôleur est responsable du déchiffrement d’un KamusSecret en un Secret classique.

Second mode : Secrets Zero-Trust
Contrairement à un Bitnami Sealed Secret ou à un KamusSecret, le mode zero-trust de Kamus présente l’immense avantage de ne jamais révéler les données déchiffrées, sauf au service concerné, à l’exécution.
La partie chiffrement du mode secrets zero-trust est très similaire à l’exemple précédent : au lieu d’envelopper les données encodées dans un KamusSecret, une ConfigMap sera utilisée pour stocker les secrets sous forme de fichiers chiffrés dans un volume ConfigMap monté.

Pour la consommation des secrets, un initContainer invoque le service kamus-decryptor et les résultats déchiffrés sont stockés dans un volume emptyDir éphémère accessible uniquement au conteneur applicatif du pod.

Installation
L’installation du contrôleur et de ses CustomResourcesDefinitions est relativement simple :
|
Nous avons ajouté ici la génération de clé aléatoire, car la solution est fournie (dangereusement) avec une clé prédéfinie. De plus, contrairement à Bitnami, la solution installe ses workloads dans le namespace par défaut, nous recommandons donc d’ajuster la release helm en conséquence. Enfin, le nombre de replicas des déploiements encryptor/decryptor est fixé à 2 par défaut, ce qui peut s’avérer superflu.
Le client Kamus est malheureusement un package npm (un choix discutable qui oblige les utilisateurs à installer le gestionnaire de paquets npm). Une image de conteneur Docker semble disponible, cependant elle n’est pas mentionnée sur le site de Kamus, nous avons donc choisi de nous en tenir à l’approche documentée :
|
Nous allons utiliser un namespace et un compte de service dédiés :
|
Le kamus-cli a également besoin d’un pod encryptor exposé sous forme d’URL :
|
Usage de KamusSecret
Premier scénario, mise en place d’un KamusSecret contenant nos informations sensibles :
|
Nous devons ensuite créer un objet enveloppe KamusSecret basé sur la structure suivante (notez que la documentation fait référence à une section data qui ne semble pas pertinente ou fonctionnelle, nous avons donc utilisé la syntaxe stringData).
|
$ kubectl apply -f kamus.yaml
|
$ kubectl get kamussecrets.soluto.com -n my-namespace NAME AGE db-credentials 20s $ kubectl get secrets -n my-namespace NAME TYPE DATA AGE default-token-sc4ds kubernetes.io/service-account-token 3 9m3s db-credentials Generic 2 53s
|
$ kubectl get secrets -n my-namespace db-credentials -o jsonpath=’{.data.DBUSER}’ | base64 –decode mydbuser $ kubectl get secrets -n my-namespace db-credentials -o jsonpath=’{.data.DBPWD}’ | base64 –decode highlysecret
|
$ cat «EOF >kamus-encrypted-secrets-cm.yaml apiVersion: v1 kind: ConfigMap metadata: name: kamus-encrypted-secrets-cm namespace: my-namespace data: DBUSER: 0XZm9vDLVvjNGCgeFZqNnA==:GoaTZCXkwyfoBNRwjtgsOQ== DBPWD: NlHRuvaqFlip5mDP6d5cDw==:OVW3C2hY/wZW6o2I+YjL5Q== EOF
|
$ cat «EOF >kamus-pod.yaml apiVersion: v1 kind: Pod metadata: namespace: my-namespace name: kamus-zero-trust-example spec: serviceAccountName: dummy-sa automountServiceAccountToken: true initContainers:
- name: “kamus-init”
image: “soluto/kamus-init-container:latest”
imagePullPolicy: IfNotPresent
env:
- name: KAMUS_URL value: http://kamus-decryptor.default.svc.cluster.local/ volumeMounts:
- name: encrypted-secrets mountPath: /encrypted-secrets
- name: decrypted-secrets mountPath: /decrypted-secrets args: ["-e","/encrypted-secrets","-d","/decrypted-secrets", “-n”, “config.json”] containers:
- name: app
image: busybox
imagePullPolicy: IfNotPresent
args: [“sleep”, “3600”]
volumeMounts:
- name: decrypted-secrets mountPath: /secrets volumes:
- name: encrypted-secrets configMap: name: kamus-encrypted-secrets-cm
- name: decrypted-secrets emptyDir: medium: Memory EOF ```
Création de notre pod applicatif :
|
Remarques
À noter que :
- dans le cas d’un KamusSecret (première méthode), les arguments –service-account et –namespace de la ligne de commande kamus-cli, bien qu’obligatoires, sont purement arbitraires : les données chiffrées d’un KamusSecret ne sont liées à aucun compte de service ni namespace en particulier, et leur existence n’est pas requise au moment du chiffrement.
- À l’inverse, dans le cas du mode Zero-Trust (seconde méthode), la cohérence des comptes de service et des namespaces entre les étapes de chiffrement et ceux utilisés dans l’initContainer est fondamentale. Un autre compte de service, ou un autre namespace, ne déclenchera pas de déchiffrement correct.
Résumé
➕ Plusieurs modes de fonctionnement
➕ Fonctionne en environnement zero-trust
➕ Peut utiliser un véritable backend KMS
➖ Documentation parfois confuse
➖ Solution encore aux premiers stades
➖ Son client basé sur npm
Conclusion
Si Kamus offre davantage d’options que Bitnami, il nécessite un effort nettement plus important pour les scénarios les plus courants. La documentation fournie avec la solution est également parfois confuse, bien que des efforts significatifs aient été faits sur la clarté depuis les premières versions. Certains clients et publics seront particulièrement sensibles à sa capacité à fonctionner dans un environnement zero-trust, malgré la mise en place conséquente et l’injection d’init containers dans les workloads que cela exige. Elle peut également s’appuyer sur des solutions de coffre-fort cryptographiques sophistiquées et sécurisées comme backend, ce qui constitue également un argument de poids lorsque cela est requis.
Dans les scénarios les plus courants cependant, Bitnami Sealed Secrets demeure particulièrement efficace, compact et simple à exploiter. La solution existe depuis un certain temps déjà, est bien maintenue et s’impose comme une réponse de facto pour la gestion légère des secrets dans les workflows GitOps, et ce pour de bonnes raisons.
Comme toujours, il n’existe pas de meilleure alternative universelle, mais il y a toujours la meilleure réponse à un cas d’usage particulier, avec ses exigences et ses contraintes propres à l’environnement. Nous espérons que cet article vous aidera à choisir la solution de gestion de secrets adaptée à vos scénarios GitOps.
Pour aller plus loin, découvrez notre formation ArgoCD et GitOps et nos consultants DevSecOps.