Monitorando Syscalls com Aya em Rust em 2026

Guia prático para transformar Rust e eBPF em um projeto de observabilidade: monitorar syscalls com Aya, modelar eventos, medir overhead e explicar valor de carreira.

Por que monitorar syscalls é um bom primeiro projeto com eBPF

Quem lê sobre Rust e eBPF costuma encontrar rapidamente uma barreira: muitos exemplos pulam de teoria para kernel, verifier, mapas, loaders e permissões sem explicar qual problema pequeno vale implementar primeiro. Monitorar syscalls é um bom recorte porque todo processo Linux passa por essa fronteira. Abrir arquivo, criar processo, conectar em rede, ler socket, escrever em disco e executar binário são ações concretas, fáceis de explicar e úteis para observabilidade e segurança.

O objetivo não é criar um produto de segurança completo em um fim de semana. O objetivo é construir uma ferramenta pequena, honesta e demonstrável: um agente que observa uma classe limitada de eventos, transforma esses eventos em registros estruturados e deixa claro quando algo incomum aconteceu. Para carreira Rust, isso vale mais do que um exemplo artificial. Mostra Linux, tipos, async, operação, cuidado com overhead e maturidade para discutir riscos.

Aya entra nesse cenário como uma ponte Rust-native para eBPF. Ela permite escrever o programa eBPF e o user space em Rust, reduzindo a troca mental entre C, build scripts e bindings. Ainda há complexidade: você precisa respeitar restrições do kernel, versões, permissões e limites do verifier. Mas a experiência fica mais alinhada com quem já usa Tokio, Tracing e Serde no resto do stack.

Arquitetura mínima do agente

Um projeto realista pode se chamar aya-syscall-watch. Ele não precisa observar tudo. Comece com uma pergunta simples: quais processos estão executando novos binários ou abrindo conexões de saída? A partir daí, divida o projeto em duas partes.

A primeira parte é o programa eBPF. Ele roda anexado a tracepoints ou kprobes relacionados a uma syscall específica, coleta poucos campos e envia eventos para user space por um ring buffer ou perf buffer. Essa parte deve ser pequena. Quanto menos lógica dentro do kernel, mais fácil validar overhead, evitar rejeição pelo verifier e explicar o comportamento.

A segunda parte é a aplicação Rust em user space. Ela carrega o programa, lê eventos, resolve nomes quando necessário, aplica filtros configuráveis, escreve logs estruturados e exporta métricas. É aqui que entram ergonomia, configuração, testes e integração com o mundo normal de backend.

Uma estrutura simples seria:

aya-syscall-watch/
  aya-syscall-watch-ebpf/
    src/main.rs
  aya-syscall-watch/
    src/main.rs
    src/config.rs
    src/events.rs
    src/output.rs
    src/filters.rs

Esse desenho deixa claro que eBPF é a sonda, não a aplicação inteira. A inteligência principal deve ficar no user space, onde Rust tem biblioteca padrão, testes mais confortáveis, logging, serialização e integração com HTTP ou Prometheus.

Quais syscalls observar primeiro

Para um primeiro projeto, evite tentar cobrir o sistema inteiro. Escolha uma família pequena:

  • execve para detectar execução de novos binários.
  • connect para observar conexões de saída.
  • openat para acompanhar acesso a arquivos sensíveis.
  • clone ou fork para entender criação de processos.

execve é um bom ponto de partida porque o evento é fácil de demonstrar. Quando alguém roda curl, bash, psql ou um binário desconhecido dentro de um container, o agente registra processo, usuário, comando e timestamp. Em ambientes de produção, esse sinal ajuda a investigar shells inesperados, scripts temporários e comportamento pós-incidente.

connect adiciona valor de rede: qual processo tentou falar com qual destino. É útil, mas exige mais cuidado com volume e interpretação. Muitos processos fazem conexões legítimas o tempo todo. Sem filtros, você cria ruído. Por isso, comece registrando destino, porta e processo, mas trate alertas como camada separada.

openat é poderoso para segurança, especialmente ao observar caminhos como /etc/passwd, chaves, diretórios de configuração e segredos montados. Também é fácil gerar evento demais. Use allowlists, prefixos e amostragem desde o começo.

Modelo de evento em Rust

Um erro comum em projetos de observabilidade é tratar evento como string solta. Melhor modelar cedo:

use serde::Serialize;

#[derive(Debug, Clone, Serialize)]
pub struct SyscallEvent {
    pub event_id: String,
    pub syscall: String,
    pub pid: u32,
    pub uid: u32,
    pub comm: String,
    pub timestamp_unix_ms: u64,
    pub target: Option<String>,
    pub container_hint: Option<String>,
}

O programa eBPF talvez entregue uma versão mais compacta, com arrays fixos e campos numéricos. A aplicação user space pode transformar isso em um tipo mais expressivo. Essa separação mantém o kernel enxuto e deixa o formato final mais útil para logs, métricas e integrações.

Use Serde para exportar JSON linha a linha. Esse formato é simples de testar, fácil de mandar para Loki, Elasticsearch, arquivo local ou um pipeline interno. Não invente protocolo complexo antes de provar que o sinal é valioso.

Observabilidade do próprio agente

Um agente de observabilidade que não observa a si mesmo vira um risco operacional. Instrumente desde o começo com Tracing. Registre quando o programa eBPF foi carregado, quais hooks foram ativados, quantos eventos foram recebidos, quantos foram filtrados, quantos foram descartados e qual erro aconteceu ao resolver metadados.

Métricas mínimas:

  • eventos recebidos por syscall;
  • eventos descartados por filtro;
  • erros de parsing;
  • tempo médio de processamento no user space;
  • tamanho de fila interna;
  • perda de eventos reportada pelo buffer, quando disponível.

Essas métricas importam porque eBPF costuma ser vendido como baixo overhead, mas baixo overhead não significa overhead zero. Um projeto profissional precisa mostrar como mede custo. Rode carga simples, compare antes/depois e documente limites: kernel testado, distribuição, versão do Rust, permissões necessárias e cenários em que o agente não deve ser usado.

Segurança e privacidade

Monitorar syscalls pode capturar dados sensíveis. Argumentos de processo podem conter tokens. Caminhos de arquivo podem revelar nomes de cliente. Destinos de rede podem expor topologia interna. Por isso, o guia de segurança em Rust continua relevante mesmo quando o código é memory-safe.

Adote três regras. Primeiro, colete o mínimo necessário para o objetivo declarado. Segundo, mascare valores arriscados antes de gravar logs. Terceiro, deixe retenção e destino explícitos. Se a ferramenta grava tudo em arquivo local sem rotação, ela pode virar o incidente que tentava prevenir.

Também explique permissões. eBPF normalmente exige capacidades elevadas ou execução como root, dependendo do kernel e da configuração. Isso não é detalhe. Um README sério deve dizer exatamente quais privilégios são necessários, por que eles existem e como rodar em ambiente isolado para estudo.

Como transformar isso em peça de portfólio

Para quem busca vagas Rust em plataforma, segurança, backend de infraestrutura ou SRE, o valor está na narrativa técnica. Um bom repositório não diz apenas “usei Aya”. Ele mostra o problema, a arquitetura, os limites, exemplos de saída, testes e próximos passos.

Inclua um README com:

  • diagrama simples de kernel para user space;
  • exemplo de evento JSON;
  • comandos para rodar em uma VM local;
  • seção de overhead e limitações;
  • lista de syscalls suportadas;
  • explicação de filtros;
  • riscos de privacidade;
  • ideias de integração com Prometheus, Loki ou OpenTelemetry.

Esse projeto também conversa com Rust para DevOps e infraestrutura, logging e observabilidade e empresas que usam Rust. O mercado brasileiro ainda tem poucas vagas explicitamente chamadas de “Rust eBPF”, mas há demanda por gente que entende Linux, incidentes, telemetria, segurança e ferramentas confiáveis.

Próximos incrementos depois do MVP

Depois que execve funciona bem, evolua em fatias pequenas. Adicione configuração por YAML. Depois filtros por UID, processo e caminho. Depois saída em JSON e métricas. Depois connect. Só então pense em alertas. Esse ritmo evita o erro de construir um SIEM ruim antes de ter um coletor confiável.

Outra evolução natural é comparar sinais do agente com logs de aplicação. Uma API Axum instrumentada com Tracing mostra spans e erros internos; o agente eBPF mostra o que aconteceu no sistema operacional ao redor dela. Quando os dois sinais se encontram, você consegue responder perguntas mais maduras: o erro veio da aplicação, da rede, de DNS, de arquivo, de permissão ou de processo externo?

Se quiser ir para um lado mais cloud native, rode o agente em uma VM com containers locais e documente como identificar processos dentro de namespaces. Não precisa prometer cobertura completa de Kubernetes. Basta mostrar consciência de que PID, container, host e workload não são a mesma coisa.

Conclusão

Monitorar syscalls com Aya em Rust é uma fatia pequena, mas forte, do universo eBPF. O projeto ensina kernel sem virar tese, observabilidade sem depender só de logs e segurança sem prometer milagre. Para o leitor brasileiro, também cria uma ponte entre estudo avançado e carreira: uma ferramenta demonstrável, com código Rust real, saída estruturada e decisões operacionais explícitas.

O caminho certo é começar estreito. Uma syscall, poucos campos, documentação honesta, medição de overhead e links claros com problemas de produção. Depois você expande. Rust ajuda a manter o projeto organizado, mas o diferencial profissional vem de saber operar a ferramenta com responsabilidade.

Se você já leu sobre eBPF, Tokio e Tracing, esse é um bom próximo passo: sair do conceito e construir uma sonda mínima que explica o sistema enquanto ele roda.