Retour au blogSécuriser Kubernetes en Production : Best Practices 2024 Guide complet pour durcir la sécurité de vos clusters K8s : RBAC, Network Policies, Pod Security Standards.
Partager
📋 Vue d'ensemble rapide des sujets traités dans cet article
Cliquez sur les sections ci-dessous pour naviguer rapidement
Sécuriser Kubernetes en Production : Best Practices 2024
La sécurité Kubernetes est un défi majeur en production. Avec l'adoption massive des conteneurs et l'orchestration, les surfaces d'attaque se multiplient. Ce guide présente les stratégies essentielles pour durcir vos clusters contre les menaces modernes et établir une posture de sécurité robuste.
💡 Comprendre le modèle de sécurité Kubernetes
Les 4 C de la sécurité Cloud Native
La sécurité Kubernetes s'articule autour de quatre couches interdépendantes :
• Cloud/Cluster : Infrastructure et configuration du cluster
• Cluster : Composants Kubernetes (API Server, etcd, kubelet)
• Container : Images et runtime de conteneurs
• Code : Application et dépendances
Principe de défense en profondeur
Chaque couche doit être sécurisée indépendamment, créant plusieurs barrières contre les attaquants. Une compromission à un niveau ne doit pas permettre l'accès aux autres couches.
💡 Les Piliers de la Sécurité K8s
1. Authentification et Autorisation (RBAC)
Configuration RBAC granulaire
# ServiceAccount pour une application
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
namespace: production
annotations:
description: "Service account for production app with minimal permissions"
---
# Role avec permissions minimales (principe du moindre privilège)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: app-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
resourceNames: ["app-config"] # Restriction sur des ressources spécifiques
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
resourceNames: ["app-secrets"]
---
# RoleBinding pour lier le ServiceAccount au Role
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-role-binding
namespace: production
subjects:
- kind: ServiceAccount
name: app-service-account
namespace: production
roleRef:
kind: Role
name: app-role
apiGroup: rbac.authorization.k8s.io
ClusterRole pour permissions cross-namespace
# ClusterRole pour monitoring (lecture seule)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-reader
rules:
- apiGroups: [""]
resources: ["nodes", "nodes/metrics", "services", "endpoints", "pods"]
verbs: ["get", "list", "watch"]
- apiGroups: ["extensions", "apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics", "/metrics/cadvisor"]
verbs: ["get"]
---
# ClusterRoleBinding pour Prometheus
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus-monitoring
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: monitoring-reader
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
2. Pod Security Standards
Configuration des niveaux de sécurité
# Namespace avec Pod Security Standards stricts
apiVersion: v1
kind: Namespace
metadata:
name: secure-production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/enforce-version: latest
Pod sécurisé avec SecurityContext
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
namespace: secure-production
spec:
replicas: 3
selector:
matchLabels:
app: secure-app
template:
metadata:
labels:
app: secure-app
spec:
serviceAccountName: app-service-account
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:v1.2.3
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 10001
capabilities:
drop:
- ALL
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
volumeMounts:
- name: tmp-volume
mountPath: /tmp
- name: cache-volume
mountPath: /app/cache
volumes:
- name: tmp-volume
emptyDir: {}
- name: cache-volume
emptyDir: {}
💡 Network Policies avancées
Politique de déni par défaut
# Deny all ingress and egress by default
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Politique granulaire pour microservices
# Allow specific communication between microservices
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# Allow backend to database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-to-database
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
# Allow DNS resolution
- to: []
ports:
- protocol: UDP
port: 53
Politique pour accès externe contrôlé
# Allow egress to specific external services
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-apis
namespace: production
spec:
podSelector:
matchLabels:
app: api-client
policyTypes:
- Egress
egress:
# Allow HTTPS to external APIs
- to: []
ports:
- protocol: TCP
port: 443
# Allow DNS
- to: []
ports:
- protocol: UDP
port: 53
# Block all other egress
💡 Gestion des Secrets et chiffrement
External Secrets Operator avec HashiCorp Vault
# SecretStore configuration for Vault
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: production
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"
serviceAccountRef:
name: external-secrets-sa
---
# ExternalSecret to sync from Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: database-password
remoteRef:
key: myapp/database
property: password
- secretKey: api-key
remoteRef:
key: myapp/external-api
property: key
Chiffrement des données au repos (etcd)
# EncryptionConfiguration for etcd
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
💡 Scanning de Sécurité et Monitoring
Trivy pour scanner les vulnérabilités
# Trivy Operator configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: trivy-operator
namespace: trivy-system
data:
trivy.repository: "ghcr.io/aquasecurity/trivy"
trivy.tag: "latest"
trivy.severity: "CRITICAL,HIGH,MEDIUM"
trivy.ignoreUnfixed: "false"
trivy.timeout: "5m"
---
# VulnerabilityReport CRD example
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:
name: deployment-nginx
namespace: default
spec:
artifact:
repository: nginx
tag: "1.21"
Falco pour la détection d'anomalies runtime
# Falco configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: falco-config
namespace: falco
data:
falco.yaml: |
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/k8s_audit_rules.yaml
json_output: true
json_include_output_property: true
# Alerting channels
http_output:
enabled: true
url: "http://webhook-service:8080/alerts"
# Custom rules
custom_rules.yaml: |
- rule: Suspicious Network Activity
desc: Detect suspicious network connections
condition: >
spawned_process and
proc.name in (nc, ncat, netcat, socat) and
not container.image.repository in (debug-tools)
output: >
Suspicious network tool executed
(user=%user.name command=%proc.cmdline container=%container.name)
priority: WARNING
OPA Gatekeeper pour les politiques
# Gatekeeper ConstraintTemplate
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredsecuritycontext
spec:
crd:
spec:
names:
kind: K8sRequiredSecurityContext
validation:
properties:
runAsNonRoot:
type: boolean
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredsecuritycontext
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.runAsNonRoot
msg := "Container must run as non-root user"
}
---
# Constraint using the template
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredSecurityContext
metadata:
name: must-run-as-non-root
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
namespaces: ["production", "staging"]
parameters:
runAsNonRoot: true
💡 Sécurisation de l'API Server
Configuration sécurisée de l'API Server
# kube-apiserver configuration (via kubeadm)
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
extraArgs:
# Audit logging
audit-log-path: /var/log/audit.log
audit-log-maxage: "30"
audit-log-maxbackup: "10"
audit-log-maxsize: "100"
audit-policy-file: /etc/kubernetes/audit-policy.yaml
# Security settings
enable-admission-plugins: "NodeRestriction,PodSecurityPolicy,ResourceQuota,LimitRanger"
disable-admission-plugins: ""
# Encryption at rest
encryption-provider-config: /etc/kubernetes/encryption-config.yaml
# Anonymous access
anonymous-auth: "false"
# RBAC
authorization-mode: "Node,RBAC"
# Secure communication
tls-cipher-suites: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
tls-min-version: "VersionTLS12"
Politique d'audit complète
# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Log all requests at RequestResponse level
- level: RequestResponse
resources:
- group: ""
resources: ["secrets", "configmaps"]
# Log all authentication failures
- level: Request
users: ["system:anonymous"]
# Log all privilege escalation attempts
- level: RequestResponse
verbs: ["create", "update", "patch"]
resources:
- group: "rbac.authorization.k8s.io"
resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"]
# Log exec and portforward requests
- level: Request
verbs: ["create"]
resources:
- group: ""
resources: ["pods/exec", "pods/portforward"]
💡 Sécurisation des Nodes
Configuration sécurisée du kubelet
# kubelet-config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
anonymous:
enabled: false
webhook:
enabled: true
authorization:
mode: Webhook
serverTLSBootstrap: true
protectKernelDefaults: true
makeIPTablesUtilChains: true
eventRecordQPS: 0
readOnlyPort: 0
streamingConnectionIdleTimeout: 4h
Hardening du système d'exploitation
#!/bin/bash
# node-hardening.sh - Script de durcissement des nodes
# Désactiver les services non nécessaires
systemctl disable cups
systemctl disable avahi-daemon
systemctl disable bluetooth
# Configuration du firewall
ufw --force enable
ufw default deny incoming
ufw default allow outgoing
# Autoriser seulement les ports nécessaires
ufw allow 22/tcp # SSH
ufw allow 6443/tcp # Kubernetes API
ufw allow 10250/tcp # kubelet
ufw allow 10251/tcp # kube-scheduler
ufw allow 10252/tcp # kube-controller-manager
ufw allow 2379:2380/tcp # etcd
# Durcissement du kernel
cat >> /etc/sysctl.d/99-kubernetes-security.conf << EOF
# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 1
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Ignore ping requests
net.ipv4.icmp_echo_ignore_all = 1
EOF
sysctl -p /etc/sysctl.d/99-kubernetes-security.conf
💡 Monitoring et alerting de sécurité
Métriques de sécurité avec Prometheus
# ServiceMonitor for security metrics
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: security-metrics
namespace: monitoring
spec:
selector:
matchLabels:
app: security-exporter
endpoints:
- port: metrics
interval: 30s
path: /metrics
---
# PrometheusRule for security alerts
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: kubernetes-security-alerts
namespace: monitoring
spec:
groups:
- name: kubernetes.security
rules:
- alert: PodSecurityViolation
expr: increase(gatekeeper_violations_total[5m]) > 0
for: 0m
labels:
severity: warning
annotations:
summary: "Pod security policy violation detected"
description: "Gatekeeper detected {{ $value }} policy violations in the last 5 minutes"
- alert: PrivilegedPodCreated
expr: increase(kube_pod_container_status_running{container=~".*privileged.*"}[5m]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "Privileged pod created"
description: "A privileged pod has been created in namespace {{ $labels.namespace }}"
- alert: RootUserInContainer
expr: kube_pod_container_info{container_id!="",image!~".*debug.*"} * on(pod,namespace) group_left() kube_pod_container_status_running == 1
for: 5m
labels:
severity: warning
annotations:
summary: "Container running as root user"
description: "Container {{ $labels.container }} in pod {{ $labels.pod }} is running as root"
💡 Incident Response et forensics
Playbook de réponse aux incidents
# Incident response ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: incident-response-playbook
namespace: security
data:
playbook.yaml: |
incident_types:
- name: "Suspicious Pod Activity"
steps:
- "Isolate the affected pod using NetworkPolicy"
- "Capture pod logs and events"
- "Create memory dump if needed"
- "Analyze container image for malware"
- "Check for lateral movement"
- name: "Privilege Escalation"
steps:
- "Immediately revoke elevated permissions"
- "Audit all recent RBAC changes"
- "Check for unauthorized ServiceAccount usage"
- "Review admission controller logs"
tools:
- name: "kubectl"
commands:
- "kubectl get events --sort-by=.metadata.creationTimestamp"
- "kubectl logs -f <pod-name> --previous"
- "kubectl describe pod <pod-name>"
- name: "crictl"
commands:
- "crictl ps -a"
- "crictl logs <container-id>"
- "crictl inspect <container-id>"
Script d'isolation automatique
#!/bin/bash
# isolate-pod.sh - Script d'isolation d'urgence
POD_NAME=$1
NAMESPACE=$2
if [[ -z "$POD_NAME" || -z "$NAMESPACE" ]]; then
echo "Usage: $0 <pod-name> <namespace>"
exit 1
fi
echo "🚨 Isolating pod $POD_NAME in namespace $NAMESPACE"
# Créer une NetworkPolicy d'isolation
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: isolate-$POD_NAME
namespace: $NAMESPACE
spec:
podSelector:
matchLabels:
app: $(kubectl get pod $POD_NAME -n $NAMESPACE -o jsonpath='{.metadata.labels.app}')
policyTypes:
- Ingress
- Egress
# Deny all traffic
EOF
# Capturer les logs
kubectl logs $POD_NAME -n $NAMESPACE --previous > /tmp/${POD_NAME}-previous.log
kubectl logs $POD_NAME -n $NAMESPACE > /tmp/${POD_NAME}-current.log
# Capturer les événements
kubectl get events -n $NAMESPACE --field-selector involvedObject.name=$POD_NAME > /tmp/${POD_NAME}-events.log
echo "✅ Pod isolated and logs captured in /tmp/"
echo "📋 Next steps:"
echo " 1. Analyze logs in /tmp/"
echo " 2. Check for indicators of compromise"
echo " 3. Consider scaling down the deployment"
echo " 4. Update incident response ticket"
💡 Checklist de sécurité pour la production
Pré-déploiement
• [ ] Images scannées avec Trivy/Clair (aucune vulnérabilité critique)
• [ ] RBAC configuré avec principe du moindre privilège
• [ ] SecurityContext défini pour tous les conteneurs
• [ ] NetworkPolicies en place (deny-by-default)
• [ ] Resource limits configurées
• [ ] Secrets gérés via External Secrets Operator
• [ ] Admission controllers activés (OPA Gatekeeper)
Post-déploiement
• [ ] Monitoring de sécurité actif (Falco, Prometheus)
• [ ] Audit logs configurés et centralisés
• [ ] Backup des configurations critiques
• [ ] Tests de pénétration planifiés
• [ ] Plan de réponse aux incidents documenté
• [ ] Formation équipe sur les procédures de sécurité
Maintenance continue
• [ ] Mise à jour régulière des images de base
• [ ] Rotation des certificats et secrets
• [ ] Review périodique des permissions RBAC
• [ ] Audit des configurations de sécurité
• [ ] Veille sur les nouvelles vulnérabilités
• [ ] Tests réguliers des procédures d'urgence
💡 Outils essentiels pour la sécurité K8s
Scanner de vulnérabilités
• Trivy : Scanner complet (images, filesystem, git)
• Grype : Scanner de vulnérabilités Anchore
• Clair : Scanner d'images CoreOS
Runtime Security
• Falco : Détection d'anomalies comportementales
• Sysdig Secure : Plateforme de sécurité complète
• Aqua Security : Protection runtime avancée
Policy Management
• OPA Gatekeeper : Policies as Code
• Kyverno : Alternative YAML-native à OPA
• Polaris : Validation des best practices
Secrets Management
• External Secrets Operator : Synchronisation de secrets
• HashiCorp Vault : Coffre-fort centralisé
• AWS Secrets Manager : Service AWS natif
💡 Conclusion
La sécurité Kubernetes nécessite une approche multicouche et proactive :
Principes fondamentaux :
• Defense in depth : Sécurité à tous les niveaux du stack
• Zero Trust : Vérification continue, jamais de confiance implicite
• Least Privilege : Permissions minimales nécessaires
• Automation : Politiques as code et déploiement automatisé
Surveillance continue :
• Monitoring en temps réel des métriques de sécurité
• Alerting proactif sur les anomalies
• Audit régulier des configurations
• Response rapide aux incidents
Évolution permanente :
• Veille technologique sur les nouvelles menaces
• Formation continue des équipes
• Tests réguliers des procédures
• Amélioration itérative des processus
La sécurité n'est pas un état final mais un processus continu d'amélioration. Avec ces pratiques et outils, vous disposez d'une base solide pour sécuriser vos clusters Kubernetes en production.
Pour approfondir ces concepts et obtenir une formation pratique, consultez mes sessions spécialisées en sécurité Kubernetes et DevSecOps.
À propos de l'auteur Florian Courouge - Expert DevOps et Apache Kafka avec plus de 5 ans d'expérience dans l'architecture de systèmes distribués et l'automatisation d'infrastructures.
Cet article vous a été utile ? Découvrez mes autres articles techniques ou contactez-moi pour discuter de vos projets DevOps et Kafka.
© 2025 Florian Courouge. Tous droits réservés.