Skip to content

Esta herramienta no está afiliada a The Linux Foundation, la Cloud Native Computing Foundation (CNCF) ni el proyecto Kubernetes, ni está respaldada ni patrocinada por ellos. Kubernetes y K8s son marcas registradas de The Linux Foundation. EKS, GKE, AKS y otros nombres son marcas comerciales de sus respectivos propietarios.

Política de auditoría de Kubernetes: qué registrar

Cómo escribir una política de auditoría de Kubernetes que capture la evidencia útil (exec, RBAC, workloads, tokens) sin registrar secrets ni ahogarse en ruido.

Publicado el 5 min de lectura

TL;DR. Una política de auditoría es una lista ordenada de reglas; la primera que coincide fija el nivel de auditoría de cada solicitud. Para forense necesitas Metadata en todo, Request o RequestResponse en las escrituras de objetos RBAC, pods y controladores de workloads, Request en pods/exec, pods/attach, pods/portforward, nodes/proxy y serviceaccounts/token, y estrictamente Metadata en secrets, configmaps y token reviews. Descarta los health checks y la cháchara de kubelets y kube-proxy. Envía el resultado fuera del clúster y consérvalo al menos 90 días.

Un API server autogestionado no escribe nada hasta que le das una política: la documentación de auditoría de Kubernetes es explícita, sin --audit-policy-file no se registra ningún evento. En EKS, GKE y AKS, el proveedor fija la política por ti y tu única decisión es qué logs exportar (consulta la guía de exportación para EKS, GKE y AKS). En ambos casos, saber cómo es una buena política te dice qué evidencia puedes esperar.

Cómo se evalúan las reglas

Cada regla puede filtrar por users, userGroups, verbs, resources (con grupo de API y, opcionalmente, resourceNames), namespaces y nonResourceURLs. Las reglas se procesan en orden y gana la primera que coincide. Una solicitud que no coincide con ninguna regla no se registra. Dos consecuencias prácticas:

  • Coloca tus exclusiones (level: None) y tus reglas de «nunca registrar cuerpos» antes de las reglas amplias.
  • Termina con un level: Metadata general para que nada se escape en silencio.

omitStages elimina stages de forma global o por regla. Casi todo el mundo omite RequestReceived, que duplica el evento final. omitManagedFields: true quita el voluminoso metadata.managedFields de los cuerpos registrados.

Qué necesita quien investiga, y por qué

EvidenciaNivel mínimoPor qué
Quién llamó a qué, desde dónde y con qué resultadoMetadata en todoIdentidad, IP de origen, verbo, objeto, código: la columna vertebral de cualquier línea de tiempo
Specs de pods y workloads (pods, deployments, daemonsets, statefulsets, jobs, cronjobs) en create / update / patchRequestSin el cuerpo no puedes ver privileged, hostPID, un hostPath ni la imagen
Escrituras RBAC (roles, rolebindings, clusterroles, clusterrolebindings)Request o RequestResponseEl roleRef y los subjects dicen si un binding concede cluster-admin y a quién
pods/exec, pods/attach, pods/portforward, nodes/proxyBasta con Metadata, Request también sirveEl comando está en requestURI; la sesión en sí nunca se registra
serviceaccounts/tokenRequest, no RequestResponseLa solicitud muestra la caducidad y la audiencia pedidas; la respuesta contiene el propio token
secrets, configmaps, tokenreviewsSolo MetadataLos cuerpos contienen credenciales; registrarlos convierte el audit log en un almacén de credenciales
Borrado de eventsMetadataBorrar Events es una medida antiforense barata

Una política orientada a forense

Esta política es un punto de partida, no algo para pegar tal cual. Pruébala en un clúster que no sea de producción y vigila el volumen de logs.

apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
  - "RequestReceived"
omitManagedFields: true
rules:
  # 1. Noise: health checks and high-frequency system reads
  - level: None
    nonResourceURLs: ["/healthz*", "/livez*", "/readyz*", "/version"]
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: ""
        resources: ["endpoints", "services", "services/status"]
  - level: None
    userGroups: ["system:nodes"]
    verbs: ["get"]
    resources:
      - group: ""
        resources: ["nodes", "nodes/status"]

  # 2. Credentials: never log bodies
  - level: Metadata
    resources:
      - group: ""
        resources: ["secrets", "configmaps"]
      - group: "authentication.k8s.io"
        resources: ["tokenreviews"]

  # 3. Interactive access and token minting (request only: the
  #    TokenRequest response contains the token)
  - level: Request
    resources:
      - group: ""
        resources: ["pods/exec", "pods/attach", "pods/portforward",
                    "nodes/proxy", "serviceaccounts/token"]

  # 4. Events: keep deletions, drop the rest
  - level: Metadata
    verbs: ["delete", "deletecollection"]
    resources:
      - group: ""
        resources: ["events"]
      - group: "events.k8s.io"
        resources: ["events"]
  - level: None
    resources:
      - group: ""
        resources: ["events"]
      - group: "events.k8s.io"
        resources: ["events"]

  # 5. Writes to workloads, RBAC, service accounts, admission: full bodies
  - level: RequestResponse
    verbs: ["create", "update", "patch", "delete", "deletecollection"]
    resources:
      - group: ""
        resources: ["pods", "serviceaccounts", "namespaces", "nodes"]
      - group: "apps"
      - group: "batch"
      - group: "rbac.authorization.k8s.io"
      - group: "admissionregistration.k8s.io"

  # 6. Everything else
  - level: Metadata

Algunas decisiones que conviene explicar:

  • La regla 2 va antes que la 5. Los secrets deben quedarse en Metadata incluso en create y update, así que la regla de secrets tiene que coincidir primero.
  • La regla 3 usa Request. Los subrecursos de streaming no tienen un cuerpo significativo; lo que necesitas (el comando, el contenedor) está en la URI. Para serviceaccounts/token, RequestResponse registraría un bearer token válido.
  • Las actualizaciones de estado de los kubelets (patches de nodes/status, pods/status) caen en la regla 6, en Metadata. Es más que suficiente para investigar y mucho más barato que los cuerpos.
  • Las lecturas se quedan en Metadata. Registrar los cuerpos de respuesta de los list es lo que dispara el tamaño de los audit logs; rara vez ayuda en una investigación.

Como comparación, la política que AWS publica en la guía de buenas prácticas de EKS sigue la misma estructura: Metadata para secrets, configmaps y token reviews, Request para serviceaccounts/token, cuerpos completos para la mayoría de grupos de API conocidos. Una diferencia notable: descarta por completo los events, así que los borrados de Events no son visibles en EKS.

Activarla en un API server autogestionado

En un plano de control tipo kubeadm, el API server corre como pod estático. Añade los flags a /etc/kubernetes/manifests/kube-apiserver.yaml y monta la política y el directorio de logs:

- --audit-policy-file=/etc/kubernetes/audit-policy.yaml
- --audit-log-path=/var/log/kubernetes/audit/audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100

maxage va en días y maxsize en megabytes. El kubelet reinicia el API server cuando cambia el manifiesto. Distribuciones como k3s y RKE2 exponen los mismos flags a través de su propia configuración; consulta su documentación para las claves exactas.

La documentación también señala que la auditoría aumenta el consumo de memoria del API server, en proporción a lo que registras. Otro motivo para reservar los cuerpos a las escrituras que importan.

Saca los logs de la máquina

Un atacante con cluster-admin y un pod privilegiado puede leer y editar archivos en los nodos del plano de control. Unos logs que solo existen ahí son logs que controla el atacante. Dos opciones:

  • El backend webhook (--audit-webhook-config-file) envía lotes a un endpoint HTTP: un recolector de logs, un SIEM, una cola.
  • El backend de log más un agente de envío (Fluent Bit, Vector, el agente del proveedor cloud) lee audit.log y lo manda a un almacenamiento central.

La guía de hardening de Kubernetes de la NSA y la CISA también recomienda activar la auditoría y guardar los logs fuera del clúster. Conserva al menos 90 días: el acceso inicial suele preceder a la detección en semanas.

Comprueba tu política con detecciones reales

Una buena prueba: ejecuta una secuencia maliciosa conocida en un clúster de laboratorio (exec en un pod, creación de un pod privilegiado con un hostPath de /, binding de una cuenta de servicio a cluster-admin), exporta el log y arrástralo al analizador de audit logs. Si no aparecen los hallazgos de pod privilegiado o de cluster-admin, los cuerpos correspondientes no se están registrando. La sección de referencia del analizador enumera las detecciones; la guía paso a paso explica cómo leer el resultado.

Artículos relacionados

Artículos relacionados

Los puntos ciegos de los audit logs de Kubernetes: actividad en contenedores, acceso a kubelet y etcd, huecos de política, campos falsificables y qué los cubre.
El formato del audit log de Kubernetes campo a campo: stages, niveles, user, sourceIPs, objectRef, responseStatus, anotaciones y lo que importa en forense.
Un incidente ficticio de Kubernetes, paso a paso en el audit log: token de dashboard expuesto, recon con can-i, robo de secrets, DaemonSet privilegiado, XMRig.

Esta herramienta no está afiliada a The Linux Foundation, la Cloud Native Computing Foundation (CNCF) ni el proyecto Kubernetes, ni está respaldada ni patrocinada por ellos. Kubernetes y K8s son marcas registradas de The Linux Foundation. EKS, GKE, AKS y otros nombres son marcas comerciales de sus respectivos propietarios.