Intro
Les Secrets Kubernetes stockent identifiants, jetons, clés d'API et autres configurations sensibles. Traitez-les comme des joyaux de la couronne. Ce guide montre comment durcir les Secrets dans de vrais clusters avec des exemples YAML et des commandes kubectl pratiques. Vous apprendrez à :
- Inventorier et classifier les Secrets selon leur sensibilité et leur propriétaire
- Activer le chiffrement au repos pour les Secrets dans le magasin de données de l'API server (serveur d'API)
- Appliquer le principe du moindre privilège avec RBAC (Role‑Based Access Control) : contrôle d'accès basé sur les rôles, et des comptes de service dédiés par charge de travail
- Consommer les Secrets via des montages de fichiers en lecture seule au lieu de variables d'environnement
- Ajouter des politiques réseau default‑deny au niveau de l'espace de noms avec des autorisations explicites
- Exécuter des vérifications rapides, à faible risque, pour valider le durcissement
Workflow Overview
Appliquez les étapes espace de noms par espace de noms :
- Inventorier et classifier
- Chiffrer au repos
- Verrouiller RBAC et comptes de service
- Utiliser les Secrets en toute sécurité dans les pods
- Contenir l'exposition réseau
- Ajouter des contrôles et de la rotation
1) Inventorier et classifier les Secrets
Listez tous les Secrets et identifiez les propriétaires :
kubectl get secrets -A -o wide
Ajoutez des étiquettes cohérentes pour faciliter les requêtes :
kubectl label secret db-credentials owner=payments purpose=database -n team-apps --overwrite
Créez un nouveau Secret avec un nommage clair et une portée définie :
kubectl create secret generic db-credentials \
--from-literal=username=app \
--from-literal=password='S3cr3t!' \
-n team-apps
Conseil : préférez un Secret par application ou composant plutôt que des Secrets partagés entre plusieurs applications. Cela limite le rayon d'impact et simplifie le RBAC.
2) Chiffrer les Secrets au repos
Les Secrets résident dans le magasin de données de l'API server (Application Programming Interface server : serveur d'interface de programmation d'applications). Activez le chiffrement pour qu'ils soient chiffrés sur disque.
Exemple de EncryptionConfiguration (fournisseur aescbc) :
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <clé-32-octets-encodée-en-base64>
- identity: {}
Étapes de haut niveau :
- Placez le fichier sur chaque nœud control‑plane.
- Démarrez l'API server avec
--encryption-provider-config=/chemin/vers/config.yaml. - Redémarrez l'API server et lancez
kubectl get secrets --all-namespaces -o json | kubectl replace -f -pour rechiffrer les données existantes.
Dans les clusters gérés, activez le paramètre « Secrets encryption » fourni par la plateforme.
3) Verrouiller RBAC et comptes de service
Accordez l'accès en lecture uniquement aux charges de travail qui ont besoin d'un Secret spécifique.
Compte de service qui ne monte pas le jeton par défaut :
apiVersion: v1
kind: ServiceAccount
metadata:
name: web-sa
namespace: team-apps
automountServiceAccountToken: false
Rôle autorisant get sur un seul Secret :
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-db-credentials
namespace: team-apps
rules:
- apiGroups: ["
resources: ["secrets
d resourceNames: ["db-credentials
d verbs: ["get
RoleBinding :
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: bind-read-db-credentials
namespace: team-apps
subjects:
- kind: ServiceAccount
name: web-sa
namespace: team-apps
roleRef:
kind: Role
name: read-db-credentials
apiGroup: rbac.authorization.k8s.io
Testez :
kubectl auth can-i get secret db-credentials \
--as=system:serviceaccount:team-apps:web-sa \
-n team-apps
4) Utiliser les Secrets en toute sécurité dans les pods
Montez les Secrets comme des fichiers en lecture seule ; évitez les variables d'environnement qui peuvent fuiter dans les logs et /proc.
Extrait de spécification de pod (Deployment) :
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: team-apps
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
serviceAccountName: web-sa
automountServiceAccountToken: false
containers:
- name: app
image: your-registry/web:1.0.0
volumeMounts:
- name: db-secret
mountPath: /var/run/secrets/app
readOnly: true
env:
- name: DB_USER_FILE
value: /var/run/secrets/app/username
- name: DB_PASS_FILE
value: /var/run/secrets/app/password
volumes:
- name: db-secret
secret:
secretName: db-credentials
L'application lit les fichiers au démarrage (cat $DB_USER_FILE). Si les variables d'environnement sont inévitables, limitez-les aux conteneurs strictement nécessaires et n'utilise jamais envFrom avec un Secret entier.
Rendez les Secrets immuables pour empêcher les modifications accidentelles :
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: team-apps
type: Opaque
immutable: true
data:
username: YXBw
password: UzNjcjN0IQ==
La rotation se fait en créant db-credentials-v2, en mettant à jour le Deployment pour le référencer, en déployant le roulement, puis en supprimant l'ancien Secret après validation.
5) Contenir l'exposition réseau
Les politiques réseau ne protègent pas directement les Secrets, mais elles limitent les déplacements latéraux.
Refus par défaut de tout trafic entrant et sortant :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: team-apps
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
Autorisez le trafic DNS sortant (adaptez les sélecteurs à votre cluster) :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: team-apps
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Ajoutez des politiques d'autorisation explicites pour chaque dépendance requise.
6) Ajouter des vérifications et audits rapides
Exécutez ces contrôles légers régulièrement.
Qui peut lire quels Secrets
kubectl auth can-i --list \
--as=system:serviceaccount:team-apps:web-sa \
-n team-apps | grep secrets || true
Pods qui référencent des Secrets via des variables d'environnement (nécessite jq) :
kubectl get pods -A -o json | jq -r '
.items[]
| select((.spec.containers // [])
| map(.env // [] | map(has("valueFrom
) and .valueFrom.secretKeyRef != null) | any) | any)
| [.metadata.namespace, .metadata.name] | @tsv'
Secrets non immuables :
kubectl get secrets -A -o json | jq -r '
.items[] | select(.immutable != true) | [.metadata.namespace, .metadata.name] | @tsv'
Rôles/ClusterRoles à portée large pouvant lire de nombreux Secrets :
kubectl get role,clusterrole -A -o yaml | grep -n "resources: \[secrets\]" || true
Confirmer le chiffrement au repos – inspectez le drapeau --encryption-provider-config de l'API server sur les nœuds control‑plane ou le réglage du service géré.
Local Pilot Plan
Commencez petit, mesurez, puis étendez.
Périmètre
- Espace de noms :
team-apps - Charge de travail : un seul Deployment
webqui a besoin dedb-credentials
Critères de succès
- Seul
web-sapeutgetle Secretdb-credentialsdansteam-apps - Aucun pod dans
team-appsne consomme de Secrets via des variables d'environnement - L'espace de noms possède une politique réseau default‑deny avec des autorisations explicites
Étapes
- Créer l'espace de noms et le compte de service
kubectl create namespace team-apps
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
name: web-sa
namespace: team-apps
automountServiceAccountToken: false
EOF
- Créer le Secret
kubectl create secret generic db-credentials \
--from-literal=username=app \
--from-literal=password='S3cr3t!' \
-n team-apps
- Appliquer le RBAC au moindre privilège
kubectl apply -f - <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-db-credentials
namespace: team-apps
rules:
- apiGroups: ["
resources: ["secrets
d resourceNames: ["db-credentials
d verbs: ["get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: bind-read-db-credentials
namespace: team-apps
subjects:
- kind: ServiceAccount
name: web-sa
namespace: team-apps
roleRef:
kind: Role
name: read-db-credentials
apiGroup: rbac.authorization.k8s.io
EOF
kubectl auth can-i get secret db-credentials \
--as=system:serviceaccount:team-apps:web-sa \
-n team-apps
- Déployer l'application avec montage de Secrets en fichiers
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: team-apps
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
serviceAccountName: web-sa
automountServiceAccountToken: false
containers:
- name: app
image: your-registry/web:1.0.0
volumeMounts:
- name: db-secret
mountPath: /var/run/secrets/app
readOnly: true
env:
- name: DB_USER_FILE
value: /var/run/secrets/app/username
- name: DB_PASS_FILE
value: /var/run/secrets/app/password
volumes:
- name: db-secret
secret:
secretName: db-credentials
EOF
- Appliquer default‑deny et autoriser DNS
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: team-apps
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: team-apps
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
EOF
- Valider
# Seul web-sa peut lire db-credentials
kubectl auth can-i get secret db-credentials \
--as=system:serviceaccount:team-apps:web-sa \
-n team-apps
# Le SA par défaut ne doit pas pouvoir
kubectl auth can-i get secret db-credentials \
--as=system:serviceaccount:team-apps:default \
-n team-apps
# Aucun pod ne consomme de Secrets via des variables d'env
kubectl get pods -n team-apps -o json | jq -r '
.items[]
| select((.spec.containers // [])
| map(.env // [] | map(has("valueFrom
d) and .valueFrom.secretKeyRef != null) | any) | any)
| .metadata.name' | wc -l
# Les politiques réseau existent
kubectl get networkpolicy -n team-apps
- Documenter la rotation de
db-credentials
- Créer
db-credentials-v2 - Mettre à jour le Deployment pour référencer
db-credentials-v2 - Déployer le roulement et vérifier
- Supprimer
db-credentialsaprès validation
Plan de retour arrière
- Conservez l'ancien Secret jusqu'à la fin de la validation.
- En cas d'échec du déploiement, rétablissez la référence au Secret précédent dans le Deployment et relancez les tests.
Conclusion
Vous disposez maintenant d'un chemin pratique pour durcir les Secrets Kubernetes : inventorier, chiffrer au repos, appliquer le moindre privilège, garder les Secrets hors des variables d'environnement, restreindre l'exposition réseau et vérifier avec des contrôles reproductibles. Lancez un pilote dans un espace de noms, mesurez les résultats, puis étendez progressivement.
Suivez quelques indicateurs clés au fur et à mesure de l'expansion :
- Nombre de Secrets par espace de noms et présence d'étiquettes propriétaire
- Rôles pouvant lire des Secrets arbitraires vs Rôles limités à des noms précis
- Pods consommant des Secrets via variables d'environnement vs montages de fichiers
- Espaces de noms couverts par des politiques réseau default‑deny
Une approche par étapes garde les changements sûrs et observables tout en améliorant votre posture de sécurité pas à pas.