← Retour aux tutoriels

Kubernetes — NetworkPolicy, securityContext et Pod Security Admission

Durcissement de charges Kubernetes : NetworkPolicy default-deny, securityContext non-root et capabilities, et application des standards Pod Security Admission.

Durcissement des charges Kubernetes par isolation réseau, contexte de sécurité des conteneurs et admission Pod Security.

Contexte

Par défaut, tous les pods d’un cluster communiquent librement, s’exécutent fréquemment en root et conservent des capabilities Linux superflues. Un conteneur compromis sans isolation réseau ni restriction de privilège facilite le mouvement latéral et l’évasion. Le durcissement repose sur trois piliers : NetworkPolicy, securityContext et Pod Security Admission.

Détection

Namespaces dépourvus de NetworkPolicy (communication non restreinte) :

for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
  count=$(kubectl get netpol -n "$ns" --no-headers 2>/dev/null | wc -l)
  [ "$count" -eq 0 ] && echo "SANS NETPOL: $ns"
done

Pods s’exécutant en root ou en privileged :

kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[].securityContext.privileged==true or (.spec.securityContext.runAsNonRoot != true)) | "\(.metadata.namespace)/\(.metadata.name)"'

Niveau Pod Security appliqué par namespace :

kubectl get ns -o custom-columns='NS:.metadata.name,ENFORCE:.metadata.labels.pod-security\.kubernetes\.io/enforce'

Durcissement

NetworkPolicy default-deny en entrée, puis autorisation explicite du trafic légitime :

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: app-prod
spec:
  podSelector: {}
  policyTypes: [Ingress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-ingress
  namespace: app-prod
spec:
  podSelector:
    matchLabels: { app: web }
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels: { kubernetes.io/metadata.name: traefik }
      ports:
        - { protocol: TCP, port: 8080 }

securityContext durci au niveau pod et conteneur (non-root, système de fichiers en lecture seule, suppression de toutes les capabilities) :

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 10001
    fsGroup: 10001
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: web
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]

Application du standard restricted de Pod Security Admission au namespace :

kubectl label ns app-prod \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted \
  pod-security.kubernetes.io/audit=restricted --overwrite

Vérification

Confirmation que l’admission rejette un pod privileged dans le namespace durci :

kubectl run test-priv --image=busybox -n app-prod --privileged --restart=Never -- sleep 1 2>&1 | grep -i 'violates PodSecurity'

Test de l’isolation réseau (une connexion non autorisée doit échouer) :

kubectl run probe -n app-prod --image=nicolaka/netshoot --restart=Never --rm -it -- \
  curl -s --max-time 3 http://autre-service.autre-ns.svc.cluster.local || echo "BLOQUE (attendu)"

Vérification que les pots tournent en non-root :

kubectl exec -n app-prod deploy/web -- id -u

La sortie attendue est l’UID 10001, le rejet d’admission pour le pod privileged et l’échec de la connexion réseau non autorisée.

Vous avez un projet sur ces sujets ?

Nous contacter →