Introdução ao eBPF para monitoramento e segurança no Linux
O eBPF (extended Berkeley Packet Filter) representa uma das inovações mais significativas no kernel Linux das últimas décadas. Originalmente concebido como uma evolução do BPF clássico — usado para filtrar pacotes de rede com ferramentas como tcpdump —, o eBPF moderno transformou-se em uma máquina virtual sandboxizada dentro do kernel, capaz de executar programas personalizados de forma segura e eficiente. Esta tecnologia permite que desenvolvedores e administradores de sistemas inspecionem, monitorem e modifiquem o comportamento do kernel sem a necessidade de carregar módulos de kernel arriscados ou recompilar o sistema.
1. O que é eBPF e por que ele revolucionou o kernel Linux
1.1. Definição e origem: da BPF clássica ao eBPF moderno
A BPF clássica surgiu em 1992 como um mecanismo para filtrar pacotes de rede no espaço do kernel. Sua implementação era limitada a um conjunto restrito de instruções e a contextos exclusivamente de rede. O eBPF, introduzido no kernel 3.18 (2014), expandiu drasticamente esse conceito: agora é possível escrever programas que se anexam a qualquer ponto do kernel (kprobes, tracepoints, eventos de perf, interfaces de rede) e executam código arbitrário, desde que aprovado pelo verificador de segurança.
1.2. Funcionamento básico: sandbox virtual dentro do kernel
O eBPF funciona como uma máquina virtual de 64 bits com 11 registradores, um contador de programa e uma pilha de 512 bytes. Programas eBPF são compilados para bytecode, verificados por um analisador estático (verifier) que garante que não acessem memória inválida, não entrem em loops infinitos e não comprometam a estabilidade do kernel. Após a verificação, o bytecode é compilado Just-In-Time (JIT) para instruções nativas da arquitetura.
1.3. Vantagens sobre ferramentas tradicionais
Diferentemente de strace (que causa overhead significativo ao capturar cada chamada de sistema via ptrace) ou de módulos de kernel (que podem crashar o sistema), o eBPF oferece:
- Baixo overhead: programas são executados no contexto do kernel sem trocas de contexto desnecessárias
- Segurança: o verificador impede código malicioso ou instável
- Dinamismo: programas podem ser carregados e removidos sem reinicialização
- Observabilidade profunda: acesso a estruturas internas do kernel que ferramentas userspace não alcançam
2. Arquitetura e componentes do eBPF
2.1. Mapas eBPF: compartilhamento de dados entre kernel e userspace
Mapas são estruturas de dados genéricas (hash tables, arrays, filas, pilhas) que permitem comunicação bidirecional entre programas eBPF e processos userspace. Um programa de kernel pode contar pacotes em um mapa, enquanto um programa userspace lê esses contadores periodicamente.
// Exemplo conceitual: mapa do tipo hash
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(u32),
.value_size = sizeof(u64),
.max_entries = 1024,
};
2.2. Programas eBPF e hooks
Programas eBPF podem ser anexados a diversos pontos de instrumentação:
- kprobes/kretprobes: interceptam entrada e saída de funções do kernel
- tracepoints: pontos estáticos definidos no código do kernel
- perf_events: eventos de performance (CPU, cache, etc.)
- XDP (eXpress Data Path): processamento de pacotes no driver de rede, antes do stack de rede do kernel
// Exemplo com bpftrace: rastrear chamadas à função open
kprobe:do_sys_open
{
printf("Arquivo aberto: %s\n", str(arg1));
}
2.3. Verificador (verifier) e segurança
O verificador realiza duas passagens sobre o bytecode: na primeira, constrói um grafo de fluxo de controle; na segunda, simula a execução de cada instrução para garantir que:
- Nenhum acesso a memória fora dos limites
- Nenhum loop não terminável (loops são permitidos apenas se o limite for conhecido estaticamente)
- Nenhum acesso a ponteiros não verificados
- O programa termina em um número finito de passos
3. Ecossistema de ferramentas e frameworks
3.1. BCC (BPF Compiler Collection)
BCC fornece bindings Python e Lua para escrever programas eBPF com sintaxe C embutida. Exemplo prático de monitoramento de chamadas open:
#!/usr/bin/python3
from bcc import BPF
program = """
int kprobe__sys_openat(struct pt_regs *ctx) {
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_trace_printk("Processo %s abriu arquivo\\n", comm);
return 0;
}
"""
b = BPF(text=program)
b.trace_print()
3.2. bpftrace: linguagem de alto nível para tracing
bpftrace oferece uma sintaxe semelhante a awk, ideal para diagnósticos rápidos:
# Contar chamadas de sistema por processo
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
3.3. libbpf e CO-RE
CO-RE (Compile Once, Run Everywhere) resolve o problema de portabilidade entre diferentes versões de kernel. Programas são compilados contra headers de kernel genéricos e, no momento do carregamento, o BTF (BPF Type Format) do kernel alvo é usado para ajustar offsets de estruturas.
4. Monitoramento de desempenho com eBPF
4.1. Rastreamento de chamadas de sistema e latência de processos
Ferramentas como execsnoop (do BCC) rastreiam execuções de novos processos:
# Monitorar execuções de binários
sudo execsnoop
TIME PID PPID ARGS
08:12:33 12345 12344 /usr/bin/ls -la
4.2. Análise de rede: XDP para filtragem e inspeção de pacotes
XDP permite processar pacotes no driver de rede, antes mesmo de alocar sk_buff:
// Exemplo conceitual de programa XDP que descarta pacotes ICMP
SEC("xdp")
int xdp_drop_icmp(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
struct iphdr *ip;
if ((void*)eth + sizeof(*eth) > data_end) return XDP_PASS;
if (eth->h_proto != htons(ETH_P_IP)) return XDP_PASS;
ip = data + sizeof(*eth);
if ((void*)ip + sizeof(*ip) > data_end) return XDP_PASS;
if (ip->protocol == IPPROTO_ICMP) return XDP_DROP;
return XDP_PASS;
}
4.3. Monitoramento de I/O de disco
Ferramentas do BCC como biolatency e fileslower medem latência de operações de disco:
# Medir latência de I/O de bloco
sudo biolatency -m
Tracing block device I/O... Hit Ctrl-C to end.
usecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 12 |********************|
5. Casos de uso em segurança com eBPF
5.1. Detecção de anomalias em tempo real
O eBPF pode monitorar execuções suspeitas, como processos que tentam usar ptrace em processos não filhos:
# bpftrace: detectar chamadas ptrace
tracepoint:syscalls:sys_enter_ptrace
{
if (pid != uid) {
printf("ptrace suspeito: PID %d (%s) alvo %d\n", pid, comm, args->pid);
}
}
5.2. Prevenção de ataques com XDP
XDP pode ser usado para mitigar ataques DDoS no nível do driver, descartando pacotes maliciosos antes que consumam recursos do kernel:
# Carregar programa XDP que bloqueia IPs maliciosos
sudo ip link set dev eth0 xdp obj blocklist.o
5.3. Ferramentas de runtime security
Falco (da Sysdig) e Tetragon (da Cilium) usam eBPF para auditoria de contêineres:
- Falco: detecta shells interativos em contêineres, montagens privilegiadas, conexões de rede suspeitas
- Tetragon: fornece visibilidade completa de chamadas de sistema, processos e atividades de rede em clusters Kubernetes
# Exemplo de regra Falco
- rule: Terminal shell in container
desc: Detecta shell interativo em contêiner
condition: container.id != "" and proc.name = "bash" and evt.type = execve
output: "Shell detectado em contêiner (user=%user.name container=%container.id)"
priority: WARNING
6. Limitações, desafios e boas práticas
6.1. Limitações do verificador
O verificador impõe restrições rigorosas: programas não podem ter loops não verificados estaticamente, o número de instruções é limitado a 1 milhão (após verificação), e o acesso à memória é estritamente controlado. Para algoritmos complexos, é necessário usar mapas para armazenar estado intermediário.
6.2. Compatibilidade com versões antigas do kernel
Recursos mais avançados (como BPF trampoline, BPF iterators, BPF links) requerem kernel 5.x ou superior. Para sistemas com kernel 4.x, BCC é a opção mais compatível, embora sem suporte a CO-RE.
6.3. Performance e overhead
Embora eBPF seja leve, programas mal escritos podem causar overhead significativo:
- Programas XDP que processam cada pacote devem ser extremamente enxutos
- Kprobes em funções chamadas milhões de vezes por segundo (como kmalloc) devem ser evitados
- Sempre teste o impacto em produção antes de implantar
Boas práticas:
- Use tracepoints quando disponíveis (são mais estáveis que kprobes)
- Prefira CO-RE (libbpf) para produção, pois elimina a necessidade de compilação no alvo
- Monitore o contador de instruções do verificador (bpftool prog show) para garantir eficiência
O eBPF não é apenas uma ferramenta de observabilidade — é uma plataforma que está redefinindo como interagimos com o kernel Linux. De monitoramento de desempenho a segurança runtime, passando por redes de alta performance, o eBPF oferece um nível de controle e segurança que antes exigia módulos de kernel personalizados ou instrumentação pesada. Com o ecossistema maduro de BCC, bpftrace e libbpf, qualquer profissional de TI pode começar a explorar essa tecnologia poderosa hoje mesmo.
Referências
- Documentação oficial do eBPF (kernel.org) — Guia completo de referência sobre a arquitetura, mapas, programas e ferramentas do eBPF no kernel Linux
- BCC - BPF Compiler Collection (GitHub) — Repositório oficial com ferramentas de tracing, exemplos e documentação para criação de programas eBPF com Python e Lua
- bpftrace - Guia de Referência — Repositório e documentação da ferramenta de tracing de alto nível com sintaxe estilo awk para análise rápida de sistemas
- Falco - Runtime Security (CNCF) — Documentação oficial da ferramenta de segurança runtime baseada em eBPF para detecção de anomalias em contêineres e hosts
- Tetragon - eBPF-based Security Observability (Cilium) — Documentação oficial do Tetragon para auditoria e monitoramento de segurança em clusters Kubernetes usando eBPF
- eBPF Foundation - Whitepapers e Casos de Uso — Portal oficial da fundação eBPF com whitepapers, estudos de caso e uma visão geral do ecossistema
- Livro: "BPF Performance Tools" de Brendan Gregg — Recurso abrangente sobre ferramentas de performance baseadas em eBPF, com exemplos práticos de monitoramento