☸️ Partie 5 : Sécurité & Bonnes Pratiques
RBAC, Service Accounts, NetworkPolicies, Helm, Probes (Liveness/Readiness), Autoscaling (HPA) & Quotas.
1. Sécurité : Vue d'ensemble
Les "4C" (Cloud, Cluster, Container, Code). Authentification (kubeconfig) vs Autorisation (RBAC).
2. Sécurité : RBAC (Autorisation)
Role-Based Access Control. Qui (Subject) peut faire Quoi (Verbs) sur Quoi (Resources).
3. Sécurité : ServiceAccount
L'identité d'un Pod (Subject) pour le RBAC. (Pour les Pods qui parlent à l'API K8s).
4. Sécurité : NetworkPolicy
Le "Firewall" L3/L4 des Pods. (Défaut : Allow All). (Nécessite Calico/Cilium).
5. Sécurité : Secrets (Deep Dive)
Base64 (non chiffré !). Risques (etcd, env vars). Solutions (Vault, SealedSecrets).
6. Pratique : Helm & Kustomize
Le "templating" (modèles) de YAML (Helm) vs le "patching" (superposition) (Kustomize).
7. Pratique : Probes (Santé)
Liveness (Mort ? -> Restart) vs Readiness (Prêt ? -> Envoie trafic).
LivenessProbe ReadinessProbe8. Pratique : Autoscaling
HPA (Pods, CPU/Mem), VPA (Taille Pods), Cluster Autoscaler (Nœuds).
HPA Autoscaling9. Pratique : Quotas & Limites
LimitRange (Défaut Pod) vs ResourceQuota (Plafond Namespace). requests vs limits.
La sécurité K8s est un modèle en "couches de défense" (Defense in Depth), souvent appelé les "4C" (Cloud, Cluster, Container, Code).
| Couche | Composant | Menace / Responsabilité |
|---|---|---|
| Cloud (ou DataCenter) | Réseau VPC, IAM, Sécurité de l'Hôte (VM). | Accès physique/réseau aux machines (Nœuds). |
| Cluster | API Server, etcd, Kubelet. | Qui peut parler à l'API ? (AuthN/AuthZ). (Le focus de cette page). |
| Container | Image (Dockerfile), Registre. | Vulnérabilités (CVEs) dans l'image (ex: log4j). |
| Code | Application (Python, Java...). | Injection SQL, XSS. (Ex: DataDog ASM). |
Authentification (AuthN) vs Autorisation (AuthZ)
- Authentification (Qui es-tu ?) : L'API Server vérifie l'identité. Pour un humain :
~/.kube/config(Certificat, Token). Pour un Pod :ServiceAccount(Token). - Autorisation (Qu'as-tu le droit de faire ?) : RBAC. (Ex: "L'User 'Bob' est authentifié, mais a-t-il le droit de lister les Secrets ?").
RBAC est le mécanisme d'autorisation "par défaut" (et obligatoire). Il répond à la question : Qui (Subject) peut faire Quoi (Verbs) sur Quoi (Resources) ?
Il utilise 4 objets :
- Subject :
User(Humain),Group, ouServiceAccount(Pod). Role: Une liste de permissions (Verbs+Resources) **dans** un Namespace. (Ex: "Role 'pod-reader' autorise 'get', 'list' sur 'pods'").ClusterRole: UnRoleglobal (pour *tous* les Namespaces, ou pour des objets "cluster" comme les Nœuds).RoleBinding/ClusterRoleBinding: Le "lien" qui **attache** unSubjectà unRole. (Ex: "Lie 'User Bob' au Role 'pod-reader'").
Diagramme (RBAC)
+-----------+ +---------------------+
| Sujet | | Role (Permissions) |
| (Ex: User "Bob" ou | (Ex: "get", "list" |
| ServiceAccount) | sur "pods") |
+-----------+ +---------------------+
| |
| (Attaché via) |
▼ ▼
+---------------------------------+
| RoleBinding (Le "Lien") |
| (Attache "Bob" au "Role") |
+---------------------------------+
Exemple YAML (role-dev.yaml)
# 1. Le Role (Les permissions)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: prod
name: pod-reader-role
rules:
- apiGroups: [""] # ("" = Core API Group)
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"] # (Peut lire, mais pas 'create' ou 'delete')
---
# 2. Le Lien (Le "Binding")
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: bob-read-pods-binding
namespace: prod
subjects: # (Le "Qui ?")
- kind: User
name: "bob@mon-entreprise.com" # (Nom de l'utilisateur (défini par l'AuthN))
apiGroup: rbac.authorization.k8s.io
roleRef: # (Le "Quoi ?")
kind: Role
name: pod-reader-role # (Nom du Role ci-dessus)
apiGroup: rbac.authorization.k8s.io
Un ServiceAccount (SA) est l'**identité** d'un Pod. C'est le "User" (Sujet) pour les processus *internes* au cluster.
Cas d'usage : Un Pod (ex: DataDog Agent, ou votre propre app) a besoin de parler à l'API Server pour lister d'autres Pods.
Par défaut, chaque Pod utilise le default ServiceAccount (qui n'a (presque) aucun droit).
Workflow (Ex: Pod "datadog-agent")
- (Admin) Crée un
ServiceAccount:kubectl create sa datadog-sa. - (Admin) Crée un
ClusterRole: (ex: "datadog-role" qui peut "get", "list" les "pods", "nodes"). - (Admin) Crée un
ClusterRoleBinding: Liedatadog-saàdatadog-role. - (Dev/Admin) Assigne le SA au Pod : Dans le YAML du Pod (ou Deployment/DaemonSet), on ajoute :
spec.serviceAccountName: datadog-sa. - (K8s) : K8s monte automatiquement un "Token" (JWT) (dans
/var/run/secrets/...) dans le Pod. - (Pod) : L'Agent DataDog utilise ce Token pour s'authentifier (AuthN) auprès de l'API Server. Le RBAC (AuthZ) autorise l'action.
Par défaut, K8s a un réseau "flat" (plat) : **tous les Pods peuvent parler à tous les Pods** (zéro-confiance non-activée).
Une NetworkPolicy (NetPol) est un "Firewall" L3/L4 (règles IP/Port) pour les Pods. (Nécessite un CNI compatible, ex: Calico, Cilium. Flannel ne les supporte pas).
Dès qu'une NetPol cible un Pod (via podSelector), ce Pod devient **"Deny All"** (isolé) et n'accepte que le trafic explicitement autorisé (ingress/egress).
Exemple YAML (Isoler un Backend)
"Le Pod api-backend (dans le namespace prod) ne doit accepter du trafic (ingress) que des Pods app:frontend sur le port 8080."
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-backend-policy
namespace: prod
spec:
# 1. Quels Pods sont ciblés (isolés) ?
podSelector:
matchLabels:
app: api-backend
policyTypes:
- Ingress # (On applique des règles 'Ingress' (entrantes))
# 2. Règles 'Ingress'
ingress:
- from:
# (Autoriser les Pods qui ont ce label)
- podSelector:
matchLabels:
app: frontend
ports:
# (Sur ce port)
- protocol: TCP
port: 8080
Un Secret est identique à un ConfigMap (un dictionnaire), mais il est conçu pour les données **sensibles** (API_KEY, DB_PASSWORD, certif-tls.key).
Le "Piège" du Base64
Attention : Par défaut, les Secrets sont stockés dans etcd en Base64. Ce n'est **PAS DU CHIFFREMENT**, c'est un simple encodage.
$ echo "mon-password" | base64 bW9uLXBhc3N3b3Jk $ echo "bW9uLXBhc3N3b3Jk" | base64 --decode mon-password
N'importe qui ayant accès à etcd ou kubectl get secret -o yaml peut lire vos mots de passe.
Solutions (Bonnes Pratiques)
- Chiffrement "At-Rest" : Configurer K8s pour chiffrer les Secrets dans
etcd(Encryption at Rest). - RBAC (Minimum) : N'autoriser (via RBAC) que les administrateurs (et les
ServiceAccountsnécessaires) àget/listles Secrets. - Secrets Externes (Meilleur) : Ne pas stocker les secrets dans K8s. Utiliser un "provider" externe (ex: HashiCorp Vault, AWS Secrets Manager) et un injecteur (ex: "External Secrets Operator") qui les monte dans le Pod.
Écrire 50 fichiers YAML (Deployment, Service, Ingress, ConfigMap...) est ingérable. On utilise des "gestionnaires de paquets".
Helm (Le "Gestionnaire de Paquets")
Helm est le "apt" ou "npm" de K8s. Il "package" une application (ex: DataDog Agent, Postgres) en un Chart (un dossier).
Un "Chart" contient des **templates** (modèles) de YAML (avec des variables {{ .Values.imageTag }}) et un fichier values.yaml (où vous définissez vos variables (ex: imageTag: 1.5.0)).
# (Installer DataDog (le Chart) avec Helm,
# en surchargeant la "value" apiKey)
$ helm install datadog-agent \
--set datadog.apiKey="VOTRE_API_KEY" \
datadog/datadog
Kustomize (Le "Patching")
Kustomize (intégré dans kubectl) gère les YAMLs par "patching" (superposition), sans "templating" (moustaches {{}}).
On définit une base (YAML commun) et des overlays (ex: prod, staging) qui "patchent" (modifient) la base (ex: prod change replicas: 10).
# (Appliquer le "patch" 'prod') $ kubectl apply -k overlays/production
Problème : K8s sait si le *processus* (PID 1) tourne, mais il ne sait pas si votre application (ex: une API Flask) est "bloquée" (deadlock) ou "en cours de démarrage".
Solution : Les Probes (sondes). Le kubelet interroge votre Pod.
| Probe (Sonde) | Question | Action (si échec) | Cas d'usage |
|---|---|---|---|
LivenessProbe (Survie) | "Es-tu vivant (non-bloqué) ?" | Restart (Tuer et Redémarrer) le Pod. | Détecter un deadlock, une corruption mémoire. |
ReadinessProbe (Prêt) | "Es-tu prêt à recevoir du trafic ?" | Retirer le Pod de la liste du Service (load balancer). | Attendre que l'app finisse de charger (ex: cache, connexion DB). |
StartupProbe | "As-tu démarré ?" (1 fois) | (Désactive les 2 autres sondes pendant le (long) démarrage). | Applications Java/Spring (lentes à démarrer). |
Exemple YAML (dans le Deployment)
# (spec.template.spec.containers[0])
spec:
containers:
- name: my-api
image: ...
# 1. Sonde de "Survie" (Liveness)
# (Si /healthz échoue 3x, Kubelet TUE le Pod)
livenessProbe:
httpGet:
path: /healthz # (Un endpoint qui dit "OK 200")
port: 8080
initialDelaySeconds: 15 # (Attendre 15s avant de sonder)
periodSeconds: 20 # (Sonder toutes les 20s)
failureThreshold: 3
# 2. Sonde de "Prêt" (Readiness)
# (Si /ready échoue, Kubelet RETIRE le Pod du Service)
readinessProbe:
httpGet:
path: /ready # (Un endpoint qui vérifie la DB)
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
L'Autoscaling est la capacité de K8s à s'adapter (scaler) à la charge (CPU/Mem).
| Type | Objet | Action | Basé sur |
|---|---|---|---|
HPA (Horizontal) | HorizontalPodAutoscaler | Ajoute/Supprime des Pods (Répliques). | CPU / Mémoire (Métriques). |
VPA (Vertical) | VerticalPodAutoscaler | Augmente/Diminue la taille (requests/limits) d'un Pod. | Utilisation (Historique). |
CA (Cluster) | ClusterAutoscaler | Ajoute/Supprime des Nœuds (VMs). | Pods Pending (en attente). |
Exemple YAML (hpa.yaml - Le plus courant)
"Pour le Deployment 'my-api', garder entre 3 et 10 Pods. Scaler (ajouter des Pods) si le CPU moyen dépasse 80%."
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-api-hpa
spec:
scaleTargetRef: # (La Cible)
apiVersion: apps/v1
kind: Deployment
name: my-api-deployment
minReplicas: 3
maxReplicas: 10
metrics: # (Les Déclencheurs)
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80 # (80% CPU)
Problème : Un développeur déploie une app (dans dev) sans limites de CPU/RAM. L'app a une fuite mémoire, consomme 100% du Nœud, et fait tomber les Pods de prod (sur le même Nœud).
requests vs limits (Dans le Pod/Deployment)
C'est la configuration **la plus importante** d'un Pod (QoS - Qualité de Service).
requests(Réservation) : "Je **garantis** (réserve) ce minimum." (Utilisé par le Scheduler pour placer le Pod).limits(Plafond) : "Je **ne dois jamais dépasser** ce maximum." (Si CPU dépasse, il est "throttled" (ralenti). Si RAM dépasse, il est "OOMKilled" (tué)).
ResourceQuota (Plafond du Namespace)
Définit le "budget" total pour un Namespace (ex: "Le namespace dev ne peut pas utiliser plus de 100Gi de RAM au total").
LimitRange (Défaut du Pod)
Définit les requests/limits **par défaut** pour les Pods (dans un Namespace) qui n'en définissent pas. (Empêche les Pods "BestEffort" (sans limites)).
Exemple (requests & limits dans un Deployment)
# (spec.template.spec.containers[0])
spec:
containers:
- name: my-api
image: ...
resources:
# (Réservation)
requests:
memory: "256Mi"
cpu: "100m" # (10% d'1 vCPU)
# (Plafond)
limits:
memory: "512Mi"
cpu: "500m" # (50% d'1 vCPU)
