Les logs d'audit Kubernetes enregistrent chaque requête à l'API Server pour la traçabilité et l'investigation de sécurité. Ils répondent aux questions "qui a fait quoi, quand, et via quelle méthode". Ce guide vous montre comment configurer une politique d'audit, activer les logs sur l'API Server, et analyser les événements pour détecter les comportements suspects ou reconstituer un incident.
Prérequis
Section intitulée « Prérequis »- Accès à la configuration de l'API Server (fichiers manifests ou flags)
- Pour les clusters managés (EKS/GKE/AKS) : accès à la console cloud
- Compréhension des ressources Kubernetes (pods, secrets, configmaps)
Comment fonctionne l'audit Kubernetes ?
Section intitulée « Comment fonctionne l'audit Kubernetes ? »L'audit se compose de trois éléments :
- Audit Policy : définit quels événements capturer et à quel niveau de détail
- API Server : applique la politique et génère les événements
- Backend : stocke les logs (fichier local ou webhook vers système externe)
Les quatre niveaux d'audit
Section intitulée « Les quatre niveaux d'audit »Chaque règle de la politique spécifie un niveau qui détermine la quantité d'information enregistrée :
| Niveau | Ce qui est enregistré | Usage |
|---|---|---|
None | Rien | Exclure des ressources du logging (éviter le bruit) |
Metadata | Metadata de la requête (user, timestamp, resource, verb) mais pas le body | Audit de base, faible volume |
Request | Metadata + body de la requête | Voir ce qui a été envoyé |
RequestResponse | Metadata + requête + réponse | Debug complet, volume élevé |
Les quatre stages d'un événement
Section intitulée « Les quatre stages d'un événement »Un événement peut être enregistré à différentes étapes de son traitement :
| Stage | Moment | Usage |
|---|---|---|
RequestReceived | Dès réception, avant traitement | Logging exhaustif |
ResponseStarted | Headers de réponse envoyés (watch/long-running) | Requêtes longues |
ResponseComplete | Réponse terminée | Standard, le plus utilisé |
Panic | Si l'API Server panic | Debugging uniquement |
Créer une politique d'audit
Section intitulée « Créer une politique d'audit »Une audit policy est un fichier YAML composé d'une liste de règles évaluées de haut en bas : la première règle qui correspond à la requête l'emporte, les suivantes sont ignorées. L'ordre est donc structurant, une règle générale placée en tête neutralise tout ce qui suit. Sans politique, l'API Server ne journalise rien du tout.
Structure de base
Section intitulée « Structure de base »Cette politique de référence traite les cas dans l'ordre du plus spécifique au plus général : elle écarte d'abord le bruit des sondes de santé, protège ensuite les ressources sensibles en Metadata, puis se termine par une règle attrape-tout.
apiVersion: audit.k8s.io/v1kind: Policy# Règle par défaut si aucune autre ne matchomitStages: - "RequestReceived" # Évite les doublonsrules: # Les règles sont évaluées dans l'ordre, première qui match gagne
# 1. Ne pas logger les health checks (bruit) - level: None users: ["system:kube-probe", "system:serviceaccount:kube-system:kube-proxy"] resources: - group: "" resources: ["endpoints", "services"]
# 2. Secrets/ConfigMaps/TokenRequests : Metadata uniquement (jamais le contenu) # IMPORTANT : RequestResponse exposerait les valeurs sensibles dans les logs ! - level: Metadata resources: - group: "" resources: ["secrets", "configmaps", "serviceaccounts/token"] - group: authentication.k8s.io resources: ["tokenreviews"] omitStages: - "RequestReceived"
# 4. Logger les modifications RBAC - level: RequestResponse resources: - group: "rbac.authorization.k8s.io" resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]
# 5. Logger les actions sur pods au niveau Metadata - level: Metadata resources: - group: "" resources: ["pods", "pods/exec", "pods/portforward"]
# 6. Règle par défaut : Metadata pour tout le reste - level: MetadataPolitique minimale pour CKS
Section intitulée « Politique minimale pour CKS »Pour l'examen CKS, voici une politique simple mais fonctionnelle :
apiVersion: audit.k8s.io/v1kind: PolicyomitStages: - "RequestReceived"rules: # Ignorer les endpoints health - level: None nonResourceURLs: - "/healthz*" - "/readyz*" - "/livez*"
# Secrets, ConfigMaps, tokens : Metadata uniquement (pas le contenu) - level: Metadata resources: - group: "" resources: ["secrets", "configmaps", "serviceaccounts/token"] - group: authentication.k8s.io resources: ["tokenreviews"]
# RBAC : RequestResponse pour traçabilité complète - level: RequestResponse resources: - group: "rbac.authorization.k8s.io"
# Metadata pour tout le reste - level: MetadataFiltres disponibles
Section intitulée « Filtres disponibles »Les critères d'une règle se cumulent avec un ET logique : la requête doit satisfaire tous les champs présents pour que la règle s'applique. À l'intérieur d'un même champ, en revanche, les valeurs listées fonctionnent en OU.
rules: - level: Metadata # Filtrer par utilisateur users: ["admin", "system:serviceaccount:default:myapp"] userGroups: ["system:authenticated"]
# Filtrer par verbe verbs: ["create", "update", "delete"]
# Filtrer par ressource resources: - group: "" # core API group resources: ["pods", "services"] - group: "apps" resources: ["deployments"]
# Filtrer par namespace namespaces: ["production", "finance"]
# URLs non-ressources (healthz, metrics) nonResourceURLs: ["/api*", "/version"]
# Stages à omettre pour cette règle omitStages: ["RequestReceived"]Wildcard group: "*" (Kubernetes 1.36)
Section intitulée « Wildcard group: "*" (Kubernetes 1.36) »Depuis Kubernetes 1.36, l'API d'audit accepte la valeur "*" dans le champ group pour cibler tous les groupes d'API d'un coup, sans avoir à les énumérer. Pratique pour une politique de fond qui doit couvrir des CRDs ajoutés au fil du temps.
rules: - level: Metadata # Toutes les ressources de tous les groupes resources: - group: "*" resources: ["*"] # Combinable avec un filtre de verbe pour limiter le volume verbs: ["create", "update", "delete", "patch"] omitStages: ["RequestReceived"]Configurer l'API Server
Section intitulée « Configurer l'API Server »Écrire la politique ne suffit pas : il faut la faire lire par l'API Server et lui indiquer où déposer les événements. Sur un cluster installé avec kubeadm, cela passe par le manifest statique du composant, que le kubelet surveille en permanence. Toute erreur de syntaxe dans ce fichier empêche l'API Server de redémarrer, et donc tout le cluster de répondre : travaillez avec une copie de sauvegarde du manifest.
Clusters kubeadm / self-managed
Section intitulée « Clusters kubeadm / self-managed »Ces cinq étapes doivent être menées sur chaque nœud du plan de contrôle, car chaque API Server possède son propre manifest et son propre fichier de journal.
-
Créer le fichier de politique
Fenêtre de terminal sudo mkdir -p /etc/kubernetes/auditsudo vim /etc/kubernetes/audit/audit-policy.yamlColler la politique YAML.
-
Modifier le manifest de l'API Server
Fenêtre de terminal sudo vim /etc/kubernetes/manifests/kube-apiserver.yamlAjouter les flags :
spec:containers:- command:- kube-apiserver# ... autres flags existants ...- --audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml- --audit-log-path=/var/log/kubernetes/audit/audit.log- --audit-log-maxage=30 # Rétention en jours- --audit-log-maxbackup=10 # Nombre de fichiers à conserver- --audit-log-maxsize=100 # Taille max en Mo avant rotation -
Ajouter les volumes et volumeMounts
spec:containers:- volumeMounts:# ... mounts existants ...- mountPath: /etc/kubernetes/auditname: audit-policyreadOnly: true- mountPath: /var/log/kubernetes/auditname: audit-logsvolumes:# ... volumes existants ...- name: audit-policyhostPath:path: /etc/kubernetes/audittype: DirectoryOrCreate- name: audit-logshostPath:path: /var/log/kubernetes/audittype: DirectoryOrCreate -
Créer le répertoire de logs
Fenêtre de terminal sudo mkdir -p /var/log/kubernetes/audit -
Vérifier le redémarrage de l'API Server
Fenêtre de terminal # L'API Server redémarre automatiquement (kubelet surveille /etc/kubernetes/manifests)kubectl get pods -n kube-system | grep api# Vérifier les logssudo tail -f /var/log/kubernetes/audit/audit.log | jq .
Exemple de manifest complet
Section intitulée « Exemple de manifest complet »Voici le résultat des trois premières étapes réunies dans un seul fichier, pour vérifier que rien ne manque : les cinq drapeaux --audit-*, les deux volumeMounts et les deux hostPath correspondants. Un montage oublié se traduit par un API Server en CrashLoopBackOff.
apiVersion: v1kind: Podmetadata: name: kube-apiserver namespace: kube-systemspec: containers: - name: kube-apiserver command: - kube-apiserver - --advertise-address=10.0.0.10 - --allow-privileged=true - --authorization-mode=Node,RBAC # ... autres flags ... - --audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml - --audit-log-path=/var/log/kubernetes/audit/audit.log - --audit-log-maxage=7 - --audit-log-maxbackup=5 - --audit-log-maxsize=100 volumeMounts: - mountPath: /etc/kubernetes/audit name: audit-policy readOnly: true - mountPath: /var/log/kubernetes/audit name: audit-logs volumes: - hostPath: path: /etc/kubernetes/audit type: DirectoryOrCreate name: audit-policy - hostPath: path: /var/log/kubernetes/audit type: DirectoryOrCreate name: audit-logsWebhook backend (optionnel)
Section intitulée « Webhook backend (optionnel) »Pour envoyer les événements vers un système externe (SIEM, Elasticsearch) :
apiVersion: v1kind: Configclusters: - name: audit-webhook cluster: server: https://siem.example.com/k8s-audit certificate-authority: /etc/kubernetes/pki/ca.crtcontexts: - name: audit-webhook context: cluster: audit-webhookcurrent-context: audit-webhookFlags API Server pour webhook :
--audit-webhook-config-file=/etc/kubernetes/audit/audit-webhook-config.yaml--audit-webhook-initial-backoff=5s--audit-webhook-batch-max-size=1000Analyser les logs d'audit
Section intitulée « Analyser les logs d'audit »Le fichier d'audit contient un objet JSON par ligne, ce qui le rend directement exploitable avec jq. Chaque ligne est un événement complet et autonome : elle porte l'identité de l'appelant, l'action demandée, la ressource visée et le code de retour. Savoir lire ces quatre champs suffit à mener la majorité des investigations.
Structure d'un événement
Section intitulée « Structure d'un événement »Les champs à repérer en priorité sont user.username (qui), verb (quoi), objectRef (sur quelle ressource) et responseStatus.code (avec quel résultat). Le champ sourceIPs complète le tableau en donnant l'origine réseau de la requête.
{ "kind": "Event", "apiVersion": "audit.k8s.io/v1", "level": "RequestResponse", "auditID": "abc123", "stage": "ResponseComplete", "requestURI": "/api/v1/namespaces/default/secrets", "verb": "create", "user": { "username": "admin", "groups": ["system:masters", "system:authenticated"] }, "sourceIPs": ["10.0.0.50"], "userAgent": "kubectl/v1.30.0", "objectRef": { "resource": "secrets", "namespace": "default", "name": "my-secret", "apiVersion": "v1" }, "responseStatus": { "code": 201 }, "requestObject": { "kind": "Secret", "data": { "password": "***" } }, "stageTimestamp": "2025-06-19T10:30:00Z"}Requêtes d'investigation courantes
Section intitulée « Requêtes d'investigation courantes »Ces filtres jq répondent chacun à une question précise posée en incident. Les accès refusés (code 403) méritent une attention particulière : une rafale de 403 sur un même compte de service trahit souvent une tentative d'élévation de privilèges.
# Toutes les créations de secretscat audit.log | jq 'select(.verb=="create" and .objectRef.resource=="secrets")'
# Actions d'un utilisateur spécifiquecat audit.log | jq 'select(.user.username=="suspicious-user")'
# Accès refusés (403)cat audit.log | jq 'select(.responseStatus.code==403)'
# Toutes les actions sur un namespacecat audit.log | jq 'select(.objectRef.namespace=="production")'
# Exec dans des podscat audit.log | jq 'select(.objectRef.subresource=="exec")'
# Top 10 des utilisateurs les plus actifscat audit.log | jq -r '.user.username' | sort | uniq -c | sort -rn | head -10
# Actions des 5 dernières minutescat audit.log | jq --arg d "$(date -u -d '5 minutes ago' +%Y-%m-%dT%H:%M:%SZ)" \ 'select(.stageTimestamp > $d)'Reconstituer une timeline d'incident
Section intitulée « Reconstituer une timeline d'incident »Une chronologie exploitable s'obtient en croisant trois filtres : le groupe d'API concerné, une fenêtre temporelle et l'exclusion des verbes de lecture (get, list, watch), qui noieraient les modifications sous le trafic normal.
# Exemple : qui a modifié le RBAC hier ?cat audit.log | jq --arg start "2025-06-18T00:00:00Z" --arg end "2025-06-19T00:00:00Z" \ 'select( .objectRef.apiGroup=="rbac.authorization.k8s.io" and .verb!="get" and .verb!="list" and .verb!="watch" and .stageTimestamp > $start and .stageTimestamp < $end ) | {time: .stageTimestamp, user: .user.username, verb, resource: .objectRef.resource, name: .objectRef.name}'Intégration avec Falco
Section intitulée « Intégration avec Falco »Falco peut analyser les audit logs Kubernetes pour des détections avancées. Voir le guide Falco pour la configuration du plugin K8s Audit.
Clusters managés
Section intitulée « Clusters managés »Sur les clusters managés, les audit logs sont gérés par le fournisseur cloud. Vous ne pouvez généralement pas personnaliser la politique d'audit, mais les logs sont automatiquement centralisés et consultables via les outils natifs.
AWS EKS envoie les audit logs vers CloudWatch Logs. Par défaut, seuls les logs API Server et Authenticator sont activés.
# Activer les audit logs (control plane logging)eksctl utils update-cluster-logging \ --cluster my-cluster \ --enable-types audit,api,authenticator \ --region eu-west-1Ou via AWS CLI :
aws eks update-cluster-config \ --name my-cluster \ --logging '{"clusterLogging":[{"types":["audit","api","authenticator"],"enabled":true}]}'Consulter les logs :
# Les logs sont dans le groupe /aws/eks/<cluster>/clusteraws logs filter-log-events \ --log-group-name /aws/eks/my-cluster/cluster \ --log-stream-name-prefix kube-apiserver-audit \ --filter-pattern '{ $.verb = "create" && $.objectRef.resource = "secrets" }'GKE utilise Cloud Audit Logs, activé par défaut. Deux types de logs :
- Admin Activity : créations, modifications, suppressions (toujours activé, gratuit)
- Data Access : lectures (désactivé par défaut, payant)
# Vérifier la configuration du clustergcloud container clusters describe my-cluster \ --format="yaml(loggingConfig)"
# Activer Data Access logs (optionnel, génère du volume)gcloud projects get-iam-policy PROJECT_ID \ --format=yaml > policy.yaml# Éditer pour ajouter auditConfigs pour GKEConsulter les logs dans Cloud Logging :
resource.type="k8s_cluster"protoPayload.methodName:"secrets"protoPayload.authenticationInfo.principalEmail:"user@example.com"AKS expose les audit logs via Azure Monitor et Diagnostic Settings. Plusieurs catégories disponibles :
kube-audit: audit complet (volume élevé)kube-audit-admin: uniquement les opérations d'écriture (recommandé)
# Créer un Log Analytics workspace si nécessaireaz monitor log-analytics workspace create \ --resource-group myRG \ --workspace-name myWorkspace
# Activer les diagnostic settingsCLUSTER_ID=$(az aks show -g myRG -n my-cluster --query id -o tsv)WORKSPACE_ID=$(az monitor log-analytics workspace show \ -g myRG -n myWorkspace --query id -o tsv)
az monitor diagnostic-settings create \ --name aks-audit \ --resource "$CLUSTER_ID" \ --workspace "$WORKSPACE_ID" \ --logs '[{"category":"kube-audit-admin","enabled":true}]'Requête KQL dans Log Analytics :
AzureDiagnostics| where Category == "kube-audit-admin"| extend auditLog = parse_json(log_s)| where auditLog.objectRef.resource == "secrets"| project TimeGenerated, User=auditLog.user.username, Verb=auditLog.verb, Resource=auditLog.objectRef.name| order by TimeGenerated descDépannage
Section intitulée « Dépannage »Deux symptômes dominent après l'activation de l'audit : l'API Server refuse de redémarrer, ou il démarre sans produire le moindre fichier. Le premier cas se diagnostique dans les journaux du kubelet, puisque l'API Server n'est plus joignable par kubectl ; le second vient presque toujours d'un drapeau absent ou d'un répertoire non monté.
L'API Server ne redémarre pas
Section intitulée « L'API Server ne redémarre pas »Quand l'API Server est à terre, kubectl ne répond plus : la seule source d'information est le kubelet local, qui journalise l'échec de démarrage du pod statique.
# Vérifier les erreurssudo journalctl -u kubelet | grep -i apiserver | tail -50
# Erreurs YAML courantessudo cat /etc/kubernetes/manifests/kube-apiserver.yaml | yq .Causes fréquentes :
| Symptôme | Cause | Solution |
|---|---|---|
| "audit policy file not found" | Chemin incorrect ou fichier absent | Vérifier le path et les permissions |
| "invalid audit policy" | YAML invalide | Valider le YAML avec yq . policy.yaml ou un linter |
| CrashLoopBackOff API Server | Volume mount incorrect | Vérifier hostPath et mountPath |
Pas de logs générés
Section intitulée « Pas de logs générés »Un fichier vide n'est pas forcément une panne : la politique peut simplement ne rien capturer. Vérifiez d'abord que les drapeaux sont bien passés au processus, puis provoquez une action volontairement journalisée pour confirmer.
# Vérifier que l'audit est actifps aux | grep kube-apiserver | grep audit
# Les flags doivent apparaître# --audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml# --audit-log-path=/var/log/kubernetes/audit/audit.log
# Vérifier les permissions du répertoirels -la /var/log/kubernetes/audit/
# Générer une action pour créer des logskubectl create secret generic test-audit --from-literal=key=valuekubectl delete secret test-auditBonnes pratiques
Section intitulée « Bonnes pratiques »L'audit se règle sur un compromis entre volume et valeur de preuve. Une politique trop large sature le disque du plan de contrôle et rend les recherches inutilisables ; une politique trop étroite laisse des angles morts au moment où vous en avez besoin. Les quatre réflexes ci-dessous tiennent ce compromis.
Exclure le bruit
Utiliser level: None pour les health checks, metrics, et reads fréquents.
RequestResponse avec parcimonie
Réserver ce niveau aux secrets, RBAC, et pods/exec.
Rotation des logs
Configurer maxage, maxbackup, maxsize pour éviter de remplir le disque.
Centraliser les logs
Utiliser un webhook vers SIEM/Elasticsearch pour la rétention longue.
À retenir
Section intitulée « À retenir »| Concept | Description |
|---|---|
| Audit Policy | Fichier YAML définissant quoi logger et à quel niveau |
| Niveaux | None < Metadata < Request < RequestResponse |
| Stages | RequestReceived, ResponseStarted, ResponseComplete, Panic |
| Backend fichier | --audit-log-path pour logs locaux |
| Backend webhook | Pour envoyer vers systèmes externes (SIEM) |
| Rotation | --audit-log-maxage/maxbackup/maxsize |
Prochaines étapes
Section intitulée « Prochaines étapes »Falco Runtime Security
Détecter les comportements suspects en temps réel. Configurer Falco
CIS Benchmark
Valider la configuration sécurisée du cluster. CIS Benchmark
RBAC
Contrôler les accès audités. RBAC Kubernetes
Dix questions issues de la banque CKS sur le domaine journalisation et audit, à faire en huit minutes avec 70 % de bonnes réponses pour valider.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications