Aller au contenu

Module 4d · Sécurité

Objectifs

  • Durcir un pod : securityContext, non-root, capabilities, lecture seule.
  • Cloisonner le réseau avec les NetworkPolicies (deny par défaut).
  • Aller plus loin sur le RBAC (least privilege, ServiceAccounts).
  • Connaître les bonnes pratiques images/secrets et savoir auditer (popeye).
  • Durée : ~1 h 15 · Pré-requis : Module 4c.

Durcir le pod - securityContext

Par défaut, un conteneur tourne souvent en root : à éviter.

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 10001
    fsGroup: 10001
  containers:
    - name: app
      image: monapp:1.0
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]          # on retire tout, on rajoute le strict nécessaire
Réglage Pourquoi
runAsNonRoot empêche l'exécution en root
readOnlyRootFilesystem le conteneur ne peut pas se modifier (limite la persistance d'un attaquant)
drop: ["ALL"] retire toutes les capabilities Linux superflues
allowPrivilegeEscalation: false bloque l'escalade via setuid
graph LR
    D[Par défaut<br/>root · toutes capabilities<br/>rootfs inscriptible] -->|securityContext| H[Durci<br/>non-root · 0 capability<br/>rootfs en lecture seule]

Chaque réglage retire un moyen d'agir à qui entrerait dans le conteneur : c'est de la défense en profondeur - aucune couche n'est parfaite, mais empilées elles réduisent la surface au strict nécessaire.

Cloisonner le réseau - NetworkPolicy

Par défaut, tout pod peut parler à tout pod. Une NetworkPolicy restreint ce trafic. Le motif sain : tout interdire, puis autoriser au cas par cas.

# Deny par défaut tout le trafic ENTRANT du namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}          # tous les pods
  policyTypes: ["Ingress"]
# Puis autoriser : seuls les pods "front" peuvent joindre les pods "api" sur 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: front-vers-api
spec:
  podSelector:
    matchLabels: { app: api }
  ingress:
    - from:
        - podSelector:
            matchLabels: { app: front }
      ports:
        - port: 8080
graph LR
    F[pod front] -->|autorisé : 8080| A[pod api]
    O[tout autre pod] -. bloqué .-> A

default-deny mure toutes les entrées vers api ; la règle front-vers-api rouvre un seul chemin (depuis front, sur 8080). On raisonne en liste blanche : ce qui n'est pas explicitement autorisé est refusé - l'inverse du réseau plat par défaut.

Le CNI doit supporter les NetworkPolicies

Les NetworkPolicies ne font effet que si le plugin réseau les implémente (Calico, Cilium…). Sur certains clusters de lab/vcluster, elles peuvent être ignorées silencieusement : teste toujours l'effet réel (cf. exercice).

RBAC - least privilege

Rappel du module 4, version sécurité : on donne le minimum.

  • Un ServiceAccount dédié par application (jamais default).
  • Un Role namespacé plutôt qu'un ClusterRole quand c'est possible.
  • On vérifie toujours :
kubectl auth can-i --list --as system:serviceaccount:demo:monapp
kubectl auth can-i delete pods --as system:serviceaccount:demo:monapp -n demo
:rbac        ← inspecter les règles RBAC dans k9s

Images & secrets

  • Images : tag fixe (pas latest), idéalement digest, base slim/distroless.
  • Scanner les images (Trivy, Grype) en CI avant déploiement.
  • Registre privé : un secret docker-registry par namespace, référencé par imagePullSecrets dans le pod (ou attaché au ServiceAccount). Oublié = ImagePullBackOff unauthorized - revois le module 3.
  • Secrets : jamais en clair dans un manifest ; un Secret k8s est seulement base64 (≠ chiffré) → chiffrer au repos (etcd) et/ou utiliser un gestionnaire externe (Sealed Secrets, Vault, SOPS).

Auditer le cluster - Popeye

Popeye (même auteur que k9s) n'est plus une vue de k9s : intégré autrefois via :popeye, il a été retiré à la refonte v0.50. C'est de nouveau un outil séparé, lancé au terminal (installé dans la sandbox).

popeye       # audit : pods sans limits, en root, RBAC trop large, images en latest…

C'est l'outil idéal pour mesurer l'effet des bonnes pratiques de ce module : on audite, on corrige, on ré-audite, le score monte.

Pratique

Le TP de ce module, dans le terminal de ta sandbox - une fois la lecture faite :

tp start 03d-rbac     # créer un ServiceAccount, Role + RoleBinding, vérifier au can-i (moindre privilège)
tp start ancrage-4d   # en autonomie ensuite : moindre privilège sur une autre ressource

fais la manip puis tp check (ou tp watch pour l'auto-validation) · tp hint / tp solution si tu bloques.

Pour t'entraîner librement à côté :

Exercice 4d.1 - Non-root

  1. Déploie un nginx standard, exec dedans, id → tu es root.
  2. Ajoute un securityContext runAsNonRoot: true + runAsUser : le pod refuse de démarrer (image nginx veut root). Comprends pourquoi, puis utilise nginxinc/nginx-unprivileged.

Exercice 4d.2 - Deny par défaut

  1. Déploie deux pods front et api (+ Service api). Vérifie que front joint api (wget).
  2. Applique default-deny-ingress : front ne joint plus api.
  3. Ajoute front-vers-api : la communication revient, uniquement depuis front.
  4. (Si rien ne change après le deny : ton CNI n'applique pas les NetworkPolicies - note-le.)

Exercice 4d.3 - Audit

Au terminal : lance popeye, corrige deux findings (ex. limits manquantes, image latest), ré-audite.

Ce qu'il faut retenir

  • Non-root + drop capabilities + rootfs en lecture seule = base du durcissement pod.
  • NetworkPolicy : deny par défaut, puis autoriser finement (si le CNI le supporte).
  • RBAC = least privilege, un ServiceAccount par app, on vérifie avec auth can-i.
  • Un Secret k8s n'est pas chiffré par défaut : prévoir chiffrement/gestionnaire externe.
  • popeye (au terminal) pour mesurer et progresser.

Quiz éclair

Teste-toi : réponds de tête, puis déplie.

1. Cite trois réglages de base pour durcir un pod.

runAsNonRoot, capabilities.drop: ["ALL"], readOnlyRootFilesystem (et allowPrivilegeEscalation: false).

2. Quel est le motif sain pour les NetworkPolicies ?

Deny par défaut, puis autoriser finement au cas par cas - à condition que le CNI supporte les NetworkPolicies (sinon elles sont ignorées silencieusement).

3. Un Secret Kubernetes est-il chiffré ?

Non, juste encodé en base64. La protection vient du RBAC (qui peut le lire) + chiffrement au repos (etcd) ou un gestionnaire externe (Sealed Secrets, Vault, SOPS).

➡️ Suite : Module 4e - Packaging & GitOps (Helm, ArgoCD)