Module 5 : Sécurité dans Kubernetes

Formation Kubernetes - Débutant à Avancé

Plan du Module 5 (1/2)

Partie 1 : contrôle d'accès (RBAC)

  • Ressources RBAC
  • Tooling d'audit du RBAC

Partie 2 : restreindre les Pods pour améliorer la sécurité

  • Configuration sécurisée des Pods
  • Capabilities, utilisateurs, filesystem, UserNamespace

Plan du Module 5 (2/2)

Partie 3 : Admission Control

  • Pod Security Standards (successeur PSP)
  • CEL (Common Expression Language)
  • Politiques Kyverno, détection de menaces

Partie 4 : bonnes pratiques, audit et conformité

  • Container security, cluster hardening
  • API Server audit logging et conformité CIS
  • Outils de conformité et automatisation

Partie 1 : Contrôle d'accès (RBAC)

Introduction au RBAC : principes généraux

Role-Based Access Control : contrôle d'accès basé sur les rôles

Dans Kubernetes, tout est API :

  • Créer un Pod → POST /api/v1/namespaces/default/pods
  • Lister les Services → GET /api/v1/services
  • Supprimer un Deployment → DELETE /apis/apps/v1/deployments/nginx

RBAC définit qui peut faire quoi sur quelles ressources.

Users, Groups, ServiceAccounts

Users : Humains (développeurs, ops, admins)

  • Gérés en dehors de Kubernetes (OIDC, certificats, LDAP...)
  • mais pas d'objet User dans l'API

Groups : Groupes d'utilisateurs (ex. system:masters, developer-team)

ServiceAccounts : Identités pour les applications

  • Utilisés par les Pods pour accéder à l'API
  • Un par namespace par défaut (default)

Roles

Role : permissions dans un namespace

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

ClusterRoles

ClusterRole : permissions à l'échelle du cluster

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-reader
rules:
- apiGroups: [""]
  resources: ["nodes", "namespaces"]  
  verbs: ["get", "list"]

RoleBindings

Lie un Role à des sujets dans un namespace

apiVersion: rbac.authorization.k8s.io/v1                                              
kind: RoleBinding
metadata:
  name: developers-pods
  namespace: production
subjects:
- kind: ServiceAccount
  name: deployment-sa
  namespace: production
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding

Pareil mais pour un ClusterRole

apiVersion: rbac.authorization.k8s.io/v1                                                  
kind: ClusterRoleBinding
metadata:
  name: cluster-admins
subjects:
- kind: User
  name: admin
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:masters
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

Tooling d'audit du RBAC

Vérifier les permissions :

# Vérifier si un utilisateur peut faire une action
kubectl auth can-i create pods --as=alice
kubectl auth can-i get secrets --as=system:serviceaccount:default:my-sa

# Lister toutes les permissions d'un utilisateur
kubectl auth can-i --list --as=alice

# Voir les permissions dans un namespace
kubectl auth can-i --list --as=alice -n production

Quelques autres outils d'audit

Plugins kubectl utiles :

  • kubectl who-can : Plugin - Découvrir qui peut faire quoi
  • rbac-lookup : Outil - Analyser les permissions RBAC
  • kubectl rbac-tool : Plugin - Visualiser et analyser les règles RBAC

Outils d'audit avancés :

  • Falco : Détection des violations RBAC en runtime
  • Polaris : Audit automatique des configurations de sécurité

Partie 2: restreindre les Pods pour améliorer la sécurité

Security Context : sécuriser les Pods

Le Security Context définit les privilèges et contrôles d'accès pour un Pod ou Container.

Ces Security Contexts peuvent être définis à 2 niveaux :

  • Pod (pour tous les containers du pod)
  • container

Security Context au niveau du Pod

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    <paramètres de sécurité>
  [...]
  containers:
  - name: sec-ctx-demo
    image: busybox:1.37
    command: [ "sh", "-c", "sleep 1h" ]

Security Context au niveau du container

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-2
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: sec-ctx-demo-2
    image: busybox:1.37
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      <paramètres de sécurité>      # Priorité sur Pod-level                       

ℹ️ le Container-level a la priorité sur Pod-level

Quelques concepts de base

  • runAsUser: UID du processus principal
  • runAsGroup: GID principal du processus
  • fsGroup: GID propriétaire des volumes
  • privileged: Accès complet au système hôte (dangereux !)
  • allowPrivilegeEscalation: Permet l'escalade de privilèges
  • readOnlyRootFilesystem: Système de fichiers racine en lecture seule
  • runAsNonRoot: Force l'exécution avec un utilisateur non-root

Capabilities

Contrôle fin des privilèges système : les capabilities Linux permettent de donner des permissions spécifiques sans donner tous les droits root.

securityContext:
  capabilities:
    add: ["NET_ADMIN", "SYS_TIME"]
    drop: ["ALL"]

Security context : exemple avec nginx

apiVersion: v1
kind: Pod
metadata:
  name: nginx-insecure
spec:
  containers:
  - name: nginx
    image: nginx:1.29
    ports:
    - containerPort: 80

Security context : exemple sécurisé

apiVersion: v1
kind: Pod
metadata:
  name: nginx-secure
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1001
    fsGroup: 1001
  containers:
  - name: nginx
    image: nginx:1.29
    ports:
    - containerPort: 8080  # Port non-privilégié                                                
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]         

SELinux

Système de contrôle d'accès obligatoire (MAC) au niveau kernel

securityContext:
  seLinuxOptions:
    level: "s0:c123,c456"
    type: "container_t"
    user: "system_u"
    role: "system_r"

AppArmor profiles

Système de sécurité par profils pour confiner les applications

metadata:
  annotations:
    container.apparmor.security.beta.kubernetes.io/container-name: runtime/default
spec:
  containers:
  - name: container-name
    # ...

Paramètres sysctl

Configuration des paramètres du kernel Linux pour un seul Pod.
⚠️ : tous les paramètres ne sont pas considérés "safe" :

spec:
  securityContext:
    sysctls:
    - name: kernel.shm_rmid_forced # safe
      value: "0"
    - name: net.core.somaxconn # unsafe!
      value: "1024"

Vérification des UID/GID

# Dans le container non-sécurisé (nginx-insecure)
kubectl exec -it nginx-insecure -- id
# uid=0(root) gid=0(root) groups=0(root)

kubectl exec -it nginx-insecure -- ps aux
# USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
# root         1  0.0  0.0  11528  7396 ?        Ss   10:30   0:00 nginx: master process

# Dans le container sécurisé (nginx-secure)
kubectl exec -it nginx-secure -- id
# uid=1001 gid=1001 groups=1001

kubectl exec -it nginx-secure -- ps aux
# USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
# 1001         1  0.0  0.0  11528  7396 ?        Ss   10:30   0:00 nginx: master process      

Test des capabilities (1/2)

# Test de ping (nécessite CAP_NET_RAW)
kubectl exec -it nginx-insecure -- ping -c 1 8.8.8.8
# PING 8.8.8.8 (8.8.8.8): 56 data bytes
# 64 bytes from 8.8.8.8: icmp_seq=0 ttl=113 time=15.123 ms ✅

kubectl exec -it nginx-secure -- ping -c 1 8.8.8.8
# ping: socket: Operation not permitted ❌

Test des capabilities (2/2)

# Test lecture seule du filesystem
kubectl exec -it nginx-insecure -- touch /test-file
# (Réussit) ✅

kubectl exec -it nginx-secure -- touch /test-file
# touch: /test-file: Read-only file system ❌

# Test des capabilities disponibles
kubectl exec -it nginx-insecure -- cat /proc/1/status | grep Cap
kubectl exec -it nginx-secure -- cat /proc/1/status | grep Cap
# (Le Pod sécurisé aura beaucoup moins de capabilities)

UserNamespace

Par défaut, UID/GID dans le conteneur = UID/GID sur l'hôte.

Si un attaquant s'échappe d'un container qui tourne en root, il devient root de l'hôte sous-jacent !

Dans les cas où il n'est pas possible de ne pas exécuter un container en root, on peut utiliser la fonctionnalité UserNamespaces (alpha en 1.25, beta depuis 1.30). Les UID/GID du container sont alors uniques (offset côté hôte, transparent pour le container)

UserNamespace : exemple de manifest

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-userns
spec:
  hostUsers: false  # Active l'isolation UserNamespace
  securityContext:
    runAsUser: 1000
    runAsGroup: 1000
  containers:
  - name: app
    image: alpine:3.22
    command: ["sleep", "3600"]

Vérification de la fonctionnalité

# Test de l'isolation
kubectl apply -f pod-with-userns.yaml

# Dans le conteneur : UID 1000
kubectl exec -it pod-with-userns -- id
# uid=1000 gid=1000

# Sur le nœud : UID mappé avec l'offset
sudo cat /proc/$(pgrep -f "sleep 3600")/status | grep Uid
# Uid:    231072  231072  231072  231072

Partie 3: Admission Control

C'est quoi l'admission control ?

Mécanisme qui intercepte les requêtes à l'API Server après l'authentification et l'autorisation, mais avant la persistance des objets.
center

source : Kubernetes admission controllers in 5 minutes

Types d'admission controllers

  1. Validating Admission Controllers : Valident les requêtes
  2. Mutating Admission Controllers : Modifient les requêtes
  3. Admission Webhooks : Controllers personnalisés

Admission Controllers natifs

Exemples d'admission controllers intégrés :

  • NodeRestriction : Limite les permissions des kubelets
  • ResourceQuota : Applique les quotas de ressources
  • LimitRanger : Applique les limites par défaut
  • PodSecurityPolicy : ⚠️ Déprécié en v1.25

kubernetes.io - liste des controllers disponibles

Etendre les possibilités natives

Par défaut, Kubernetes autorise "trop" de choses à l'utilisateur final. On va vouloir restreindre les possibilités.

Il existe plusieurs façon d'étendre l'admission control dans Kubernetes

  • développer soi même un webhook
  • utiliser un outil comme Kyverno ou OPA/Gatekeeper
  • Pod Security Standards (successeur de PSP)
  • CEL (Common Expression Language)!

Pod Security Standards (1/3)

  • Successeur de PodSecurityPolicy (PSP) devenues obsolète en v1.25+
  • Activation via des labels sur les Namespaces directement
    • s'applique donc à tout le namespace !

Pod Security Standards (2/3)

3 politiques de sécurité :

  • privileged : ouverte sans restrictions
  • baseline : minimale, empêche les escalades de privilèges connues
  • restricted : très restrictive, suit les best practices sécurité

3 modes disponibles :

  • enforce : bloque les Pods non conformes
  • audit : journalise les violations dans les logs d'audit
  • warn : affiche des warnings à l'utilisateur

Pod Security Standards (3/4)

Configuration par namespace avec des labels :

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # Mode d'application
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted  
    pod-security.kubernetes.io/warn: restricted
    # Version du standard
    pod-security.kubernetes.io/enforce-version: v1.29

Pod Security Standards (4/4)

Différences pratiques entre niveaux :

Contrôle Baseline Restricted
runAsNonRoot - ✅ Obligatoire
runAsUser > 0 > 0
allowPrivilegeEscalation false false
capabilities Drop ALL sauf NET_BIND_SERVICE Drop ALL
volumes Plusieurs autorisés Très restrictif
hostNetwork/hostPID Interdit Interdit

CEL : Common Expression Language

"Nouveauté" Kubernetes native (alpha 1.26) pour définir des politiques d'admission sans webhook externe !

  • Validating Admission Policy (stable v1.30)

  • Mutating Admission Policy (beta v1.34)

  • Intégré nativement dans Kubernetes

  • Pas besoin de webhook externe

  • Syntaxe "simple"

CEL : exemple Validating Admission Policy

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "require-non-root"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - operations: ["CREATE", "UPDATE"]
      apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
  validations:
  - expression: "object.spec.securityContext.runAsNonRoot == true"
    message: "Pod must run as non-root user"
  - expression: "object.spec.containers.all(c, c.securityContext.allowPrivilegeEscalation == false)"
    message: "Containers must not allow privilege escalation"

CEL : Binding de la politique

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "require-non-root-binding"
spec:
  policyName: "require-non-root"
  validationActions: [Warn, Audit]  # ou [Deny] pour bloquer
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: "production"

Politique réutilisable avec différents bindings selon les namespaces

Politiques de conformité

Il existe 2 logiciels permettant de faciliter la création et l'application de politiques de conformités dans Kubernetes :

  • Open Policy Agent
  • Kyverno

OPA est un outil générique, Kyverno est Kubernetes-native

C'est quoi Kyverno ?

Moteur de politiques cloud-native pour Kubernetes

  • Syntaxe YAML native (pas de DSL) + JMESPath
  • Validation, mutation et génération de ressources
  • Reporting et observabilité intégrés (policy-reporter + ui)
  • 2 types de politiques :
    • ClusterPolicy : à l'échelle du cluster
    • Policy : dans un namespace spécifique

Kyverno : exemple de politique

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-pod-rnu-as-non-root
spec:
  validationFailureAction: enforce
  background: true
  rules:
  - name: check-security-context
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "Security context with runAsNonRoot at pod level is required"
      pattern:
        spec:
          securityContext:
            runAsNonRoot: true                                                                         

Kyverno : quelques concepts (1/2)

Validation : enforce ou audit

spec:
  validationFailureAction: enforce  # Bloque la création
  # validationFailureAction: audit   # Permet avec un warning + log

Background scanning : audit des ressources existantes

spec:
  background: true  # Scanne aussi l'existant

Kyverno : quelques concepts (2/2)

Mutation : modification automatique d'un objet avant sa création

rules:
- name: add-security-context
  match:
    any:
    - resources:
        kinds: [Pod]
  mutate:
    patchStrategicMerge:
      spec:
        securityContext:
          runAsNonRoot: true

Kyverno : tests de politiques

Kyverno permet aussi de tester (TU) ses policies kyverno test . :

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
  name: test-security-policy
policies:
- policy.yaml
resources:
- pod-good.yaml
- pod-bad.yaml  
results:
- policy: require-pod-security-standards
  rule: check-security-context
  resource: pod-good
  result: pass
- policy: require-pod-security-standards                                                                     
  rule: check-security-context
  resource: pod-bad
  result: fail

Détection de menaces : concepts (1/2)

Pour sécuriser le cluster, 2 concepts sont complémentaires :

  • runtime analysis (moteur de détection en temps réel)
  • static analysis (analyse statique, notamment des images)

Détection de menaces : concepts (2/2)

Objectifs de la static analysis :

  • Scanner les vulnérabilités des images avant déploiement
  • Vérifier la conformité aux politiques de sécurité
  • Détecter les secrets hardcodés dans le code

Objectifs de la runtime analysis :

  • Détecter les comportements suspects, escalades de privilèges ou accès non autorisés aux ressources en temps réel
  • Alerter sur les violations de politiques de sécurité

runtime analysis : exemple avec Falco (1/2)

Processus (ou container) privilégié sur les Nodes

  • Surveille les appels système (syscalls)
  • Règles de détection configurables
  • Intégration avec SIEM/alerting

runtime analysis : exemple avec Falco (2/2)

- rule: Unexpected root access
  desc: Detect unexpected root access to container
  condition: >
    spawned_process and container and
    proc.euser.name = root and
    not user_known_exec_in_container
  output: >
    Unexpected root access (user=%user.name command=%proc.cmdline 
    image=%container.image.repository:%container.image.tag)
  priority: WARNING

static analysis (1/2)

Outils de scan de vulnérabilités d'images :

  • Trivy : Scanner de vulnérabilités open-source
  • Grype : Analyse de sécurité des conteneurs
# Exemple avec Trivy
trivy image nginx:1.29
# Scanne les vulnérabilités CVE dans l'image

# Exemple avec un pipeline CI/CD
trivy image --severity HIGH,CRITICAL --exit-code 1 myapp:latest                
# Bloque le déploiement si vulnérabilités critiques

static analysis (2/2)

Outils d'analyse de configuration :

Partie 4: Bonnes pratiques, audit et conformité

Bonnes pratiques de création des images

  • Utiliser des images officielles ou trusted registries
  • Diminuer la surface d'attaque (distroless, scratch, multi-stage)
  • Pas de root dans les Dockerfiles
  • Secrets management (pas de hardcoding)
  • Scan de vulnérabilités (Trivy, etc.)
  • Signatures d'images (cosign, notation)
  • SBOM (Software Bill of Materials)

Bonnes pratiques pour les clusters (1/3)

Réseau :

  • Network Policies (vu dans le module suivant)
  • Ingress avec TLS
  • mTLS entre pods (nécessite parfois un Service Mesh)

Nodes :

  • OS minimaliste immutables (Talos Linux) ou hardening (CIS bench)
  • Modules de sécurité Kernel (AppArmor, SELinux)
  • Mise à jour régulières

Bonnes pratiques pour les clusters (2/3)

Control plane :

  • etcd encryption at rest
  • Admission controllers activés

Audit et traçabilité :

  • Logs d'audit configurés avec politique appropriée
  • Monitoring des accès privilégiés
  • Alerting sur les actions suspectes

Focus API Server Audit Logging

L'audit Kubernetes capture toutes les requêtes à l'API Server. Attention, ça peut générer beaucoup de logs !!

4 niveaux d'audit :

  • None : pas d'audit
  • Metadata : requête, utilisateur, timestamp (sans le body)
  • Request : metadata + request body
  • RequestResponse : metadata + request + response bodies ⚠️

Focus API Server Audit Logging - exemple

# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
  namespaces: ["kube-system", "kube-public"]
  verbs: ["create", "delete", "patch"]
- level: Request
  resources:
  - group: ""
    resources: ["secrets", "configmaps"]
- level: None
  users: ["system:apiserver", "system:kube-scheduler"]

Activation de l'audit logging

# /etc/kubernetes/manifests/kube-apiserver.yaml
...
  - command:
    - kube-apiserver
    - --audit-log-path=/var/log/kubernetes/audit.log
    - --audit-policy-file=/etc/kubernetes/audit-policy.yaml
    - --audit-log-maxage=30
    - --audit-log-maxbackup=10
    - --audit-log-maxsize=100
...

Analyse des logs d'audit

{
  "kind": "Event",
  "apiVersion": "audit.k8s.io/v1",
  "level": "Request",
  "auditID": "8a4c5c7e-7b9f-4c8d-9e6f-1a2b3c4d5e6f",
  "stage": "ResponseStarted",
  "requestURI": "/api/v1/namespaces/default/secrets",
  "verb": "create",
  "user": {
    "username": "alice@company.com",
    "groups": ["developers"]
  },
  "sourceIPs": ["10.0.0.5"],
  "userAgent": "kubectl/v1.33.0",
  "objectRef": {
    "resource": "secrets",
    "namespace": "default",
    "name": "app-secret"
  },
  "requestReceivedTimestamp": "2024-03-15T10:30:45.123456Z"                                                 
}

Analyse avec des outils :

  • jq : cat audit.log | jq '.user.username' | sort | uniq -c
  • Falco : détection de patterns suspects
  • ELK Stack : indexation et dashboards
  • Grafana + Loki : visualisation moderne

Bonnes pratiques pour les clusters (3/3)

Conformité :

  • Vérification régulière avec CIS Kubernetes Benchmark (Kube-bench)
  • Scan de sécurité automatisé (Polaris, Trivy)
  • Documentation des configurations de sécurité

CIS Kubernetes Benchmark avec Kube-bench

Center for Internet Security (CIS) = standards de sécurité

kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml

Exemple de sortie :

[INFO] 1 Control Plane Security Configuration
[PASS] 1.1.1 Ensure that the API server pod specification file permissions are set to 644 or more restrictive
[FAIL] 1.1.2 Ensure that the API server pod specification file ownership is set to root:root
[WARN] 1.1.3 Ensure that the controller manager pod specification file permissions are set to 644 or more restrictive

== Remediations control-plane ==
1.1.2 Run the below command on the control plane node:
chown root:root /etc/kubernetes/manifests/kube-apiserver.yaml

Bonnes pratiques pour les applications

Security Context :

  • runAsNonRoot: true par défaut
  • readOnlyRootFilesystem: true si possible
  • Supprimer les capabilities non nécessaires (drop: [ALL])

RBAC :

  • Principe de moindre privilège
  • ServiceAccounts dédiés par application
  • Audit régulier des permissions

TP 5 : Sécurité dans Kubernetes

Objectif du TP : sécuriser un cluster Kubernetes

  • Implémenter RBAC avec ServiceAccounts et Roles
  • Configurer Security Context pour des Pods sécurisés
  • Scanner les vulnérabilités avec Trivy

Instructions détaillées dans TP/module-5/

Questions ?

Prêts pour parler des CRI, CNI et CSI dans Kubernetes ?

Resources et outils (1/3)

Documentation officielle :

Resources et outils (2/3)

Outils de sécurité :

Resources et outils (3/3)

Frameworks de sécurité :