Sécurité dans Kubernetes
La sécurité dans Kubernetes est un sujet vaste qui couvre plusieurs couches : l'accès à l'API, l'isolation des workloads, la gestion des secrets, et les restrictions de ressources. Ce chapitre présente les mécanismes essentiels pour sécuriser un cluster.
RBAC (Role-Based Access Control)
Le RBAC est le mécanisme de contrôle d'accès dans Kubernetes. Il permet de définir qui peut faire quoi sur quelles ressources.
Concepts clés
-> Subjects : Les entités qui effectuent des actions
- Users : utilisateurs humains (non gérés par K8s)
- Groups : groupes d'utilisateurs
- ServiceAccounts : identités pour les pods
-> Resources : Les objets K8s (pods, services, deployments, etc.)
-> Verbs : Les actions possibles
get,list,watch: lecturecreate,update,patch: modificationdelete: suppression
Les 4 objets RBAC
| Objet | Portée | Description |
|---|---|---|
| Role | Namespace | Définit des permissions dans un namespace |
| ClusterRole | Cluster | Définit des permissions au niveau cluster |
| RoleBinding | Namespace | Lie un Role/ClusterRole à un subject dans un namespace |
| ClusterRoleBinding | Cluster | Lie un ClusterRole à un subject au niveau cluster |
Exemple : Role et RoleBinding
Créons un Role qui permet de lire les pods dans le namespace dev :
# role-pod-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]Maintenant, lions ce Role à un utilisateur :
# rolebinding-pod-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: dev
subjects:
- kind: User
name: alice
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.ioExemple : ClusterRole pour un ServiceAccount
Pour qu'un pod puisse lister les nodes du cluster :
# clusterrole-node-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitoring-sa
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: monitoring-node-reader
subjects:
- kind: ServiceAccount
name: monitoring-sa
namespace: monitoring
roleRef:
kind: ClusterRole
name: node-reader
apiGroup: rbac.authorization.k8s.ioMise en pratique
-> Créez un namespace et appliquez le RBAC
kubectl create ns dev
kubectl apply -f role-pod-reader.yaml
kubectl apply -f rolebinding-pod-reader.yaml-> Vérifiez les permissions
kubectl auth can-i list pods --namespace=dev --as=alice
# yes
kubectl auth can-i delete pods --namespace=dev --as=alice
# no
kubectl auth can-i list pods --namespace=default --as=alice
# no-> Listez les roles et bindings
kubectl get roles -n dev
kubectl get rolebindings -n dev
kubectl describe role pod-reader -n devNetwork Policies
Les Network Policies permettent de contrôler le trafic réseau entre les pods. Par défaut, tous les pods peuvent communiquer entre eux. Les Network Policies ajoutent des règles de firewall au niveau du cluster.
WARNING
Les Network Policies nécessitent un CNI (Container Network Interface) qui les supporte, comme Calico, Cilium, ou Weave Net. Le CNI par défaut de Minikube ne les supporte pas nativement.
Activer les Network Policies sur Minikube
minikube start --cni=calicoPolitique par défaut : Deny All
Une bonne pratique est de bloquer tout le trafic par défaut, puis d'ouvrir au cas par cas :
# deny-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressAutoriser le trafic entrant depuis certains pods
Autorisons les pods avec le label role: frontend à accéder aux pods role: backend sur le port 8080 :
# allow-frontend-to-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
role: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 8080Autoriser le trafic sortant vers Internet
Autorisons les pods à accéder à Internet (DNS + HTTPS) :
# allow-egress-internet.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-internet
namespace: production
spec:
podSelector:
matchLabels:
needs-internet: "true"
policyTypes:
- Egress
egress:
# DNS
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
# HTTPS
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
ports:
- protocol: TCP
port: 443Mise en pratique
-> Appliquez une politique deny-all
kubectl create ns production
kubectl apply -f deny-all.yaml-> Testez la connectivité (elle devrait échouer)
# Créer deux pods de test
kubectl run frontend --image=nginx -n production -l role=frontend
kubectl run backend --image=nginx -n production -l role=backend
# Tester la connexion (timeout attendu)
kubectl exec -n production frontend -- curl --max-time 5 backend-> Appliquez la politique d'autorisation
kubectl apply -f allow-frontend-to-backend.yaml
# Retester (devrait fonctionner)
kubectl exec -n production frontend -- curl --max-time 5 backendResourceQuota et LimitRange
Ces objets permettent de limiter les ressources consommées dans un namespace.
ResourceQuota
Limite les ressources totales d'un namespace :
# resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: dev
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
services: "10"
secrets: "20"
configmaps: "20"
persistentvolumeclaims: "10"LimitRange
Définit des valeurs par défaut et des limites pour les containers :
# limitrange.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: dev
spec:
limits:
- default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
max:
cpu: "2"
memory: "2Gi"
min:
cpu: "50m"
memory: "64Mi"
type: ContainerMise en pratique
-> Appliquez les quotas
kubectl apply -f resourcequota.yaml
kubectl apply -f limitrange.yaml-> Vérifiez les quotas
kubectl get resourcequota -n dev
kubectl describe resourcequota compute-quota -n dev
# Used:
# limits.cpu: 0
# limits.memory: 0
# ...-> Testez le dépassement
# Ce pod sera refusé si le quota est dépassé
kubectl run test --image=nginx -n dev --requests='cpu=10,memory=20Gi'
# Error: exceeded quotaSecrets
Les Secrets permettent de stocker des données sensibles (mots de passe, tokens, clés SSH).
WARNING
Par défaut, les Secrets sont stockés en base64 (encodés, pas chiffrés). Pour une vraie sécurité en production, activez le chiffrement at-rest d'etcd.
Créer un Secret
# Via kubectl
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=supersecret
# Ou via un fichier
kubectl create secret generic tls-cert \
--from-file=tls.crt=./server.crt \
--from-file=tls.key=./server.keyUtiliser un Secret dans un Pod
# pod-with-secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: app-with-secrets
spec:
containers:
- name: app
image: nginx
env:
# En variable d'environnement
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# Ou en fichier monté
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentialsBonnes pratiques pour les Secrets
- Ne jamais commiter de secrets dans Git
- Utiliser des outils comme Sealed Secrets, Vault, ou External Secrets Operator
- Activer le chiffrement at-rest pour etcd
- Limiter l'accès aux secrets via RBAC
Sécurisation de l'accès à etcd
etcd contient toutes les données du cluster, y compris les secrets. Sa sécurisation est critique.
Bonnes pratiques
-> Chiffrement at-rest
Configurer Kubernetes pour chiffrer les données dans etcd :
# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}-> Accès réseau restreint
- etcd ne doit être accessible que par l'API Server
- Utiliser TLS pour toutes les communications
- Firewall entre etcd et le reste du réseau
-> Authentification TLS mutuelle
L'API Server et etcd doivent s'authentifier mutuellement avec des certificats.
Security Context
Le Security Context définit les privilèges et les restrictions d'un pod ou d'un container.
Exemple : Pod non-root
# secure-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: app
image: nginx
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}Options importantes
| Option | Description |
|---|---|
runAsNonRoot | Empêche l'exécution en root |
runAsUser | UID de l'utilisateur |
readOnlyRootFilesystem | Filesystem en lecture seule |
allowPrivilegeEscalation | Empêche l'escalade de privilèges |
capabilities.drop | Supprime des capabilities Linux |
Checklist sécurité
Voici une checklist pour sécuriser un cluster Kubernetes :
Accès et authentification
- [ ] RBAC activé et configuré (principe du moindre privilège)
- [ ] ServiceAccounts dédiés par application
- [ ] Pas de token ServiceAccount monté par défaut (
automountServiceAccountToken: false)
Réseau
- [ ] Network Policies en place (deny by default)
- [ ] CNI supportant les Network Policies (Calico, Cilium)
- [ ] API Server accessible uniquement depuis des IPs autorisées
Workloads
- [ ] Pods exécutés en non-root
- [ ] Images de base minimales et à jour
- [ ] Scan de vulnérabilités des images (Trivy, Clair)
- [ ] Pas de containers privilégiés
Secrets
- [ ] Secrets chiffrés at-rest dans etcd
- [ ] Pas de secrets dans les variables d'environnement (préférer les volumes)
- [ ] Rotation régulière des secrets
Ressources
- [ ] ResourceQuota par namespace
- [ ] LimitRange avec des valeurs par défaut
- [ ] Requests et limits définis sur tous les pods
Monitoring
- [ ] Audit logs activés
- [ ] Alertes sur les événements de sécurité
- [ ] Monitoring des accès à l'API Server