Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

☸️ Partie 5 : Sécurité & Bonnes Pratiques

RBAC, Service Accounts, NetworkPolicies, Helm, Probes (Liveness/Readiness), Autoscaling (HPA) & Quotas.

5.1 Moyen

1. Sécurité : Vue d'ensemble

Les "4C" (Cloud, Cluster, Container, Code). Authentification (kubeconfig) vs Autorisation (RBAC).

Sécurité 4C
5.2 Avancé

2. Sécurité : RBAC (Autorisation)

Role-Based Access Control. Qui (Subject) peut faire Quoi (Verbs) sur Quoi (Resources).

RBAC Role RoleBinding
5.3 Avancé

3. Sécurité : ServiceAccount

L'identité d'un Pod (Subject) pour le RBAC. (Pour les Pods qui parlent à l'API K8s).

ServiceAccount Identité (Pod)
5.4 Avancé

4. Sécurité : NetworkPolicy

Le "Firewall" L3/L4 des Pods. (Défaut : Allow All). (Nécessite Calico/Cilium).

NetworkPolicy Firewall
5.5 Moyen

5. Sécurité : Secrets (Deep Dive)

Base64 (non chiffré !). Risques (etcd, env vars). Solutions (Vault, SealedSecrets).

Secret Vault
5.6 Moyen

6. Pratique : Helm & Kustomize

Le "templating" (modèles) de YAML (Helm) vs le "patching" (superposition) (Kustomize).

Helm Kustomize
5.7 Avancé

7. Pratique : Probes (Santé)

Liveness (Mort ? -> Restart) vs Readiness (Prêt ? -> Envoie trafic).

LivenessProbe ReadinessProbe
5.8 Avancé

8. Pratique : Autoscaling

HPA (Pods, CPU/Mem), VPA (Taille Pods), Cluster Autoscaler (Nœuds).

HPA Autoscaling
5.9 Moyen

9. Pratique : Quotas & Limites

LimitRange (Défaut Pod) vs ResourceQuota (Plafond Namespace). requests vs limits.

ResourceQuota requests limits
5.1 Sécurité : Vue d'ensemble (Les "4C")

La sécurité K8s est un modèle en "couches de défense" (Defense in Depth), souvent appelé les "4C" (Cloud, Cluster, Container, Code).

CoucheComposantMenace / Responsabilité
Cloud (ou DataCenter)Réseau VPC, IAM, Sécurité de l'Hôte (VM).Accès physique/réseau aux machines (Nœuds).
ClusterAPI Server, etcd, Kubelet.Qui peut parler à l'API ? (AuthN/AuthZ). (Le focus de cette page).
ContainerImage (Dockerfile), Registre.Vulnérabilités (CVEs) dans l'image (ex: log4j).
CodeApplication (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 ?").
5.2 Sécurité : RBAC (Role-Based Access Control)

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 :

  1. Subject : User (Humain), Group, ou ServiceAccount (Pod).
  2. Role : Une liste de permissions (Verbs+Resources) **dans** un Namespace. (Ex: "Role 'pod-reader' autorise 'get', 'list' sur 'pods'").
  3. ClusterRole : Un Role global (pour *tous* les Namespaces, ou pour des objets "cluster" comme les Nœuds).
  4. RoleBinding / ClusterRoleBinding : Le "lien" qui **attache** un Subject à un Role. (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
5.3 Sécurité : ServiceAccount (Identité du Pod)

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")
  1. (Admin) Crée un ServiceAccount : kubectl create sa datadog-sa.
  2. (Admin) Crée un ClusterRole : (ex: "datadog-role" qui peut "get", "list" les "pods", "nodes").
  3. (Admin) Crée un ClusterRoleBinding : Lie datadog-sa à datadog-role.
  4. (Dev/Admin) Assigne le SA au Pod : Dans le YAML du Pod (ou Deployment/DaemonSet), on ajoute : spec.serviceAccountName: datadog-sa.
  5. (K8s) : K8s monte automatiquement un "Token" (JWT) (dans /var/run/secrets/...) dans le Pod.
  6. (Pod) : L'Agent DataDog utilise ce Token pour s'authentifier (AuthN) auprès de l'API Server. Le RBAC (AuthZ) autorise l'action.
5.4 Sécurité : NetworkPolicy (Firewall)

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
5.5 Sécurité : Secrets (Deep Dive)

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 ServiceAccounts nécessaires) à get/list les 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.
6. Pratique : Helm & Kustomize (Gestion de YAMLs)

É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
7. Pratique : Probes (Liveness & Readiness)

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)QuestionAction (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
8. Pratique : Autoscaling

L'Autoscaling est la capacité de K8s à s'adapter (scaler) à la charge (CPU/Mem).

TypeObjetActionBasé sur
HPA (Horizontal)HorizontalPodAutoscalerAjoute/Supprime des Pods (Répliques).CPU / Mémoire (Métriques).
VPA (Vertical)VerticalPodAutoscalerAugmente/Diminue la taille (requests/limits) d'un Pod.Utilisation (Historique).
CA (Cluster)ClusterAutoscalerAjoute/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)
9. Pratique : Quotas & Limites (Gouvernance)

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)