La sécurité Kubernetes est devenue en 2026 une responsabilité partagée entre équipes Platform Engineering et équipes produit. Un développeur qui pousse ses propres manifests Kubernetes sans maîtriser les 7 chapitres de sécurité de base (images, SecurityContext, secrets, RBAC, NetworkPolicy, Pod Security Standards, resources) introduit des vulnérabilités qui ne seront ni détectées en pre-commit ni filtrées par un admission controller correctement configuré. Les incidents cloud-native documentés depuis 2022 (compromissions via IMDSv1, escalades via ServiceAccount cluster-admin, fuites de secrets via containers debug) confirment que l'origine est presque toujours applicative, pas infrastructurelle. Cet article détaille les 7 chapitres de sécurité Kubernetes pertinents pour un développeur, avec manifests YAML complets, check-list actionnable par PR et stack d'outils à installer en local.
Pourquoi la sécurité Kubernetes concerne les développeurs en 2026
Trois évolutions structurelles de la filière tech rendent la sécurité Kubernetes non-négociable pour tout développeur qui déploie ses propres workloads.
1. La convergence DevSecOps des responsabilités. Les équipes produit écrivent leurs manifests, Helm charts, Kustomize overlays directement, sans équipe ops intermédiaire. Une erreur de SecurityContext ou une absence de NetworkPolicy est un défaut du développeur.
2. Les modèles d'attaque cloud-native. Les incidents Capital One (2019, compromission via SSRF + IMDSv1), Codecov (2021, supply chain via image compromise), UnitedHealth (2024, ransomware via compromission d'identité via application), Solana (2022, npm malicious packages chaînant vers K8s) partent tous d'une compromission applicative et s'étendent via des privilèges K8s mal configurés.
3. Les exigences réglementaires. NIS 2 (transposée France fin 2024), DORA (janvier 2025), Cyber Resilience Act européen (applicable 2027) imposent de documenter les contrôles de sécurité applicatifs y compris la configuration Kubernetes. La non-conformité expose à des amendes et à des exclusions de marché.
1. Construire des images sécurisées
L'image container est la fondation. Une image mal construite annule tous les contrôles en aval.
Règles de base pour une image production-ready
- Image de base minimale : Distroless (gcr.io/distroless), Chainguard Images, ou Alpine si contrainte de size. Bannir Ubuntu/Debian en prod (1-2 GB, surface maximale).
- Multi-stage build pour séparer compilation et runtime.
- Pin de versions précises plutôt que
latestou tags flottants. - Utilisateur non-root via directive Dockerfile USER (UID supérieur à 1000).
- Minimisation des layers et des packages installés.
- Scan obligatoire en CI via Trivy, Grype ou Docker Scout.
- Signature des images via Cosign (SigStore, keyless OIDC).
Exemple Dockerfile multi-stage distroless (Node.js)
# Stage 1 : build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2 : runtime distroless
FROM gcr.io/distroless/nodejs20-debian12:nonroot
WORKDIR /app
COPY --from=builder --chown=nonroot:nonroot /app/dist ./dist
COPY --from=builder --chown=nonroot:nonroot /app/node_modules ./node_modules
EXPOSE 3000
USER nonroot
ENTRYPOINT ["node", "dist/server.js"]Scan en CI avec Trivy
# Scan de l'image avant push registry
trivy image --severity HIGH,CRITICAL \
--exit-code 1 \
--ignore-unfixed \
my-app:${GIT_SHA}
# Génération du SBOM pour supply chain compliance
syft my-app:${GIT_SHA} -o cyclonedx-json > sbom.cyclonedx.json
# Signature keyless avec Cosign (GitHub Actions OIDC)
cosign sign --yes my-registry.io/my-app:${GIT_SHA}2. Configurer correctement les Pods (SecurityContext)
Le SecurityContext Kubernetes impose les restrictions Linux sur le conteneur. Les valeurs par défaut sont permissives, il faut les durcir explicitement.
SecurityContext complet recommandé
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
team: backend
environment: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
# Pod-level SecurityContext
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
serviceAccountName: my-app-sa
automountServiceAccountToken: false
containers:
- name: my-app
image: my-registry.io/my-app:v1.2.3@sha256:abc123
imagePullPolicy: Always
# Container-level SecurityContext
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
ports:
- containerPort: 3000
name: http
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/.cache
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: http
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}Explication des champs critiques
| Champ | Effet | Pourquoi |
|---|---|---|
| runAsNonRoot: true | Refuse démarrage en root | Défense n°1 contre escalades |
| runAsUser: 1000 | UID non-root explicite | Complète runAsNonRoot |
| readOnlyRootFilesystem: true | FS en lecture seule | Neutralise 80 % des malwares post-RCE |
| allowPrivilegeEscalation: false | Bloque setuid | Empêche escalation via binaries |
| capabilities.drop: ALL | Retire toutes les capabilities | Principe du moindre privilège |
| seccompProfile.type: RuntimeDefault | Filtre syscalls par défaut | Réduit surface kernel exposée |
| automountServiceAccountToken: false | Pas de token auto-injecté | Sauf si ServiceAccount utilisé |
| image pinned by digest | Image immuable | Prévient pull d'une image compromise |
3. Gérer les secrets sans jamais les committer
Les secrets hardcodés en base64 dans un manifest committé sont un classique des audits AppSec. Quatre patterns acceptables en 2026.
Pattern 1 — Secrets K8s natifs avec encryption at rest
Le minimum viable : utiliser Secret Kubernetes mais activer l'EncryptionConfiguration côté API server (empêche lecture depuis etcd). Rotation manuelle.
Pattern 2 — External Secrets Operator (ESO)
Opérateur CNCF qui synchronise les secrets depuis un coffre externe vers K8s.
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: my-app
spec:
provider:
vault:
server: "https://vault.internal:8200"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "my-app-reader"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: my-app-secret
namespace: my-app
spec:
refreshInterval: "1h"
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: my-app-credentials
creationPolicy: Owner
data:
- secretKey: DATABASE_PASSWORD
remoteRef:
key: my-app/production
property: db_passwordPattern 3 — Secrets Store CSI Driver
Monte les secrets comme fichiers depuis le coffre externe, sans créer de Secret K8s dans etcd. Moins de surface d'attaque, pratique pour les workloads ultra-sensibles.
Pattern 4 — Vault Agent Injector sidecar
HashiCorp Vault injecte un sidecar qui gère le fetch et la rotation des secrets. Lourd mais puissant.
Règles de développeur
- Jamais de secret en clair dans un manifest.
- Jamais de secret en variable d'environnement si l'application peut lire des fichiers (les env sont leakées via /proc).
- Rotation automatique configurée au niveau du coffre.
- Secrets par environnement (dev/staging/prod), jamais partagés.
- Scan des repos avec gitleaks ou trufflehog pré-commit et en CI.
4. RBAC et comptes de service au juste-privilège
Chaque workload doit avoir son propre ServiceAccount avec les droits strictement nécessaires, pas plus.
Exemple RBAC correct : application qui lit des ConfigMaps dans son namespace
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-app
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: my-app
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-app-config"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: my-app
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: my-app
roleRef:
kind: Role
name: my-app-role
apiGroup: rbac.authorization.k8s.ioAnti-patterns à éviter absolument
| Anti-pattern | Impact |
|---|---|
| Utiliser ServiceAccount default | Impossible d'auditer, privilèges partagés |
| ClusterRoleBinding vers cluster-admin | Prise de contrôle totale si pod compromis |
| Role avec verbs: [""] et resources: [""] | Équivaut à admin namespace |
| automountServiceAccountToken: true sans besoin | Token K8s exposé dans pod |
| Role trop large « au cas où » | Élargit la surface d'exploitation |
Outils pour auditer les RBAC existants
- rbac-lookup : recherche qui a quels droits sur quelle ressource.
- rakkess : matrice d'accès pour une ressource donnée.
- krane : analyse statique RBAC avec scoring de risque.
- kube-score : scan général incluant vérifications RBAC.
5. NetworkPolicy : segmenter par défaut
Par défaut, tout pod Kubernetes peut communiquer avec tous les autres pods du cluster. C'est un design permissif hérité. En 2026, tout namespace de production doit avoir au minimum une NetworkPolicy de default-deny.
NetworkPolicy de default-deny + allow-list
# Étape 1 : bloquer tout trafic entrant et sortant par défaut
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: my-app
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Étape 2 : autoriser le trafic entrant depuis l'Ingress Controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress
namespace: my-app
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 3000
---
# Étape 3 : autoriser le trafic sortant vers DNS et la base de données
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-essentials
namespace: my-app
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: database
podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432Choix du CNI pour enforcement
Le CNI (Container Network Interface) doit supporter NetworkPolicy pour que les règles soient effectivement appliquées.
| CNI | Support NetworkPolicy | Remarques |
|---|---|---|
| Cilium | Oui, étendu (CiliumNetworkPolicy L7) | Leader CNCF, eBPF, identity-based |
| Calico | Oui, complet | Mature, GlobalNetworkPolicy |
| Weave Net | Partiel | Legacy |
| Flannel | Non seul | Nécessite Calico en surcouche |
Recommandation 2026 : Cilium pour les nouveaux clusters (eBPF, observabilité Hubble, identity-based L7). Calico pour les clusters historiques qui fonctionnent.
6. Pod Security Standards et admission control
Depuis Kubernetes 1.25, les Pod Security Policies (PSP) sont supprimées. Les remplacements à connaître.
Pod Security Admission (PSA) — natif depuis 1.23
Trois profils officiels :
| Profil | Usage | Contrôles |
|---|---|---|
| privileged | Non restrictif | Aucun, pour workloads système |
| baseline | Empêche escalades connues | Pas de privileged, hostNetwork, hostPath, capabilities risquées |
| restricted | Hardening sévère | runAsNonRoot, readOnlyRootFilesystem, drop ALL capabilities, seccomp RuntimeDefault |
Activation par label de namespace
apiVersion: v1
kind: Namespace
metadata:
name: my-app
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.29
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedCombinaison avec Kyverno ou OPA Gatekeeper pour règles custom
PSA couvre les baselines standards. Kyverno ou OPA Gatekeeper complètent avec les règles spécifiques à l'organisation (registry whitelist, labels obligatoires, quotas custom, conformité sectorielle).
7. Resources limits et Quality of Service
Les resources requests et limits ne sont pas qu'une question de performance : elles sont une mesure de sécurité contre les DoS internes et les crypto-miners introduits par compromission.
Trois classes de QoS Kubernetes
| Classe QoS | Conditions | Priorité eviction |
|---|---|---|
| Guaranteed | requests = limits sur CPU et memory | La moins évincée |
| Burstable | Au moins un request défini, différent du limit | Moyenne |
| BestEffort | Aucun request ni limit | La première évincée |
Recommandation 2026
- Production : Guaranteed ou Burstable avec limits définis.
- Dev/staging : Burstable acceptable.
- Éviter BestEffort en production : imprévisibilité, vulnérabilité DoS, difficile à capacité-planifier.
Example snippet à systématiser
resources:
requests:
cpu: 100m
memory: 128Mi
ephemeral-storage: 100Mi
limits:
cpu: 500m
memory: 512Mi
ephemeral-storage: 500MiNote : le limit memory déclenche un OOMKill si dépassé. Le limit CPU throttle l'application (ne tue pas). Dimensionner mémoire 2-3x le steady-state normal.
Check-list PR-ready pour un développeur
À systématiser dans chaque PR qui touche des manifests Kubernetes.
- Image depuis un registry autorisé, pin par digest ou tag immuable (jamais latest).
- Image de base minimale : distroless, Chainguard, ou Alpine avec justification.
- Multi-stage build pour séparer build et runtime.
- USER non-root dans Dockerfile (USER 1000 ou supérieur).
- Scan Trivy en CI avec échec sur CRITICAL/HIGH non-fixés.
- Cosign signature keyless via GitHub Actions OIDC.
- runAsNonRoot: true et runAsUser défini.
- readOnlyRootFilesystem: true avec emptyDir pour chemins d'écriture légitimes.
- allowPrivilegeEscalation: false.
- capabilities.drop: ALL, ajout explicite si nécessaire uniquement.
- seccompProfile.type: RuntimeDefault.
- ServiceAccount dédié par workload, automountServiceAccountToken: false par défaut.
- Role ou ClusterRole avec verbs et resources au juste nécessaire.
- NetworkPolicy de default-deny + allow-list explicite.
- Secrets via External Secrets Operator ou Secrets Store CSI, jamais en clair dans le manifest.
- Secrets montés comme fichiers plutôt que variables d'environnement si possible.
- resources.requests et resources.limits définis pour CPU et memory.
- livenessProbe et readinessProbe configurées.
- Labels organisationnels obligatoires : app, team, environment, version.
- Namespace avec Pod Security Admission label enforce au niveau adéquat (baseline en dev, restricted en prod).
Outils à avoir en local et en CI
Stack d'outils gratuits à installer en local pour un développeur K8s.
| Outil | Rôle | Installation |
|---|---|---|
| kubectl | CLI officielle | brew install kubectl |
| kind ou minikube | Cluster local | brew install kind |
| kube-linter | Best practices static | brew install kube-linter |
| kubeconform | Validation schema OpenAPI | brew install kubeconform |
| Trivy | Scan images + manifests + IaC | brew install trivy |
| Polaris | Audit + dashboard | brew install FairwindsOps/tap/polaris |
| Kubescape | CIS + NSA/CISA benchmarks | brew install kubescape |
| kube-score | Best practices scoring | brew install kube-score |
| Stern | Multi-pod logs | brew install stern |
| kubectl-neat | Clean manifest output | kubectl krew install neat |
Pipeline pre-commit typique
#!/bin/bash
set -e
echo "Schema validation..."
kubeconform -strict -ignore-missing-schemas manifests/
echo "Best practices lint..."
kube-linter lint manifests/
echo "Security scan..."
trivy config --severity HIGH,CRITICAL manifests/
echo "Image scan..."
trivy image --severity HIGH,CRITICAL \
--exit-code 1 \
--ignore-unfixed \
$(yq '.spec.template.spec.containers[].image' manifests/*.yaml | head -1)
echo "Best practices scoring..."
kube-score score manifests/
echo "All checks passed."Pièges courants
Désactiver readOnlyRootFilesystem parce que l'application crashe. La correction n'est pas de désactiver : ajouter des emptyDir ciblés pour les chemins d'écriture légitimes.
Copier un SecurityContext de tuto sans adapter. Les tutos donnent un baseline. runAsUser: 1000 peut nécessiter un chown dans l'image si le FS attend un autre UID. Tester systématiquement.
Committer des Secrets K8s encodés base64 en pensant qu'ils sont chiffrés. Base64 n'est pas du chiffrement. Scanner pré-commit avec gitleaks obligatoire.
Utiliser automountServiceAccountToken: true par défaut. Si l'application n'appelle pas l'API K8s, désactiver. Réduit la surface en cas de compromission.
Ignorer les NetworkPolicy parce que « le cluster n'est pas exposé ». Tout cluster a des menaces internes (compromission d'un pod, mouvement latéral). NetworkPolicy est défense en profondeur essentielle.
Confondre requests et limits. Requests = réservation minimale pour le scheduling. Limits = plafond max. BestEffort (aucun défini) est le QoS le moins sécurisé.
Utiliser le namespace default pour la production. Zéro policies, zéro isolation. Toujours créer un namespace dédié par application avec labels appropriés.
Déployer sans livenessProbe ni readinessProbe. L'application continue à recevoir du trafic même en panne. Attaquant peut exploiter une instance dans un état dégradé prévisible.
Points clés à retenir
- 7 chapitres obligatoires : images, SecurityContext, secrets, RBAC, NetworkPolicy, Pod Security Standards, resources.
- Image distroless ou Chainguard en prod, Alpine en fallback, Ubuntu/Debian bannis.
- SecurityContext durci systématiquement : runAsNonRoot, readOnlyRootFilesystem, drop ALL capabilities, seccomp RuntimeDefault.
- Secrets via External Secrets Operator ou Secrets Store CSI — jamais en clair dans les manifests.
- ServiceAccount dédié par workload, RBAC au juste privilège, automountServiceAccountToken: false par défaut.
- NetworkPolicy de default-deny + allow-list, CNI Cilium ou Calico pour enforcement effectif.
- PSA restricted en prod, combiné à Kyverno ou OPA Gatekeeper pour règles custom.
- Resources requests et limits définis, QoS Guaranteed ou Burstable en prod.
- Stack outils local : kubectl, kind, kube-linter, kubeconform, Trivy, Polaris, Kubescape — 3-5 secondes par validation.
- Check-list de 20 points applicable à chaque PR qui touche un manifest Kubernetes.
Pour aller plus loin
- OPA Open Policy Agent : définition, Rego et cas d'usage 2026 — zoom sur l'admission control avancé au-delà de PSA.
- Outils DevSecOps à connaître en 2026 — stack complète dont les outils K8s cités ici.
- Roadmap DevSecOps 2026 — parcours d'apprentissage structuré couvrant ces thématiques.
- Roadmap Cloud Security 2026 — extension vers la sécurité cloud et CNAPP.
- Secure coding : définition, principes et référentiels 2026 — principes applicatifs complémentaires à la sécurité Kubernetes.
- Devenir DevSecOps sans expérience — pillar catégorie, point d'entrée global.







