<h2>Introdução: Por que Rastrear Mudanças no Kubernetes?</h2>
<p>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 <strong>Events</strong> e a <strong>Auditoria</strong> do Kubernetes — dois pilares fundamentais para observabilidade, conformidade e investigação de incidentes.</p>
<p>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.</p>
<h2>Kubernetes Events: Observando o Comportamento do Cluster</h2>
<h3>O que são Events e como funcionam</h3>
<p>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.</p>
<p>Quando você executa <code>kubectl describe pod seu-pod</code>, 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.</p>
<h3>Acessando e interpretando Events</h3>
<p>Existem várias formas de acessar Events. A mais simples é via CLI:</p>
<pre><code class="language-bash"># 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'</code></pre>
<p>Agora vamos examinar um exemplo prático de interpretação. Suponha que você encontre este Event:</p>
<pre><code class="language-yaml">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</code></pre>
<p>Este Event nos diz que um container dentro do Pod <code>nginx-deployment-5f9c8d7e3b1a-xyz12</code> 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 <code>message</code> — 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.</p>
<h3>Exportando Events para análise duradoura</h3>
<p>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.</p>
<p>Aqui está um exemplo usando Fluent Bit para coletar Events:</p>
<pre><code class="language-yaml">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</code></pre>
<p>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:</p>
<pre><code class="language-python">#!/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()</code></pre>
<p>Para executar este controller no cluster:</p>
<pre><code class="language-yaml">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</code></pre>
<h2>Auditoria do Kubernetes: Registrando Todas as Ações na API</h2>
<h3>Entendendo o sistema de auditoria</h3>
<p>A Auditoria do Kubernetes funciona no nível da API Server. Cada requisição que chega ao <code>kube-apiserver</code> 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).</p>
<p>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.</p>
<h3>Configurando a política de auditoria</h3>
<p>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.</p>
<p>Aqui está um exemplo de política de auditoria bem estruturada:</p>
<pre><code class="language-yaml"># 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</code></pre>
<p>Agora você precisa implementar isto no API Server. Se estiver usando um cluster criado com kubeadm, edite o manifesto estático:</p>
<pre><code class="language-bash"># 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</code></pre>
<p>Adicione ou modifique os seguintes argumentos no <code>kube-apiserver</code>:</p>
<pre><code class="language-yaml">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</code></pre>
<h3>Enviando logs de auditoria para um sistema remoto</h3>
<p>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.</p>
<p>Crie o arquivo de configuração do webhook:</p>
<pre><code class="language-yaml"># 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</code></pre>
<p>E aqui está um exemplo de servidor webhook que recebe esses eventos:</p>
<pre><code class="language-python">#!/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')</code></pre>
<h3>Analisando logs de auditoria</h3>
<p>Depois de armazenar seus logs, você pode consultá-los de diversas formas. Se estiver usando Elasticsearch + Kibana:</p>
<pre><code class="language-bash"># 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
}'</code></pre>
<p>Ou se estiver consultando arquivos locais de auditoria diretamente:</p>
<pre><code class="language-bash"># 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)'</code></pre>
<h2>Integração Prática: Monitorando Eventos e Auditoria em Conjunto</h2>
<h3>Cenário Real: Detectando Alterações Suspeitas</h3>
<p>Imagine que você receba um alerta de que um Secret foi deletado do namespace <code>production</code>. Você precisa rastrear o quê, quem fez e por quê. Veja como usar Events e Auditoria juntos:</p>
<pre><code class="language-bash"># 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</code></pre>
<h3>Dashboard de Segurança com Prometheus e Grafana</h3>
<p>Para monitoramento contínuo, você pode exportar eventos de auditoria como métricas Prometheus:</p>
<pre><code class="language-python">#!/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)</code></pre>
<p>Com isso rodando, você pode criar um dashboard Grafana com queries como:</p>
<pre><code class="language-promql"># 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])</code></pre>
<h2>Conclusão</h2>
<p>Você aprendeu que <strong>Events</strong> e <strong>Auditoria</strong> 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. <strong>Eventos são efêmeros</strong> 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. <strong>Na prática</strong>, 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://kubernetes.io/docs/tasks/debug-application-cluster/audit/" target="_blank" rel="noopener noreferrer">Documentação Oficial: Kubernetes Audit Logging</a></li>
<li><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#event-v1-core" target="_blank" rel="noopener noreferrer">Kubernetes Events Documentation</a></li>
<li><a href="https://kubernetes.io/docs/concepts/security/controlling-access/" target="_blank" rel="noopener noreferrer">API Server Security Documentation</a></li>
<li><a href="https://www.cncf.io/blog/2019/09/19/kubernetes-audit-logging/" target="_blank" rel="noopener noreferrer">Advanced Auditing with Kubernetes</a></li>
<li><a href="https://falco.org/" target="_blank" rel="noopener noreferrer">Falco: Runtime Security Monitoring</a></li>
</ul>
<p><!-- FIM --></p>