Skip to content

Cet outil n'est ni affilié à The Linux Foundation, à la Cloud Native Computing Foundation (CNCF) ou au projet Kubernetes, ni approuvé ni sponsorisé par eux. Kubernetes et K8s sont des marques déposées de The Linux Foundation. EKS, GKE, AKS et les autres noms sont des marques de leurs propriétaires respectifs.

Détecter kubectl exec, attach et port-forward (logs d'audit)

Comment kubectl exec, attach, cp, port-forward et nodes/proxy apparaissent dans les logs d'audit Kubernetes, et comment distinguer admin légitime et abus.

Publié le 6 min de lecture

TL;DR. kubectl exec et kubectl attach sont des requêtes sur les sous-ressources pods/exec et pods/attach ; kubectl port-forward touche pods/portforward. Filtrez sur objectRef.subresource, acceptez create et get comme verbes, attendez-vous au code de réponse 101, et lisez la commande dans les paramètres command= de requestURI. Un exec par un compte de service est rare et doit être traité en sévérité élevée. L'accès à nodes/proxy atteint directement le kubelet, c'est pire. Rien de ce qui est tapé dans la session n'est journalisé.

Exécuter une commande dans un conteneur correspond à la technique ATT&CK T1609, Container Administration Command. C'est aussi ce que fait le plus souvent un administrateur légitime pendant une panne. Le but n'est pas de signaler chaque exec, mais de rendre chaque exec explicable.

À quoi ressemble un exec dans le log d'audit

kubectl exec -it api-6c9f7d8b5-q2w8x -n shop -- sh produit un événement comme celui-ci (abrégé, noms fictifs) :

{
  "verb": "create",
  "stage": "ResponseStarted",
  "requestURI": "/api/v1/namespaces/shop/pods/api-6c9f7d8b5-q2w8x/exec?command=sh&container=api&stdin=true&stdout=true&tty=true",
  "user": { "username": "alice@example.com" },
  "objectRef": { "resource": "pods", "namespace": "shop", "name": "api-6c9f7d8b5-q2w8x", "subresource": "exec" },
  "responseStatus": { "code": 101 }
}

À savoir :

  • Verbe. L'endpoint exec est traditionnellement autorisé et audité comme create. Depuis Kubernetes 1.31, kubectl passe par WebSockets par défaut, et un upgrade WebSocket commence par un GET HTTP : selon les versions, vous pouvez voir get. Cherchez les deux.
  • Stage. L'exec est une requête longue. L'événement ResponseStarted est écrit à l'ouverture de la session ; ResponseComplete seulement à sa fermeture. Si la politique n'omet rien, vous avez les deux.
  • Code de réponse. 101 Switching Protocols signifie que la session a été établie. Un 403 signifie que quelqu'un a essayé et que RBAC a refusé, ce qui est intéressant en soi.
  • Commande. Chaque argument est un paramètre command= distinct, encodé en URL : command=chroot&command=%2Fhost&command=sh donne chroot /host sh. Comme elle est dans l'URI, elle est journalisée même au niveau Metadata.

Voir l'anatomie d'un événement d'audit pour les autres champs.

Attach, cp, debug et port-forward

Action côté clientTrace d'auditCe qu'il faut regarder
kubectl attachpods/attachPas de commande : s'attache au processus principal du conteneur
kubectl cppods/exec avec command=tarLe sens (tar cf = copie hors du pod, tar -x… = copie vers le pod) et les chemins
kubectl debug (conteneur éphémère)patch sur pods/ephemeralcontainers, puis pods/attachL'image, et si elle partage l'espace de processus de la cible
kubectl port-forwardpods/portforward avec ports=Quel port : 5432, 6379, 3306, ce sont des bases de données
kubectl proxy / proxy d'API vers un nœudnodes/proxyAccès direct à l'API du kubelet

kubectl cp mérite un examen à part : copier un fichier vers un pod permet de déposer des outils sans tirer de nouvelle image, et copier depuis un pod, c'est de l'exfiltration via l'API server.

kubectl port-forward ouvre un tunnel depuis la machine du client vers le réseau des pods. Il contourne les ingress controllers et, pour le trafic du client, les network policies qui ne filtrent que le trafic entre pods. Un port-forward vers un pod de base de données par une identité qui ne le fait jamais est une piste sérieuse.

nodes/proxy : un exec sans événement exec

La sous-ressource nodes/proxy permet de parler à l'API du kubelet via l'API server. Le kubelet peut exécuter des commandes dans n'importe quel conteneur du nœud et lire ses logs. L'API server audite la requête nodes/proxy, mais pas comme un pods/exec : les détections basées sur l'exec la ratent. Les bonnes pratiques RBAC de Kubernetes classent l'accès à la sous-ressource proxy des nœuds parmi les chemins d'élévation de privilèges.

Pire : quiconque peut joindre le port du kubelet d'un nœud avec des identifiants valides lui parle directement ; ces appels n'atteignent jamais l'API server et n'apparaissent jamais dans le log d'audit. Les réglages d'authentification et d'autorisation du kubelet sont le contrôle à cet endroit.

Distinguer travail d'admin et abus

L'analyseur de la page d'accueil découpe l'exec en quatre règles :

RègleSévéritéLogique
Commande interactive dans un conteneur (exec / attach)Moyennepods/exec ou pods/attach autorisé, par une identité humaine non système
Un compte de service a exécuté une commande dans un conteneurÉlevéeIdem, par une identité system:serviceaccount:
Port-forward vers un podMoyennepods/portforward autorisé, identité non système
API kubelet atteinte via nodes/proxyÉlevéenodes/proxy autorisé, identité non système

Pourquoi ce découpage ? Les workloads ne font presque jamais d'exec dans d'autres pods. Quand un compte de service le fait, c'est en général un token sorti d'un pod et rejoué par une personne, souvent avec un user agent kubectl/ depuis une IP inattendue, ce que deux autres règles détectent (voir vol de tokens de compte de service). Des systèmes de CI et certains opérateurs font des exec légitimes ; mettez-les explicitement en liste blanche après vérification.

Pour les identités humaines, triez chaque session avec quatre questions :

  1. Qui et d'où ? Admin connu, plage d'IP connue, user agent habituel ?
  2. Quand ? Pendant une fenêtre de changement ou un incident, ou à une heure bizarre ?
  3. Où ? Quel namespace et quel pod ; s'agit-il d'un pod privilégié ou d'un composant de kube-system ?
  4. Quoi ? La commande : sh/bash interactif, ou quelque chose comme cat /var/run/secrets/kubernetes.io/serviceaccount/token, curl … | sh, chroot /host, nsenter ?

Un chroot /host ou un nsenter --target 1 dans un pod avec un hostPath sur / ou hostPID, c'est une évasion de conteneur ; voir pods privilégiés et évasion de conteneur.

Requêtes de chasse

Lignes JSON brutes avec jq :

jq -c 'select(.objectRef.subresource == "exec" or .objectRef.subresource == "attach"
              or .objectRef.subresource == "portforward")
       | {t: .requestReceivedTimestamp, user: .user.username, ip: .sourceIPs,
          ns: .objectRef.namespace, pod: .objectRef.name,
          sub: .objectRef.subresource, uri: .requestURI, code: .responseStatus.code}' audit.log

EKS, CloudWatch Logs Insights :

fields @timestamp, user.username, sourceIPs.0, objectRef.namespace, objectRef.name, requestURI
| filter @logStream like "kube-apiserver-audit"
| filter objectRef.subresource in ["exec", "attach", "portforward"]
| sort @timestamp desc

AKS, Log Analytics (table spécifique à la ressource) :

AKSAudit
| where RequestUri has "/exec" or RequestUri has "/attach" or RequestUri has "/portforward"
| project TimeGenerated, User, SourceIps, UserAgent, RequestUri, ResponseStatus

Sur GKE, filtrez Cloud Logging sur protoPayload.methodName:"pods.exec" (ainsi que pods.attach et pods.portforward).

Réduire la surface d'attaque

  • Réservez pods/exec, pods/attach et pods/portforward à un petit groupe « break-glass » ; ce sont des ressources RBAC distinctes, vous pouvez donc accorder get sur les pods sans elles.
  • N'accordez pas nodes/proxy à des personnes ni à des workloads.
  • Alertez sur chaque exec par un compte de service, et sur chaque exec dans kube-system.
  • Conservez le log d'audit : les sessions exec sont courtes et le pod aura peut-être disparu demain.

FAQ

Comment détecter kubectl exec dans les logs d'audit Kubernetes ?

Cherchez les événements dont objectRef.subresource vaut exec (ou attach) sur la ressource pods. Selon les versions du client et du serveur, le verbe est create ou get, et le code de réponse est 101 quand l'upgrade de streaming réussit. La commande figure dans requestURI sous forme de paramètres command= répétés.

Peut-on voir ce qui a été tapé dans un shell kubectl exec ?

Non. Le log d'audit enregistre la requête qui a ouvert la session et sa commande initiale, pas les octets échangés ensuite. Les frappes et les processus dans le conteneur nécessitent un outillage runtime comme Falco ou un EDR sur le nœud.

kubectl cp apparaît-il dans les logs d'audit ?

Oui, sous forme d'exec. kubectl cp lance tar dans le conteneur via pods/exec : l'événement d'audit montre command=tar et ses arguments dans requestURI.

Articles liés

Articles liés

Détecter le crypto-minage dans Kubernetes via les logs d'audit : images et arguments de mineurs, registries inhabituels, persistance CronJob et DaemonSet.
Repérer une évasion de conteneur en préparation dans les logs d'audit Kubernetes : pods privilégiés, hostPID, hostPath sur /, DaemonSets dans kube-system.
Repérer l'escalade de privilèges RBAC Kubernetes dans les logs d'audit : bindings cluster-admin, verbes escalate, bind, impersonate, accès anonyme.

Cet outil n'est ni affilié à The Linux Foundation, à la Cloud Native Computing Foundation (CNCF) ou au projet Kubernetes, ni approuvé ni sponsorisé par eux. Kubernetes et K8s sont des marques déposées de The Linux Foundation. EKS, GKE, AKS et les autres noms sont des marques de leurs propriétaires respectifs.