O que Todo Dev Deve Saber sobre Kubernetes Events e Auditoria: Rastreando Mudanças no Cluster Já leu

Introdução: Por que Rastrear Mudanças no Kubernetes? No dia a dia de um cluster Kubernetes em produção, diversas ações ocorrem constantemente: deploys, alterações de configuração, exclusões de recursos, mudanças de permissões. Sem mecanismos adequados de rastreamento, fica impossível responder perguntas críticas como "quem deletou esse Pod?" ou "quando exatamente essa configuração foi alterada?". É aqui que entram os Events e a Auditoria do Kubernetes — dois pilares fundamentais para observabilidade, conformidade e investigação de incidentes. Os Events são notificações em tempo real sobre o que está acontecendo no cluster (falhas de scheduling, restarts, avisos). A Auditoria, por sua vez, registra todas as requisições à API do Kubernetes, criando um histórico completo e imutável de quem fez o quê e quando. Dominar esses conceitos não é apenas uma questão de curiosidade técnica; é essencial para qualquer plataforma que pretenda ser segura, auditável e pronta para produção. Kubernetes Events: Observando o Comportamento do Cluster O que são Events e como funcionam Um

Introdução: Por que Rastrear Mudanças no Kubernetes?

No dia a dia de um cluster Kubernetes em produção, diversas ações ocorrem constantemente: deploys, alterações de configuração, exclusões de recursos, mudanças de permissões. Sem mecanismos adequados de rastreamento, fica impossível responder perguntas críticas como "quem deletou esse Pod?" ou "quando exatamente essa configuração foi alterada?". É aqui que entram os Events e a Auditoria do Kubernetes — dois pilares fundamentais para observabilidade, conformidade e investigação de incidentes.

Os Events são notificações em tempo real sobre o que está acontecendo no cluster (falhas de scheduling, restarts, avisos). A Auditoria, por sua vez, registra todas as requisições à API do Kubernetes, criando um histórico completo e imutável de quem fez o quê e quando. Dominar esses conceitos não é apenas uma questão de curiosidade técnica; é essencial para qualquer plataforma que pretenda ser segura, auditável e pronta para produção.

Kubernetes Events: Observando o Comportamento do Cluster

O que são Events e como funcionam

Um Event no Kubernetes é um objeto que documenta algo que aconteceu em um momento específico. Diferente de logs de aplicação tradicionais, Events são recursos nativos da API do Kubernetes, armazenados no etcd e organizados por namespace. Cada Event contém informações sobre o que ocorreu, quando ocorreu, quantas vezes se repetiu e qual objeto foi afetado.

Quando você executa kubectl describe pod seu-pod, as linhas finais mostram exatamente os Events associados àquele Pod. Esses Events são gerados por componentes do cluster como o kubelet, scheduler, e controladores diversos. Por padrão, Events são mantidos por apenas 1 hora no cluster, após o qual são automaticamente deletados — por isso precisamos de mecanismos para exportá-los se desejamos análise histórica.

Acessando e interpretando Events

Existem várias formas de acessar Events. A mais simples é via CLI:

# Listar todos os Events do cluster
kubectl get events --all-namespaces

# Listar Events de um namespace específico
kubectl get events -n production

# Ver Events em tempo real com watch
kubectl get events --all-namespaces --watch

# Obter detalhes completos de Events ordenados por timestamp
kubectl get events --all-namespaces --sort-by='.lastTimestamp'

Agora vamos examinar um exemplo prático de interpretação. Suponha que você encontre este Event:

apiVersion: v1
kind: Event
metadata:
  name: nginx-deployment-1a2b3c4d5e6f
  namespace: production
involvedObject:
  apiVersion: v1
  kind: Pod
  name: nginx-deployment-5f9c8d7e3b1a-xyz12
  namespace: production
reason: BackOff
message: "Back-off restarting failed container"
count: 5
firstTimestamp: 2024-01-15T10:30:00Z
lastTimestamp: 2024-01-15T10:45:00Z
type: Warning
source:
  component: kubelet
  host: worker-node-02

Este Event nos diz que um container dentro do Pod nginx-deployment-5f9c8d7e3b1a-xyz12 começou a falhar repetidamente. O kubelet tentou reiniciá-lo 5 vezes (count), começando às 10:30 e última tentativa às 10:45. O motivo está no campo message — provavelmente a aplicação está crashando imediatamente após iniciar. Este é exatamente o tipo de informação que permite diagnóstico rápido de problemas.

Exportando Events para análise duradoura

Como Events são efêmeros (expiram em 1 hora por padrão), você precisará exportá-los para um sistema de armazenamento duradouro. A abordagem mais comum é usar um sidecar que "escuta" Events e os envia para um sistema de logging centralizado.

Aqui está um exemplo usando Fluent Bit para coletar Events:

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: logging
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         5
        Log_Level     info
        Daemon        off

    [INPUT]
        Name              systemd
        Tag               host.*
        Systemd_Filter    _SYSTEMD_UNIT=kubelet.service
        Read_From_Tail    On

    [INPUT]
        Name              tail
        Tag               kube.events
        Path              /var/log/kubernetes/events.log
        Parser            json
        Refresh_Interval  5
        Mem_Buf_Limit     50MB

    [OUTPUT]
        Name   stdout
        Match  kube.events

    [OUTPUT]
        Name   es
        Match  kube.events
        Host   elasticsearch.logging
        Port   9200
        Index  kubernetes-events

Uma solução mais robusta é usar um controller customizado que "assina" Events em tempo real. Aqui está um exemplo em Python usando a cliente oficial do Kubernetes:

#!/usr/bin/env python3
"""
Controller que monitora Events do Kubernetes e os envia para stdout (ou um destino real)
"""

import json
from kubernetes import client, config, watch
from datetime import datetime

def log_event(event):
    """Formata e envia o Event para um sistema externo"""
    obj = event['object']

    event_data = {
        'timestamp': datetime.utcnow().isoformat(),
        'event_name': obj.metadata.name,
        'namespace': obj.metadata.namespace,
        'involved_object': {
            'kind': obj.involved_object.kind,
            'name': obj.involved_object.name,
            'namespace': obj.involved_object.namespace,
        },
        'reason': obj.reason,
        'message': obj.message,
        'count': obj.count,
        'first_timestamp': obj.first_timestamp.isoformat() if obj.first_timestamp else None,
        'last_timestamp': obj.last_timestamp.isoformat() if obj.last_timestamp else None,
        'type': obj.type,
        'source': {
            'component': obj.source.component,
            'host': obj.source.host,
        }
    }

    print(json.dumps(event_data))
    # Aqui você enviaria para Elasticsearch, S3, ou seu sistema de logging

def main():
    # Carregar configuração do kubeconfig ou em-cluster
    try:
        config.load_incluster_config()
    except:
        config.load_kube_config()

    v1 = client.CoreV1Api()
    w = watch.Watch()

    # Assistir a Events em todos os namespaces
    for event in w.stream(v1.list_event_for_all_namespaces):
        log_event(event)

if __name__ == '__main__':
    main()

Para executar este controller no cluster:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: event-exporter
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: event-exporter
rules:
- apiGroups: [""]
  resources: ["events"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: event-exporter
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: event-exporter
subjects:
- kind: ServiceAccount
  name: event-exporter
  namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: event-exporter
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: event-exporter
  template:
    metadata:
      labels:
        app: event-exporter
    spec:
      serviceAccountName: event-exporter
      containers:
      - name: event-exporter
        image: seu-registro/event-exporter:1.0
        imagePullPolicy: Always

Auditoria do Kubernetes: Registrando Todas as Ações na API

Entendendo o sistema de auditoria

A Auditoria do Kubernetes funciona no nível da API Server. Cada requisição que chega ao kube-apiserver passa por um pipeline de auditoria que pode registrar informações detalhadas: quem fez a requisição (usuário, SA), o quê foi solicitado (verbo, recurso), quando foi feito (timestamp), e qual foi a resposta (sucesso/falha).

A auditoria é configurada no API Server através de uma política (um arquivo YAML) que define quais requisições devem ser registradas, em qual nível de detalhe, e para onde os logs devem ir. Sem auditoria configurada, o API Server não registra nada. Com auditoria bem configurada, você tem um "filme" completo de todas as operações no cluster — essencial para conformidade regulatória (PCI-DSS, HIPAA, SOC2) e investigações de segurança.

Configurando a política de auditoria

A política de auditoria é um arquivo YAML contendo regras que determinam se e como registrar cada requisição. Cada regra é avaliada sequencialmente; a primeira que corresponde determina o comportamento.

Aqui está um exemplo de política de auditoria bem estruturada:

# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy

# Level RequestResponse = registra requisição completa e resposta
# Level Metadata = apenas informações sobre a requisição (sem body)
# Level Request = apenas a requisição (sem resposta)
# Level None = não registra

rules:
  # Registre todos os eventos para pods em namespace production
  - level: RequestResponse
    namespaces: ["production"]
    verbs: ["create", "update", "patch", "delete"]
    resources: ["pods", "pods/log"]

  # Registre qualquer alteração em RBAC
  - level: RequestResponse
    verbs: ["create", "update", "patch", "delete"]
    resources:
      - "clusterrolebindings"
      - "clusterroles"
      - "rolebindings"
      - "roles"

  # Registre modificações em ConfigMaps sensíveis
  - level: RequestResponse
    namespaces: ["kube-system", "production"]
    resources: ["configmaps", "secrets"]
    verbs: ["create", "update", "patch", "delete"]

  # Registre com mais detalhes ações de autenticação
  - level: RequestResponse
    usernames: ["system:*"]
    verbs: ["create", "update", "patch", "delete"]

  # Registre READ requests apenas no nível Metadata (menos verboso)
  - level: Metadata
    omitStages:
      - RequestReceived
    verbs: ["get", "list", "watch"]

  # Registre tudo relacionado a segurança
  - level: RequestResponse
    resources:
      - "networkpolicies"
      - "podsecuritypolicies"
      - "securitycontextconstraints"

  # Para tudo mais, registre apenas Metadata
  - level: Metadata
    omitStages:
      - RequestReceived

Agora você precisa implementar isto no API Server. Se estiver usando um cluster criado com kubeadm, edite o manifesto estático:

# Criar o arquivo de política
sudo cp audit-policy.yaml /etc/kubernetes/policies/audit-policy.yaml

# Editar o manifesto do API Server
sudo nano /etc/kubernetes/manifests/kube-apiserver.yaml

Adicione ou modifique os seguintes argumentos no kube-apiserver:

apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    image: k8s.gcr.io/kube-apiserver:v1.28.0
    command:
    - kube-apiserver
    - --audit-policy-file=/etc/kubernetes/policies/audit-policy.yaml
    - --audit-log-maxage=30
    - --audit-log-maxbackup=10
    - --audit-log-maxsize=100
    # Envia logs para um servidor remoto
    - --audit-webhook-config-file=/etc/kubernetes/policies/audit-webhook.yaml
    # Salva logs localmente também
    - --audit-log-path=/var/log/kubernetes/audit.log

    volumeMounts:
    - name: audit
      mountPath: /var/log/kubernetes
    - name: audit-policy
      mountPath: /etc/kubernetes/policies

  volumes:
  - name: audit
    hostPath:
      path: /var/log/kubernetes
  - name: audit-policy
    hostPath:
      path: /etc/kubernetes/policies

Enviando logs de auditoria para um sistema remoto

Em produção, você não quer armazenar apenas localmente. Use webhook de auditoria para enviar eventos para Elasticsearch, Fluentd, ou qualquer sistema de logging.

Crie o arquivo de configuração do webhook:

# audit-webhook.yaml
apiVersion: v1
kind: Config
clusters:
- name: falconlogscale
  cluster:
    server: https://seu-falconlogscale.exemplo.com:8080/api/v1/audit-events
    certificate-authority: /etc/kubernetes/policies/ca-bundle.crt
contexts:
- context:
    cluster: falconlogscale
    user: audit-user
  name: default-context
current-context: default-context
preferences: {}
users:
- name: audit-user
  user:
    client-certificate: /etc/kubernetes/policies/client.crt
    client-key: /etc/kubernetes/policies/client.key

E aqui está um exemplo de servidor webhook que recebe esses eventos:

#!/usr/bin/env python3
"""
Servidor webhook que recebe eventos de auditoria do Kubernetes
"""

from flask import Flask, request, jsonify
import json
import logging
from datetime import datetime

app = Flask(__name__)

# Configurar logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.route('/api/v1/audit-events', methods=['POST'])
def receive_audit_events():
    """Recebe e processa eventos de auditoria do API Server"""

    try:
        payload = request.get_json()

        # A payload é um objeto AuditEventList
        if payload and 'items' in payload:
            for event in payload['items']:
                # Processar cada evento
                audit_entry = {
                    'timestamp': datetime.utcnow().isoformat(),
                    'level': event.get('level'),
                    'user': event.get('user', {}).get('username', 'unknown'),
                    'verb': event.get('verb'),
                    'object_ref': {
                        'resource': event.get('objectRef', {}).get('resource'),
                        'namespace': event.get('objectRef', {}).get('namespace'),
                        'name': event.get('objectRef', {}).get('name'),
                    },
                    'request_object': event.get('requestObject'),
                    'response_object': event.get('responseObject'),
                    'source_ip': event.get('sourceIPs', ['unknown'])[0],
                    'user_agent': event.get('userAgent', 'unknown'),
                    'response_code': event.get('responseStatus', {}).get('code'),
                }

                # Log para stdout/arquivo
                logger.info(json.dumps(audit_entry))

                # Aqui você enviaria para seu sistema de logging (Splunk, ELK, etc)
                # send_to_elasticsearch(audit_entry)

        return jsonify({'status': 'received'}), 200

    except Exception as e:
        logger.error(f"Erro ao processar audit event: {e}")
        return jsonify({'error': str(e)}), 400

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, ssl_context='adhoc')

Analisando logs de auditoria

Depois de armazenar seus logs, você pode consultá-los de diversas formas. Se estiver usando Elasticsearch + Kibana:

# Via curl, buscar todos os eventos de criação de Secrets nos últimos 7 dias
curl -X GET "localhost:9200/audit-logs/_search" -H 'Content-Type: application/json' -d'{
  "query": {
    "bool": {
      "must": [
        { "match": { "verb": "create" } },
        { "match": { "objectRef.resource": "secrets" } },
        { "range": { "timestamp": { "gte": "now-7d" } } }
      ]
    }
  },
  "size": 100
}'

Ou se estiver consultando arquivos locais de auditoria diretamente:

# Extrair todos os eventos onde alguém deletou um Pod
cat /var/log/kubernetes/audit.log | jq 'select(.verb == "delete" and .objectRef.resource == "pods")'

# Contar quantas vezes cada usuário fez "create"
cat /var/log/kubernetes/audit.log | jq 'select(.verb == "create") | .user.username' | sort | uniq -c

# Buscar tentativas de acesso não autorizado
cat /var/log/kubernetes/audit.log | jq 'select(.responseStatus.code == 403)'

Integração Prática: Monitorando Eventos e Auditoria em Conjunto

Cenário Real: Detectando Alterações Suspeitas

Imagine que você receba um alerta de que um Secret foi deletado do namespace production. Você precisa rastrear o quê, quem fez e por quê. Veja como usar Events e Auditoria juntos:

# Passo 1: Procurar o evento de deleção nos logs de auditoria
grep -i "delete" /var/log/kubernetes/audit.log | jq 'select(.objectRef.resource == "secrets" and .objectRef.namespace == "production")'

# Saída esperada:
# {
#   "level": "RequestResponse",
#   "user": { "username": "admin-user" },
#   "verb": "delete",
#   "objectRef": { "resource": "secrets", "namespace": "production", "name": "db-password" },
#   "requestTimestamp": "2024-01-15T14:32:00.123456Z",
#   "responseStatus": { "code": 200 }
# }

# Passo 2: Encontrar Events relacionados a Pods naquele namespace na mesma época
kubectl get events -n production --sort-by='.lastTimestamp' | grep "14:3"

# Passo 3: Se encontrar crashes de pods após deleção do Secret, correlacionar

Dashboard de Segurança com Prometheus e Grafana

Para monitoramento contínuo, você pode exportar eventos de auditoria como métricas Prometheus:

#!/usr/bin/env python3
"""
Exportador que converte eventos de auditoria em métricas Prometheus
"""

from prometheus_client import Counter, Histogram, start_http_server
import json
import time

# Métricas
audit_requests_total = Counter(
    'k8s_audit_requests_total',
    'Total de requisições auditadas',
    ['verb', 'resource', 'namespace', 'user']
)

audit_errors_total = Counter(
    'k8s_audit_errors_total',
    'Total de erros (4xx, 5xx) em requisições',
    ['verb', 'resource', 'code']
)

audit_request_duration_seconds = Histogram(
    'k8s_audit_request_duration_seconds',
    'Duração das requisições de API',
    ['verb', 'resource']
)

def process_audit_event(event):
    """Processa um evento de auditoria e atualiza métricas"""

    verb = event.get('verb', 'unknown')
    resource = event.get('objectRef', {}).get('resource', 'unknown')
    namespace = event.get('objectRef', {}).get('namespace', 'unknown')
    username = event.get('user', {}).get('username', 'unknown')
    response_code = event.get('responseStatus', {}).get('code', 0)

    # Incrementar contador total
    audit_requests_total.labels(
        verb=verb,
        resource=resource,
        namespace=namespace,
        user=username
    ).inc()

    # Registrar erros
    if response_code >= 400:
        audit_errors_total.labels(
            verb=verb,
            resource=resource,
            code=response_code
        ).inc()

    # Registrar duração (simulado aqui)
    audit_request_duration_seconds.labels(
        verb=verb,
        resource=resource
    ).observe(0.05)  # Na prática, calcular de verdade

if __name__ == '__main__':
    start_http_server(8000)  # Expositor Prometheus na porta 8000

    # Ler eventos de auditoria continuamente
    with open('/var/log/kubernetes/audit.log', 'r') as f:
        while True:
            line = f.readline()
            if line:
                event = json.loads(line)
                process_audit_event(event)
            else:
                time.sleep(1)

Com isso rodando, você pode criar um dashboard Grafana com queries como:

# Taxa de operações delete por hora
increase(k8s_audit_requests_total{verb="delete"}[1h])

# Top 10 usuários por número de requisições
topk(10, sum(k8s_audit_requests_total) by (user))

# Taxa de erro por recurso
increase(k8s_audit_errors_total[5m])

Conclusão

Você aprendeu que Events e Auditoria são duas camadas complementares de observabilidade no Kubernetes: Events capturá o comportamento operacional do cluster (falhas, restarts, avisos), enquanto Auditoria registra todas as ações na API Server com identidade de quem as fez. Eventos são efêmeros por padrão (expiram em 1 hora) e devem ser exportados para análise duradoura; Auditoria é persistente por design e essencial para conformidade, segurança e investigações. Na prática, combinar ambos permite diagnóstico rápido de problemas e auditoria completa de mudanças — desde "por que esse Pod não sobe?" até "quem deletou esse Secret e quando?". Dominar essas ferramentas transforma você de um operador que apenas reage a problemas para um que pode investigá-los, preventivamente, com confiança.

Referências


Artigos relacionados