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 →