
Secrets : Gérer les données sensibles (mots de passe, clés API)
Apprenez à utiliser les Secrets Kubernetes pour stocker et injecter de manière sécurisée les données sensibles (mots de passe, clés API, tokens) dans vos Pods.
Le défi des données confidentielles en configuration
Nous avons vu comment les ConfigMaps permettent de gérer efficacement la configuration non sensible. Mais qu'en est-il des informations que vous ne voulez absolument pas exposer en clair ? Mots de passe de bases de données, clés API pour des services tiers, tokens d'authentification, certificats TLS... Ces données sont critiques pour le fonctionnement de nombreuses applications, mais leur divulgation peut avoir des conséquences désastreuses sur la sécurité.
Stocker ces informations directement dans les images de conteneurs, les manifestes YAML (même en variables d'environnement directes) ou les ConfigMaps est une pratique extrêmement risquée. Il nous faut un mécanisme dédié, conçu spécifiquement pour manipuler ces données sensibles de manière plus sécurisée au sein du cluster Kubernetes. C'est le rôle de l'objet Secret.
Les Secrets sont similaires aux ConfigMaps dans leur structure et leur utilisation (stockage clé-valeur, injection dans les Pods), mais ils sont destinés exclusivement aux données confidentielles et bénéficient de mécanismes de protection supplémentaires (bien qu'il soit crucial de comprendre leurs limites par défaut).
Qu'est-ce qu'un Secret Kubernetes ?
Un Secret est un objet API Kubernetes conçu pour contenir une petite quantité de données sensibles. Tout comme les ConfigMaps, ils permettent de découpler les informations confidentielles du code applicatif et des définitions de Pods.
Les principales différences et caractéristiques par rapport aux ConfigMaps sont :
- Intention : L'intention claire est de stocker des données sensibles.
- Encodage (par défaut) : Les valeurs dans un Secret sont stockées dans etcd encodées en Base64. Attention : Base64 n'est pas du chiffrement, c'est simplement un encodage pour représenter des données binaires en texte ASCII. Il est trivial de décoder une valeur Base64. La sécurité ne repose donc pas sur cet encodage seul.
- Stockage etcd : Par défaut, les Secrets (encodés en Base64) sont stockés en clair dans etcd, tout comme les ConfigMaps. Pour une réelle sécurité au repos, il est fortement recommandé de configurer le chiffrement des données Secret au repos dans etcd (Encryption at Rest).
- Accès limité : Kubernetes permet un contrôle d'accès plus fin (via RBAC) sur les Secrets que sur les ConfigMaps. Vous pouvez restreindre qui peut lire ou écrire des Secrets spécifiques.
- Moins de fuites accidentelles : `kubectl get secret` ou `kubectl describe secret` n'affichent pas le contenu des Secrets par défaut pour éviter une exposition accidentelle dans les logs ou sur les écrans. Vous devez explicitement demander à voir le contenu décodé (par ex. avec `-o yaml` ou `-o json` et un décodage manuel/via des outils).
- Utilisation par le système : Kubernetes utilise lui-même des Secrets pour stocker les identifiants nécessaires à l'accès aux registres d'images privés (`kubernetes.io/dockerconfigjson`) ou les certificats TLS (`kubernetes.io/tls`).
Structure et types de Secrets
Un Secret stocke ses données sous forme de paires clé-valeur, où les valeurs sont des séquences d'octets (byte sequences).
Structure YAML :
apiVersion: v1
kind: Secret
metadata:
name: mon-secret-db
namespace: production
type: Opaque # Type de Secret (par défaut)
data: # Les valeurs ici DOIVENT être encodées en Base64
db.password: "MWYyZDFlMmU2N2Zj" # Exemple: echo -n '1f2d1e2e67fc' | base64
api.key: "YWFhYmJiY2NjZGRkZWVkZmZmZw==" # Exemple: echo -n 'aaabbbcccdddeeefffgg' | base64
# OU (plus pratique pour les chaînes simples)
# stringData:
# db.password: "1f2d1e2e67fc" # Valeurs en clair, Kubernetes les encodera en Base64 pour vous
# api.key: "aaabbbcccdddeeefffgg"Points clés :
- `type` : Indique le type de Secret, ce qui aide Kubernetes ou d'autres outils à interpréter les données. Le type par défaut est `Opaque`, signifiant des données arbitraires clé-valeur. D'autres types intégrés existent (voir section création). Utiliser le bon type peut activer des validations supplémentaires.
- `data` : Un dictionnaire où les clés sont les noms des entrées secrètes et les valeurs sont les données correspondantes encodées en Base64.
- `stringData` (alternatif) : Un champ de commodité qui accepte des valeurs en chaînes de caractères claires (non encodées). Kubernetes se chargera de les encoder en Base64 avant de les stocker dans le champ `data` interne. C'est souvent plus pratique pour créer des Secrets à partir de YAML. Si une clé existe à la fois dans `data` et `stringData`, `stringData` est prioritaire lors de la création/mise à jour.
Créer des Secrets
Comme pour les ConfigMaps, plusieurs méthodes existent :
- A partir de fichiers YAML (méthode déclarative) : Utilisez la structure ci-dessus, en préférant `stringData` pour la lisibilité lorsque possible. Appliquez avec `kubectl apply -f mon-secret.yaml`.
- Création générique avec `kubectl` (`type: Opaque`) :
- A partir de valeurs littérales :
- A partir d'un fichier (le contenu est stocké sous une clé portant le nom du fichier) :kubectl create secret generic mon-secret-literal \ --from-literal=username=admin \ --from-literal=password='S!cr3t' \ -n mon-namespace
- A partir d'un fichier avec une clé spécifique :kubectl create secret generic secret-depuis-fichier \ --from-file=chemin/vers/ma_cle_privee.pem \ -n mon-namespacekubectl create secret generic secret-fichier-cle \ --from-file=mon_certificat=chemin/vers/cert.crt \ -n mon-namespace - Création de Secrets typés avec `kubectl` :
- Pour les identifiants de registre Docker (`type: kubernetes.io/dockerconfigjson`) :
Ce Secret peut ensuite être référencé dans `spec.imagePullSecrets` des Pods.kubectl create secret docker-registry mon-registry-secret \ --docker-server=mon-registry.example.com \ --docker-username=monuser \ --docker-password='monmotdepasse' \ --docker-email=user@example.com \ -n mon-namespace
- Pour les certificats TLS (`type: kubernetes.io/tls`) :
Ce Secret contiendra deux clés : `tls.crt` et `tls.key`, et peut être utilisé par exemple par les contrôleurs Ingress.kubectl create secret tls mon-tls-secret \ --cert=chemin/vers/tls.crt \ --key=chemin/vers/tls.key \ -n mon-namespace
Attention : Lorsque vous utilisez `--from-literal` ou `--docker-password` en ligne de commande, le secret peut apparaître dans l'historique de votre shell. La méthode via fichier YAML (surtout avec `stringData` pour la lisibilité) stocké dans un endroit sécurisé ou via des outils de gestion de secrets externes est souvent préférable.
Utiliser les Secrets dans les Pods
Injecter un Secret dans un Pod se fait de manière très similaire à l'injection d'un ConfigMap, mais en référençant l'objet Secret.
- Comme Variables d'Environnement :
- Injecter une clé spécifique : Utilisez `env.valueFrom.secretKeyRef`.
- Injecter toutes les clés comme variables : Utilisez `envFrom.secretRef`.spec: containers: - name: mon-conteneur image: mon-image env: - name: DB_PASSWORD # Nom de la variable dans le conteneur valueFrom: secretKeyRef: name: mon-secret-db # Nom du Secret key: db.password # Clé à récupérer dans le Secretspec: containers: - name: mon-conteneur image: mon-image envFrom: - secretRef: name: mon-secret-db # Nom du Secret - Comme Fichiers dans un Volume : Montez le Secret comme un type de volume `secret`.
Chaque clé du Secret devient un fichier dans `/etc/secrets`. Le contenu du fichier est la valeur décodée du Secret. Monter des secrets en volume est souvent considéré comme plus sécurisé que via des variables d'environnement (qui peuvent fuiter via des logs ou des inspections `/proc`).spec: containers: - name: mon-conteneur image: mon-image volumeMounts: - name: secret-volume mountPath: /etc/secrets # Répertoire de montage readOnly: true # Fortement recommandé pour les secrets ! volumes: - name: secret-volume secret: secretName: mon-secret-db # Nom du Secret à monter # items: # Optionnel: pour monter des clés spécifiques # - key: api.key # path: cle_api.txt
Mises à jour : Similaire aux ConfigMaps, les variables d'environnement ne sont pas mises à jour dynamiquement après un changement du Secret. Les volumes Secrets, eux, sont mis à jour par le Kubelet, mais l'application doit pouvoir recharger le fichier.
Considérations de sécurité et bonnes pratiques
- Activer le chiffrement etcd au repos : C'est la mesure la plus importante pour protéger les Secrets stockés.
- RBAC strict : Limitez au maximum qui peut lire (`get`, `list`, `watch`) et écrire (`create`, `update`, `delete`) des objets Secret, en particulier dans les namespaces de production. Utilisez des Roles et RoleBindings spécifiques.
- Principe de moindre privilège : Les Pods ne devraient avoir accès qu'aux Secrets dont ils ont absolument besoin (via des montages spécifiques ou des `secretKeyRef`). Evitez `envFrom` si possible.
- Ne pas commiter les Secrets en clair dans Git : Utilisez des outils comme Helm Secrets, Kustomize avec SOPS, Sealed Secrets, ou des gestionnaires de secrets externes (Vault, AWS Secrets Manager, etc.) avec des intégrations Kubernetes (comme External Secrets Operator ou CSI Secret Store Driver) pour gérer les secrets de manière sécurisée dans un workflow GitOps.
- Rotation des secrets : Mettez en place des processus pour renouveler régulièrement les secrets (clés API, mots de passe, certificats).
- Audit : Activez les logs d'audit de Kubernetes pour surveiller l'accès aux Secrets.
- Attention aux logs applicatifs : Assurez-vous que vos applications ne loguent pas accidentellement les valeurs des secrets qu'elles reçoivent.
Gérer les Secrets avec `kubectl`
- Lister les Secrets : `kubectl get secrets` (ou `kubectl get secret`) [-n namespace]
- Afficher les détails (sans les valeurs) : `kubectl describe secret [NOM_SECRET]` [-n namespace] (Montre les clés, le type, la taille, mais pas les valeurs).
- Afficher le contenu complet (YAML, avec valeurs encodées) : `kubectl get secret [NOM_SECRET] -o yaml` [-n namespace]
- Afficher une valeur décodée (exemple) :
(Il faut utiliser `jsonpath` pour extraire la valeur encodée, puis la décoder manuellement).kubectl get secret mon-secret-db -n production -o jsonpath='{.data.db\.password}' | base64 --decode - Supprimer un Secret : `kubectl delete secret [NOM_SECRET]` [-n namespace]
Conclusion : la clé de voûte de la configuration sécurisée
Les Secrets sont un composant indispensable pour gérer les données sensibles nécessaires à vos applications dans Kubernetes. Bien qu'ils ne soient pas une solution miracle de sécurité par eux-mêmes (surtout sans chiffrement etcd activé), ils fournissent le cadre et les mécanismes nécessaires pour manipuler ces données de manière beaucoup plus contrôlée et découplée que les autres méthodes.
En utilisant les Secrets correctement, en appliquant des contrôles d'accès stricts et en adoptant les bonnes pratiques de sécurité, vous pouvez considérablement améliorer la posture de sécurité de vos applications déployées sur Kubernetes. Ils constituent, avec les ConfigMaps, la solution standard pour une gestion de configuration mature et adaptée aux environnements conteneurisés.