Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
Ferramentas/GitHubGitHub/danthegoodman1/netfence
Segurança de Infraestrutura em NuvemSegurança de ContêineresEvasão de IDS/IPSSegurança de RedeSegurança na NuvemDevSecOpsConfiguração IncorretaAnálise de DNS
GitHubdanthegoodman1/netfence

netfence

Como Envoy xDS, mas para filtros eBPF

Ver Repositório
10143há 1 mêsRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Netfence

Como o Envoy xDS, mas para filtros eBPF.

O Netfence é executado como um serviço em seus hosts de VM/contêineres e injeta automaticamente programas de filtro eBPF em cgroups e interfaces de rede, com um servidor DNS interno que resolve domínios permitidos e popula a lista de permissões de IP.

Os serviços Netfence podem ser controlados apenas por meio de sua API local de socket Unix, ou conectam-se a um plano de controle central que você implementa via gRPC para sincronizar listas de permissão/bloqueio com seu backend.

Seu plano de controle envia regras de rede como ALLOW *.pypi.org ou ALLOW 10.0.0.0/16 para interfaces/cgroups anexados. Quando uma VM/contêiner consulta o DNS, o Netfence a resolve, adiciona os IPs ao filtro eBPF e descarta tráfego para IPs desconhecidos antes que ele saia do host, com uma sobrecarga de caminho aquecido que é virtualmente indistinguível de uma conexão normal de socket nos benchmarks atuais.

Funcionalidades

  • Anexar filtros eBPF a interfaces de rede (TC) ou cgroups
  • Modos de política: desabilitado, lista de permissão, lista de bloqueio, bloquear-tudo
  • Suporte a CIDR IPv4 e IPv6 com TTLs opcionais
  • Servidor DNS UDP/TCP por anexação com lista de permissão/bloqueio de domínios e substituições ordenadas de upstream
  • Regras de domínio suportam subdomínios com correspondência baseada em especificidade (regras mais específicas vencem)
  • Domínios resolvidos preenchem automaticamente o filtro de IP
  • Metadados em serviços e anexações para associação com ID de VM, inquilino, etc.
  • Suporte para proxy de consultas DNS para o plano de controle para tomar decisões de DNS por anexação

Nota de segurança: isenções padrão

Baixar ferramenta

No modo de lista de permissão, o IPv4 link-local (169.254.0.0/16) não é mais automaticamente permitido por padrão — então o serviço de metadados da nuvem (169.254.169.254) é bloqueado a menos que explicitamente permitido. Isso é proposital: o serviço de metadados é um alvo de roubo de credenciais, e workloads em sandbox não devem ser capazes de alcançá-lo implicitamente. Localhost (127.0.0.0/8, ::1) e descoberta de vizinhos IPv6 (fe80::/10, ff02::/16) permanecem permitidos por padrão para que a conectividade básica e NDP continuem funcionando. Para permitir o serviço de metadados para um workload, coloque na lista de permissão 169.254.169.254/32 (uma substituição de isenção por anexação via plano de controle é um acompanhamento planejado).

Broadcast IPv4 (255.255.255.255) e multicast (224.0.0.0/4) não possuem isenção e estão sujeitos à política, portanto, no modo de lista de permissão TC, tráfego como broadcasts de renovação de DHCP é bloqueado a menos que explicitamente permitido. As verificações de isenção são executadas antes da lista de bloqueio, portanto, um intervalo isento só pode ser bloqueado desativando sua isenção — e como o IPv4 link-local agora está desativado por padrão, o modo de lista de bloqueio também pode bloquear o serviço de metadados.

Diferenças de outras opções

Alguns benefícios principais desta solução que outras opções geralmente não suportam:

  • Corte imediato de conexões existentes quando as regras mudam para desabilitar um IP (apenas anexação de interface)
  • Suporte a todos os protocolos de rede e conexão direta a IPs. Por exemplo, o excelente httpjail não permite conectar-se diretamente a IPs, ou conexões TCP/UDP diretas como conexões a bancos de dados.
  • Resolução dinâmica de DNS e filtragem pré-resolução (para que não haja exfiltração de secretdata.someattacker.com)

Até onde sei, nenhuma outra solução oferece todos esses recursos juntos.

Limitação conhecida: anexações de cgroup filtram na camada de socket (hooks connect/sendmsg), portanto, um processo com CAP_NET_RAW pode criar pacotes brutos que os ignoram. Use uma anexação TC (interface), que filtra na camada do dispositivo, para workloads que possam ter CAP_NET_RAW.

No entanto, isso tem um pouco mais de sobrecarga do que algo como httpjail.

Instantâneo de desempenho

Estes números foram medidos no gateway Linux Docker privilegiado em linux/arm64 usando make bench-docker. Os valores são medianas de cinco amostras.

Caminho de socket aquecido

O benchmark de socket aquecido usa sockets UDP conectados para isolar o custo do hook eBPF cgroup/connect4 da latência do handshake TCP. Neste caminho, o DNS já resolveu o domínio, o IP ainda está dentro do TTL e o IP/CIDR já está presente no mapa eBPF.

CaminhoLatência mediana
Conexão normal de socket, sem eBPF~2,647 us
Lista de permissão aquecida, acerto LPM protegido~2,691 us
Lista de permissão aquecida, acerto exato de host DNS~2,741 us
Erro na lista de permissão, bloqueio local~1,652 us

A diferença medida entre os caminhos de conexão normal, LPM protegido e acerto exato de host DNS está dentro do ruído da amostra.

Não há caminho de "erro no kernel pergunta ao processo pai" atualmente. Um erro de lista de permissão de cgroup é decidido localmente pelo eBPF e é bloqueado imediatamente.

Caminho de consulta DNS

Estes números medem o caminho do servidor DNS, não o caminho de socket aquecido.

CaminhoLatência mediana
Consulta proxy fria, função de política em processo~31,336 us
Consulta proxy quente~27,964 us
Consulta de lista de permissão fria com upstream local~53,510 us
Consulta de lista de permissão quente com upstream local~53,432 us

As linhas frias sincronizam através da barreira de mutação de anexação real e limpam o gráfico de propriedade do benchmark e o instantâneo de mapa exato falso entre as consultas. O timer é executado continuamente para preservar a localidade do escalonador UDP, enquanto ns/op subtrai o fixture-reset-ns/op de tempo de parede relatado separadamente (incluindo qualquer cauda do manipulador anterior após o cliente receber seu pacote) e, portanto, mede a troca do cliente atual. raw-total-ns/op relata ambos juntos. A reinicialização preserva os domínios de política configurados e o armazenamento de suporte, e o benchmark afirma uma adição de mapa exato físico por consulta. As linhas quentes pré-configuram a propriedade uma vez e afirmam uma adição física em toda a execução.

Os microbenchmarks de propriedade interna abaixo são diagnósticos de escalabilidade, não linhas de aceitação de caminho de consulta DNS fim a fim. O auxiliar em cache é mantido apenas para testes e benchmarks; ele envolve um registro por vez e repete a validação de domínio. Tanto ele quanto o tráfego normal de resolução atravessam a barreira de mutação de anexação, enquanto o tráfego normal de resolução admite cada resposta completa como uma transação.

Diagnóstico interno de escalabilidadeMediana atualMemória / alocações
Admissão de nova chave fria, gráfico de propriedade vazio~370,3 ns232 B, 5 allocs/op
Admissão de nova chave fria, 4.095 entradas não relacionadas~451,2 ns232 B, 5 allocs/op
Pressão de capacidade física e substituição LRU~3,820 ms~4,23 MB (4.226.243 B), 4.336 allocs/op
Pré-verificação de orçamento físico esgotado~611,9 ns344 B, 9 allocs/op
Pressão máxima de borda, resposta de 64 endereços~6,849 ms~7,66 MB (7.658.774 B), 2.233 allocs/op
Guarda de trabalho máximo do gráfico, plano completo permitido~2,763 ms~4,26 MB (4.264.386 B), 3.074 allocs/op
Guarda de trabalho máximo do gráfico, rejeição de pré-projeção esgotada~10,935 us8,76 KB (8.760 B), 14 allocs/op
Operação de orçamento de rotatividade perto do limite numérico~18,98 ns0 B, 0 allocs/op
Instantâneo coerente de estatísticas de propriedade~2,094 ns0 B, 0 allocs/op
Varredura de expiração sem operação em 4.095 entradas~74,849 us/varredura0 B, 0 allocs/op

Design

Arquitetura```

+------------------+ +-------------------------+ | Your Control |<------->| Daemon (per host) | | Plane (gRPC) | stream | | +------------------+ | +-------------------+ | | | DNS Server | | | | (per-attachment) | | | +-------------------+ | +-------------------------+ | +------+------+ | | TC Filter Cgroup Filter (veth, eth) (containers)

root@kitploit:~
Cada anexo obtém um endereço DNS único (porta) provisionado pelo daemon. Contentores/VMs devem ser configurados para usar o seu endereço DNS atribuído; a filtragem do tráfego DNS comum das cargas de trabalho não o redireciona de forma transparente.

### Topologia e comportamento do resolvedor DNS

`dns.listen_addr` deve identificar um endereço IPv4 ou IPv6 concreto que cada
carga de trabalho anexada possa alcançar. Endereços wildcard são rejeitados porque
não podem ser anunciados como endpoints de resolvedor. Um nome de anfitrião configurado é resolvido
uma vez quando o daemon inicia e o IP concreto resultante é usado para vinculação,
anúncio, persistência e inicialização do filtro. O `127.0.0.1` predefinido é
apropriado apenas quando a carga de trabalho partilha o espaço de nomes de rede do daemon; um
contentor ou VM noutro espaço de nomes normalmente precisa de um endereço de anfitrião/ponte
acessível.```yaml
dns:
  listen_addr: 10.0.0.1
  port_min: 11000
  port_max: 11500
  # Daemon-global fallback when DnsConfig.upstream_servers is empty.
  upstream: 1.1.1.1:53
  # Hard daemon ceilings for each attachment's bounded DNS exact ownership.
  # Zero/unset uses these defaults (max_ips_per_family instead derives from
  # filter.max_dns_rule_entries).
  max_ips_per_family: 4096
  max_ips_per_response: 64
  max_ips_per_policy_domain: 1024
  max_tracked_domains: 1024
  max_ownership_edges: 8192
  # Rolling physical-admission/LRU mutation budget and slow-planning work
  # allowance. The window is daemon-global and immutable until restart;
  # DnsConfig.max_churn_units may only lower the daemon ceiling.
  max_churn_units: 8192
  churn_window: 1m

Attach devolve o dns_address concreto; configure exatamente esse endereço como o resolvedor da carga de trabalho. O Netfence instala uma entrada de permissão protegida e não expirante /32 ou /128 para o IP do ouvinte, para que o modo de lista de permissões possa inicializar sem uma regra de DNS-IP do plano de controlo. Os filtros atuais impõem prefixos IP, não portas de destino, portanto essa entrada protegida permite cada porta no IP do ouvinte (não apenas a sua porta DNS); isto é especialmente importante para modelos de ameaça de anexação cgroup. Use um IP de ouvinte dedicado quando essa acessibilidade mais ampla não for aceitável.

O ponto final atribuído atende tanto UDP quanto TCP. As respostas UDP são truncadas para o limite de 512 bytes de um cliente legado ou seu tamanho EDNS anunciado e carregam TC quando necessário, permitindo que a carga de trabalho tente novamente o mesmo ponto final via TCP. Para resolução upstream, uma resposta UDP truncada é repetida via TCP contra o mesmo upstream primeiro. Uma falha de transporte, SERVFAIL ou REFUSED avança então para o próximo upstream configurado, em ordem.

DnsConfig.upstream_servers substitui o dns.upstream global do daemon para uma anexação. As entradas usam a sintaxe host:port (literais IPv6 entre colchetes), são canonicalizadas e desduplicadas na ordem de primeira aparição, e limitadas a oito servidores únicos. Uma lista vazia seleciona o fallback global.

Em modos de filtragem, o Netfence remove os parâmetros ipv4hint e ipv6hint das respostas HTTPS/SVCB, incluindo suas referências mandatory correspondentes, porque os endereços sugeridos não passaram independentemente pela admissão do filtro. O modo desabilitado preserva as respostas upstream inalteradas.

Os contadores de consultas DNS são mutuamente exclusivos: dns_queries_allowed conta consultas permitidas por política e respondidas com sucesso (incluindo NXDOMAIN), dns_queries_blocked conta respostas REFUSED por política, e dns_queries_errors conta erros de resolvedor, proxy, admissão de filtro, escrita de resposta e outros caminhos de erro. Uma consulta incrementa exatamente um balde.

Cada resposta com endereço em um modo de filtragem DNS é admitida no nível HASH IPv4/IPv6 exato da anexação como uma transação antes que qualquer endereço A/AAAA de suas seções de resposta, autoridade ou adicional seja retornado. Se a resposta completa não puder ser representada, o resolvedor retorna SERVFAIL sem endereço e preserva o conjunto de trabalho previamente admitido. Decisões PROXY que retornam endereços devem definir add_to_filter; caso contrário, também falham fechadas como SERVFAIL. O modo DNS desabilitado é a exceção explícita de passagem.

Entradas exatas carregam arestas TTL da consulta normalizada ao proprietário da política correspondente. Remover ou negar um domínio prontamente remove seus últimos endereços exatos DNS-only, enquanto um endereço compartilhado sobrevive a outro proprietário de consulta ativo e um CIDR de plano de controlo sobreposto continua independentemente no nível LPM protegido. Respostas DENYLIST DNS de permissão padrão e permissão explícita também são rastreadas, mesmo enquanto DENYLIST de pacotes ignora permissões exatas, para que uma mudança posterior para o modo ALLOWLIST de pacotes possa usar endereços em cache já retornados sem uma nova consulta.

Todo estado de propriedade userspace normal/ativo é limitado pelas cinco configurações de propriedade acima. As arestas provisórias sintéticas restauradas estão isentas desses limites lógicos para que não possam ser esquecidas antes da reconciliação, mas permanecem limitadas pelos mapas exatos físicos IPv4/IPv6. Domínios de política configurados e domínios de consulta ativos compartilham max_tracked_domains, e cada registro TTL (consulta, proprietário correspondente, IP) consome um slot max_ownership_edges.

Sob pressão, a admissão projeta arestas TTL expiradas primeiro. Em seguida, recupera arestas lógicas completas de consulta/proprietário com o menor colateral físico antes da recência, seguido por LRU determinístico observado pelo resolvedor (IP canônico desempata). Uma evicção física remove toda a chave DNS exata e todos os seus proprietários DNS. O IP físico recebido e a aresta exata recebida (IP, consulta, proprietário) são protegidos para a transação de resposta. A propriedade provisória restaurada protege sua chave física até a reconciliação autoritativa, mas metadados DNS normais não relacionados que compartilham essa chave ainda podem ser recuperados. Permissões autoritativas do plano de controlo/sistema e cada negação permanecem em níveis LPM separados e nunca são candidatos à recuperação DNS.

O orçamento rotativo de churn por anexação cobra uma unidade para uma nova chave física exata e uma para cada chave física DNS ativa evictada; uma substituição completa de antigo para novo, portanto, custa duas. Atualizações, recuperação apenas lógica, expiração e remoção por política custam zero. Eventos permanecem ativos enquanto sua idade é menor que dns.churn_window e expiram no limite exato. DnsConfig.max_churn_units só pode reduzir o teto do daemon; zero o herda. A janela não pode ser alterada pelo plano de controlo, e alterar o teto/janela do daemon requer uma reinicialização. Reduzir e posteriormente aumentar um limite de anexação não esquece o histórico ainda ativo.

Um livro-razão de trabalho rotativo separado limita o planejamento caro do grafo de propriedade. Atualizações rápidas e admissões comuns nunca o tocam. Antes que um caminho de pressão clone ou analise o grafo, o Netfence cobra unidades de trabalho estáveis derivadas das chaves físicas atuais, arestas de propriedade, domínios rastreados e tamanho da resposta em relação aos seus tetos imutáveis de daemon/mapa. Essa tentativa de cobrança é retida mesmo quando o plano se mostra impossível ou uma transação posterior de mapa exato falha, fechando o caminho de repetição de mutação zero para pressão de CPU/alocação sem alterar a contabilidade transacional de churn físico acima. Nos tetos padrão, a permissão admite oito passes de grafo equivalentes ao máximo por janela; reduzir DnsConfig.max_churn_units retém pelo menos um. Reduzir e posteriormente aumentar o limite nunca redimensiona ou esquece o histórico de trabalho ativo.

Quando nenhum estado DNS elegível pode satisfazer um limite, ou qualquer permissão rotativa está esgotada, o Netfence preserva o conjunto de trabalho admitido e retorna SERVFAIL sem retornar o endereço não admitido. Falhas de capacidade incrementam map_full_drops; limitações de churn físico e de planejamento de trabalho não. Heartbeats expõem valores atuais, capacidade e high-water de geração de processo do mapa exato, além de evicções LRU cumulativas de DNS, todas as falhas de admissão e uma contagem agregada de limitação de orçamento cobrindo ambos os guardas rotativos. Logs de pressão/recuperação de capacidade, orçamento físico e orçamento de trabalho são limitados em taxa independentemente. Os operadores podem aguardar recuperação de TTL/janela, reduzir churn de resposta/domínio ou tentativas repetidas de pressão de capacidade, ou aumentar DnsConfig.max_churn_units até o teto do daemon dns.max_churn_units. Aumentar o teto do daemon requer uma reinicialização; aumentar filter.max_dns_rule_entries também requer recriar a anexação porque mapas fixados não podem ser redimensionados no local.

Capacidade de CIDR protegido e recuperação com falha fechada

CIDRs autoritativos do plano de controlo e regras do sistema do daemon usam quatro mapas LPM independentes e não sujeitos a evicção: permissão/negação × IPv4/IPv6. Cada mapa tem slots filter.max_rule_entries. O bootstrap DNS /32 ou /128 do ouvinte é uma permissão de sistema e conta contra o mapa de permissão protegido correspondente. Entradas de host exato de DNS permanecem em seus mapas separados e não podem consumir esses slots. Nenhuma regra protegida é evictada por LRU: permissões explícitas, regras do sistema e toda negação permanecem até uma remoção autorizada ou substituição completa.

Um SubscribedAck ou BulkUpdate completo é canonicalizado e sua ocupação final é verificada em todos os quatro mapas antes da mutação. A capacidade é baseada no estado final, portanto substituir chaves em um mapa cheio é válido; estado superdimensionado é rejeitado sem evicção ou aceitação parcial de regras. Os sobreviventes não são removidos e readicionados. Se uma chamada de sistema de mapa falhar posteriormente, o Netfence restaura e verifica o inventário exato dos quatro mapas antes da chamada. O modo comprovado após rollback é o modo antigo ou BLOCK_ALL (normalmente BLOCK_ALL), então o daemon ainda mantém a anexação fechada com falha até que uma nova tentativa autoritativa completa tenha sucesso.

O estado de segurança de política protegida é persistido com a anexação e exportado em heartbeats. BLOCK_ALL por si só é um modo configurado normal e saudável: policy_degraded é falso quando policy_degraded_reason está vazio. Uma mutação protegida arriscada que começa enquanto BLOCK_ALL saudável primeiro registra protected_policy_mutation_in_progress. Este é um registro de falha transitório, não um diagnóstico de falha estável: a operação ativa pode publicar seu modo pretendido antes da gravação final de limpeza do registro, e a conclusão bem-sucedida limpa o próprio registro. Se a inicialização o encontrar após uma falha, a inicialização primeiro força e prova BLOCK_ALL, depois persiste protected_policy_mutation_interrupted. Códigos de razão degradada estáveis são:

  • protected_policy_mutation_interrupted
  • authoritative_protected_policy_failed
  • incremental_deny_install_failed
  • incremental_allow_removal_failed
  • incremental_mode_change_failed

Estas são classificações estáveis, nunca texto bruto de syscall/store. O registro em andamento é um limite de falha persistido interno; as estatísticas de heartbeat serializam com a mutação proprietária e, portanto, observam sua limpeza bem-sucedida ou uma conversão de falha estável, não o registro intermediário ativo. Razões degradadas/interrompidas estáveis mantêm a aplicação de pacotes em BLOCK_ALL comprovado e rejeitam comandos incrementais de CIDR e modo de pacote. Mudanças independentes de configuração DNS e expiração de TTL DNS podem continuar sob essa retenção comprovada, mas não podem limpar a razão estável ou reativar a política de pacotes. A recuperação de uma razão estável requer um estado desejado completo de LPM e DNS: aplique BulkUpdate através do plano de controlo ou API local (ou responda a um Subscribed fresco de uma anexação restaurada com SubscribedAck). O Netfence prepara o estado protegido completo, aplica o estado DNS autoritativo, ativa o modo solicitado e limpa a razão durável somente após cada etapa ter sucesso. Prefira um command_id único em um BulkUpdate de recuperação do plano de controlo e exija um CommandResult bem-sucedido; a API local rejeita command_id porque seu resultado RPC unário já relata sucesso ou falha.

Heartbeats expõem entradas físicas atuais, capacidade rígida e high-water de geração do daemon independentemente para todos os quatro mapas protegidos. O bootstrap está incluído; entradas fixadas adotadas inicializam o high-water da nova geração. map_full_drops é cumulativo e inclui rejeições de capacidade protegida. Se uma leitura de ocupação falhar, o daemon retém o último instantâneo comprovado e emite um aviso no máximo uma vez a cada 30 segundos em vez de inventar novas contagens. Para recuperar da pressão, reduza as regras desejadas completas abaixo da capacidade de cada mapa e tente novamente a atualização completa. Aumentar filter.max_rule_entries é apenas em tempo de carga e requer recriar uma anexação fixada existente. Se o daemon não puder provar BLOCK_ALL ou registrar duravelmente seu marcador de segurança, ele para de admitir mutações; repare a falha do mapa/store e reinicie em vez de assumir que a aplicação foi reaberta.

Por host

Execute o daemon, que:

  • Expõe uma API gRPC local (DaemonService) para anexações, política e inspeção
  • Opcionalmente conecta-se ao seu plano de controlo via stream bidirecional (ControlPlane.Connect)
  • Carrega e gerencia programas eBPF

Inicie o daemon:```bash

Start with default config

netfenced start

Start with custom config file

netfenced start --config /etc/netfence/config.yaml

root@kitploit:~
**Verificar status do daemon:**```bash
netfenced status

Sem control_plane.url, um novo attachment confirma no modo pacote/DNS desativado e pode ser configurado imediatamente através da API local ou CLI. Nenhum processo control-plane é necessário para o fluxo de trabalho autônomo documentado em “Per attachment.”

Limite de confiança do socket Unix local

A API gRPC local não possui autenticação por RPC. O acesso ao sistema de arquivos ao seu socket Unix é o limite de autorização, e todo processo que pode conectar é um administrador de rede do host totalmente confiável: ele pode anexar ou desanexar programas eBPF do host, substituir políticas de pacote e DNS, e abrir ou fechar tráfego de carga de trabalho. Mantenha a associação ao grupo de socket restrita e proteja o diretório pai do socket.```yaml

Defaults to /var/run/netfence.sock.

socket: /run/netfence/netfence.sock

Unix group name or numeric GID. Empty/unset uses the daemon's effective GID.

socket_group: netfence-admin

root@kitploit:~
`NETFENCE_SOCKET` e `NETFENCE_SOCKET_GROUP` são as variáveis de ambiente equivalentes. Na inicialização, o daemon associa o socket a um diretório de preparação privado, define seu grupo e modo `0660` enquanto está inacessível e, em seguida, publica-o atomicamente. O daemon remove qualquer socket Unix pré-existente no destino configurado — ele não distingue um socket obsoleto de um pertencente a outro daemon ativo — portanto, exatamente um daemon deve possuir um caminho de socket. Ele se recusa a remover um destino que não seja um socket. No Linux, a renomeação sem substituição impede a sobrescrita de um novo caminho criado após essa remoção; o desligamento remove o caminho publicado apenas enquanto ele ainda identifica o inode do socket do próprio daemon. Um grupo inválido, falha de propriedade/modo ou destino não-socket falha na inicialização sem publicar um endpoint permissivo.

### Segurança do transporte do plano de controle (TLS / mTLS / token de portador)

O canal do plano de controle é a superfície de ataque de maior valor no sistema (quem o controla pode enviar regras `ALLOW` para todas as cargas de trabalho), então o daemon **falha em estado fechado**: se `control_plane.url` estiver definido, a configuração deve escolher explicitamente um transporte — ou um bloco `control_plane.tls` ou `control_plane.insecure: true`. Uma URL sem nenhum deles é rejeitada na inicialização; não há padrão implícito de texto simples. (Esta é uma mudança de comportamento deliberada: versões mais antigas conectavam-se ao plano de controle sem criptografia de forma silenciosa.)```yaml
control_plane:
  url: cp.internal:443
  tls:
    # CA bundle used to verify the control-plane server certificate.
    # Path to a PEM file or inline PEM; omit to use the system root pool.
    ca: /etc/netfence/cp-ca.pem
    # Client certificate + key (path or inline PEM). Setting BOTH enables
    # mTLS: the daemon presents this cert to the control plane. Setting only
    # one is a config error.
    cert: /etc/netfence/daemon.pem
    key: /etc/netfence/daemon.key
    # Optional hostname override for server certificate verification (SNI),
    # e.g. when dialing by IP.
    server_name: cp.internal
  # Optional bearer token, sent as `authorization: Bearer <token>` metadata
  # on every control-plane RPC. Refused on a plaintext channel unless
  # `insecure: true` was explicitly set (so a misconfiguration can't leak it).
  auth_token: "..."

TLS apenas com raízes do sistema (certificado de servidor emitido por CA pública, sem mTLS) é apenas um bloco vazio:```yaml control_plane: url: cp.example.com:443 tls: {}

root@kitploit:~
Texto simples para desenvolvimento local é uma aceitação explícita (mutuamente exclusivo
com `tls`):```yaml
control_plane:
  url: localhost:9000
  insecure: true

Certificados e chaves são carregados uma vez na inicialização, então um caminho/PEM inválido faz a inicialização falhar com um erro claro, em vez de aparecer a cada reconexão.

Manutenção de atividade do plano de controle (keepalive) e backoff de reconexão

O daemon envia pings keepalive HTTP/2 na conexão do plano de controle para que um caminho silenciosamente morto (puxão de cabo, mapeamento NAT descartado, rota blackholed) seja detectado e derrubado em aproximadamente keepalive_time + keepalive_timeout — em vez de ficar CONNECTED por minutos até o tempo limite de retransmissão TCP do kernel enquanto cada consulta DNS procurada consome todo o seu tempo limite. As reconexões são ritmadas por um backoff exponencial com jitter (começa em 1s, dobra, ±20% de jitter, com limite máximo em reconnect_backoff_max); o backoff é redefinido para o mínimo somente depois que uma conexão permanece saudável por 30s, para que um plano de controle que aceita conexões e as descarta imediatamente continue recuando em vez de ser martelado no mínimo.```yaml control_plane:

Send a keepalive ping after this much inactivity… (default 30s; gRPC

clamps the effective interval to a 10s minimum client-side)

keepalive_time: 30s

…and declare the peer dead if no ack arrives within this (default 10s).

keepalive_timeout: 10s

Cap on the jittered exponential reconnect backoff (default 30s).

reconnect_backoff_max: 30s

root@kitploit:~
Valores zero/não definidos significam os padrões — eles **não** desabilitam o keepalive ou o backoff. Seu plano de controle deve permitir essa cadência de ping em sua política de imposição de keepalive gRPC (veja abaixo), ou rejeitará o daemon com `ENHANCE_YOUR_CALM (too_many_pings)`.

### Reinicializações, quedas e atualizações do daemon (estado BPF fixado)

O daemon fixa os links BPF e os mapas de regras de cada anexo no bpffs (`filter.bpf_pin_dir`, padrão `/sys/fs/bpf/netfence`, um diretório por ID de anexo). Como o estado fixado é mantido pelo kernel — não pelo processo do daemon — **a aplicação continua enquanto o daemon está inativo**: uma queda (`kill -9`), uma parada graciosa ou uma atualização deixa a última política conhecida (modo + todas as regras) em vigor, e a próxima inicialização do daemon readota o estado fixado como está. A restauração nunca reanexa ou reescreve os mapas ativos, portanto não há janela onde uma carga de trabalho na lista de permissões seja bloqueada ou um destino bloqueado seja permitido, e nenhum anexo duplicado.

O comportamento de parada é uma configuração explícita (`filter.detach_on_stop`):```yaml
filter:
  # false (default): stopping the daemon KEEPS ENFORCING — filters stay
  # attached via their bpffs pins and are re-adopted on the next start
  # (fail-closed across restarts/upgrades).
  # true: stopping the daemon detaches filters and removes their pins —
  # traffic is unfiltered while the daemon is down (explicit fail-open).
  detach_on_stop: false
  # bpffs directory for pinned state. Must be on a bpffs mount; the daemon
  # mounts bpffs at /sys/fs/bpf if needed (privileged). An explicit "" turns
  # pinning off entirely (BPF state then dies with the process).
  bpf_pin_dir: /sys/fs/bpf/netfence
  # Capacity of each authoritative/system LPM map (allowed/denied per family).
  # Protected entries are non-evictable; the DNS listener bootstrap consumes
  # one slot in its address family. Changing pinned-map capacity requires
  # recreating the attachment.
  max_rule_entries: 4096
  # Independent capacity of each DNS-derived exact-host HASH map (IPv4/IPv6).
  # These entries can never consume or evict authoritative/deny capacity.
  max_dns_rule_entries: 4096

Um Detach explícito (RPC/CLI), ou a remoção de um alvo vivo e coerentemente possuído, destrói o estado fixado juntamente com o anexo. Na reinicialização, a ausência do alvo não autoriza adivinhação: pins persistidos futuros, não confirmados, mistos ou de outra forma não verificáveis são preservados e a inicialização aborta para inspeção.

Os diretórios de pin são um formato de persistência versionado. O marcador de esquema é fixado por último, apenas depois que cada mapa e link obrigatórios existem. A atualização de um anexo pré-exato-tier fixa os dois novos mapas exatos vazios com um marcador em andamento, substitui atomicamente o programa de cada link reutilizando os mapas autoritativos vivos, verifica a identidade programa/mapa e confirma o marcador por último. Uma falha ou atualização ambígua deixa o marcador não confirmado; a próxima inicialização reatualiza cada link usando os mesmos mapas. As gerações de programas antigos e novos impõem a mesma política LPM autoritativa durante esse estado misto limitado, portanto a migração nunca desafixa ou recria um filtro viável. Conjuntos de pin desconhecidos, incompletos ou não verificáveis são preservados e abortam a inicialização para inspeção em vez de serem descartados por adivinhação.

Notas sobre o estado readotado:

  • Cada anexo restaurado com sucesso é marcado para reconciliação autoritativa. Em cada conexão do plano de controle, o daemon envia o SyncRequest primeiro e, em seguida, uma declaração Subscribed completa para cada anexo restaurado que ainda precisa de reconciliação. Responda com um novo SubscribedAck: seu modo, CIDRs, TTLs e configuração DNS são o estado desejado completo. O daemon aplica um delta (os CIDRs inalterados nunca são removidos) e limpa o marcador de restauração apenas depois que todo o ack é aplicado. Um timeout, desconexão ou falha de validação deixa a aplicação inalterada. Uma falha na aplicação de filtro/mapa/DNS/armazenamento pode deixar um delta parcial, mas a reconciliação não usa uma limpeza total do mapa ou remove/readiciona sobreviventes inalterados; o marcador de restauração permanece definido e o daemon tenta novamente após uma conexão posterior.
  • Os prazos TTL das regras em si não são persistidos. Regras readotadas são tratadas provisoriamente como permanentes até que o novo SubscribedAck chegue; esse ack autoritativo substitui exatamente seus tempos de vida, incluindo encurtar um prazo ou transformar uma regra provisoriamente permanente de volta em uma regra de TTL finito. As chaves DNS exatas restauradas são inventariadas e representadas por proprietários provisórios limitados (as capacidades reais do mapa fixado são o limite); a primeira configuração DNS autoritativa descarta toda reivindicação sintética, remove as chaves que ficaram sem proprietário e preserva uma chave apenas quando ela tem separadamente um proprietário vivo normal. Um inventário não canônico/conflitante aborta a restauração sem adivinhar ou publicar parcialmente metadados de propriedade.
  • Não há documento de estado desejado local persistido. Se nenhum plano de controle estiver configurado ou acessível, nenhuma alteração automática de regra ocorre: um mapa fixado adotado continua aplicando seus CIDRs protegidos conhecidos, representados no registro do espaço do usuário como provisórios até que uma atualização completa seja recebida. Uma restauração que não pode adotar pins válidos recria o anexo em seu modo persistido com mapas vazios (falha-fechada para lista de permissões/bloquear tudo). Um orquestrador independente deve reproduzir netfenced apply-rules após cada reinicialização do daemon para substituir o estado de pacote provisório e restaurar seu estado desejado completo e TTLs.
  • As regras de domínio DNS, substituições upstream por anexo e limites de DNS do anexo são estado desejado de tempo de execução fornecido através da API local ou plano de controle; eles não são persistidos. Um anexo restaurado cujo último modo DNS era ALLOWLIST, DENYLIST ou PROXY inicia seu resolvedor em uma postura ALLOWLIST vazia, retornando REFUSED até que um BulkUpdate ou SubscribedAck completo seja aplicado. Um modo DNS explicitamente DISABLED permanece encaminhando. Se qualquer ouvinte UDP/TCP confirmado morrer inesperadamente, o anexo é colocado em quarentena em IP BLOCK_ALL e relatado como um erro de cancelamento de assinatura.
  • O servidor DNS por anexo é um componente do espaço do usuário e para com o daemon; enquanto o daemon está inativo, os IPs exatos já resolvidos e fixados continuam funcionando sob a última política de pacote conhecida, mas novos nomes não podem ser resolvidos através dele. Seus prazos perdidos no espaço do usuário são tratados provisoriamente em vez de adivinhados até a reconciliação autoritativa.

Por anexo

Seu sistema de orquestração chama a API local do daemon.

RPC:``` DaemonService.Attach(interface_name: "veth123", tc_direction: TC_DIRECTION_INGRESS, metadata: {vm_id: "abc"}) // or DaemonService.Attach(cgroup_path: "/sys/fs/cgroup/...", metadata: {container_id: "xyz"})

root@kitploit:~
**CLI:**```bash
# Attach to a host-side veth peer or VM tap (TC) - use ingress direction
netfenced attach --interface veth123 --direction ingress --metadata vm_id=abc

# Attach to a cgroup
netfenced attach --cgroup /sys/fs/cgroup/... --metadata container_id=xyz

# Attach to an uplink inside the workload's own netns (TC) - egress is the default
netfenced attach --interface eth0 --metadata tenant=acme,env=prod

Direção TC: o campo tc_direction (CLI --direction) seleciona qual hook TCX o filtro se anexa, e escolher o correto depende de qual lado do link a interface está:

InterfaceDireção corretaPorquê
Uplink (ex.: eth0), ou qualquer interface dentro do próprio netns da carga de trabalhoegress (padrão)Os pacotes de saída da carga de trabalho são transmitidos através dela; o endereço de destino é o destino real.
Par veth do lado do host ou tap de VM (ex.: fcr-*)ingressOs pacotes de saída da carga de trabalho chegam ao host em essa interface. Egress lá veria, em vez disso, o tráfego de retorno host→carga de trabalho e filtraria pelo próprio endereço da carga de trabalho em vez do destino real.

A direção aplica-se apenas a anexações de interface (TC); é ignorada para anexações de cgroup.

  • O daemon anexa um filtro eBPF ao alvo.
  • Quando control_plane.url está configurado, o daemon envia Subscribed{id, target, type, metadata} e aguarda SubscribedAck com configuração inicial (modo, CIDRs, regras DNS). Se o plano de controle não responder dentro do tempo limite (padrão 5s, configurável via control_plane.subscribe_ack_timeout), a anexação é revertida e a chamada de anexação falha. Validação e outras falhas de pré-confirmação seguem a mesma regra comum de reversão.
  • Sem plano de controle configurado, a anexação é confirmada imediatamente no modo desabilitado; use os comandos de política local abaixo para configurá-la.
  • Uma política inicial válida que atinge uma falha de mapa protegido/armazenamento é a exceção de erro deliberado confirmado: o daemon mantém a anexação em BLOCK_ALL durável em vez de revertê-la destrutivamente. Com um tempo limite limitado, Attach retorna um erro contendo o ID de anexação retido; o chamador pode descobrir esse ID combinando o alvo em List, e o plano de controle deve recuperá-lo com um BulkUpdate completo.
  • Com subscribe_ack_timeout: 0, um novo Attach retorna após enfileirar Subscribed; um ack posterior ainda é validado e aplicado. Esse valor zero não desabilita a reconciliação de anexações restauradas: as tentativas de restauração aguardam até 5s em segundo plano e tentam novamente em uma conexão posterior, se necessário. Se esse ack posterior atingir pressão protegida, a anexação já retornada é mantida em BLOCK_ALL; SubscribedAck não emite nenhum CommandResult nem erro Unsubscribed, e o daemon não redireciona automaticamente a declaração antes da reinicialização. Detecte policy_degraded mais sua razão, ocupação/capacidade e map_full_drops em Heartbeat, depois envie um BulkUpdate completo com command_id para obter um resultado de recuperação explícito.
  • O daemon monitora a remoção do alvo e envia Unsubscribed automaticamente

RPC:``` DaemonService.Detach(id)

root@kitploit:~
**CLI:**```bash
netfenced detach --id <attachment-id>

Listar anexos:```bash netfenced list netfenced list --all # fetch all pages

root@kitploit:~
### Política local e inspeção

Cada mutação local é uma codificação CLI simples do único
`DaemonService.ApplyCommand(ControlCommand)` RPC. Forneça o ID de anexo
retornado por `attach`:```bash
# Packet policy and protected CIDRs.
netfenced set-mode <id> allowlist
netfenced allow-cidr <id> 10.0.0.0/8
netfenced allow-cidr <id> 192.0.2.10/32 --ttl 5m
netfenced deny-cidr <id> 10.20.0.0/16
netfenced remove-cidr <id> 10.20.0.0/16 --list deny
# --list accepts allow, deny, or both (the default).

# DNS policy.
netfenced set-dns-mode <id> denylist
netfenced allow-domain <id> example.com --subdomains
netfenced deny-domain <id> blocked.example.com
netfenced remove-domain <id> blocked.example.com

# Deterministic current-policy inspection as protobuf JSON.
netfenced rules <id>

Os modos de pacote são disabled, allowlist, denylist e block-all; os modos DNS são disabled, allowlist, denylist e proxy. O DNS proxy requer um plano de controle configurado e alcançável. A correspondência de domínio usa o sufixo de correspondência mais específico; quando regras de permissão e negação igualmente específicas coincidem, a negação vence. CIDRs e domínios são canonicalizados. TTLs, enums, CIDRs, domínios, seletores e mensagens aninhadas negativos, malformados ou de outra forma inválidos são rejeitados antes da mutação, portanto um comando inválido é uma política no-op.

Para uma substituição completa, apply-rules lê a forma JSON do protobuf BulkUpdate existente de um arquivo ou da entrada padrão:```bash cat >rules.json <<'JSON' { "mode": "POLICY_MODE_ALLOWLIST", "allowCidrs": [{"cidr": "10.0.0.0/8"}], "dns": { "mode": "DNS_MODE_DENYLIST", "denyDomains": [{"domain": "blocked.example.com", "includeSubdomains": true}] } } JSON netfenced apply-rules --file rules.json

Equivalent stdin form:

netfenced apply-rules --file - < rules.json

root@kitploit:~
`ApplyCommand` aceita apenas `SetMode`, `AllowCIDR`, `DenyCIDR`, `RemoveCIDR`, `BulkUpdate`, `SetDnsMode`, `AllowDomain`, `DenyDomain` e `RemoveDomain`.
Variantes apenas de stream sync/ack, comandos desconhecidos ou vazios e valores locais de `command_id` são rejeitados. `BulkUpdate` é também a única operação local que pode recuperar uma política de pacotes degradada estável; ela deve conter o estado desejado completo de LPM e DNS.

O seletor opcional `ControlCommand.remove_cidr_list` pode direcionar a lista de permissão, a lista de negação ou ambas quando a variante do comando é `RemoveCIDR`. Seu valor legado não especificado e o explícito `BOTH` removem de ambas as listas, preservando o comportamento original do protocolo.

Mutações locais e do plano de controle compartilham um único analisador, barreira de mutação, registro de TTL, caminho de recuperação fail-closed e proprietário de política. Não há deliberadamente arbitragem de propriedade local versus plano de controle: operações conflitantes em uma lista de políticas individual entram em vigor na ordem em que foram cometidas, independentemente da origem. Em particular, um `BulkUpdate` ou `SubscribedAck` posterior completo do plano de controle pode substituir o estado local.

`GetRules`/`netfenced rules` retorna um instantâneo coerente e determinístico do registro do espaço de usuário. Cada CIDR reporta lista de permissão/negação, `policyOwned` local ou do plano de controle, `systemOwned` do daemon, `expiresAt` absoluto, `provisional` restaurado e último estado `installed` do kernel cometido. Uma entrada instalada sem nenhum proprietário é uma nova tentativa de remoção falha, não política desejada. A saída de DNS é o `DnsConfig` efetivo, normalizado e ativo. A inspeção intencionalmente não enumera mapas do kernel protegidos nem expõe entradas de cache de host exato de DNS resolvido dinamicamente; use telemetria de heartbeat para ocupação de mapa protegido.

## Sobre o plano de controle (você implementa isso)

Implemente a RPC `ControlPlane.Connect` - um stream bidirecional:

Configure a política de enforcement de keepalive do seu servidor gRPC para permitir a cadência de ping do daemon (`control_plane.keepalive_time`, padrão 30s): defina `MinTime` igual ou abaixo desse intervalo e `PermitWithoutStream: true`. A política padrão do gRPC (5 minutos) trata os pings do daemon como abusivos e fecha a conexão com `ENHANCE_YOUR_CALM (too_many_pings)`. Em Go:```go
grpc.NewServer(grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
    MinTime:             10 * time.Second,
    PermitWithoutStream: true,
}))

Receber do daemon:

  • SyncRequest na conexão/reconexão (lista os anexos atuais)
  • Subscribed quando novos anexos são adicionados, e após SyncRequest para anexos restaurados que ainda precisam de estado autoritativo atualizado
  • Unsubscribed quando anexos são removidos
  • Heartbeat com estatísticas
  • CommandResult{command_id, id, success, error} — resultado de qualquer comando enviado com um command_id não vazio (nonce de correlação opcional no ControlCommand; comandos sem ele não produzem resultado). success é verdadeiro apenas se o comando foi totalmente aplicado — um BulkUpdate parcialmente aplicado relata falha com o erro agregado. Os resultados são de melhor esforço: trate um resultado ausente como desconhecido, não como falha.

Enviar ao daemon:

  • SyncAck após receber SyncRequest
  • SubscribedAck{mode, cidrs, dns_config} após receber Subscribed (obrigatório - o daemon aguarda por isso)
  • SetMode{mode} - alterar o modo da política de filtro IP
  • AllowCIDR{cidr, ttl} / DenyCIDR / RemoveCIDR (opcionalmente selecionar allow, deny ou ambos; não especificado mantém o comportamento legado "ambos")
  • SetDnsMode{mode} - alterar modo de filtragem DNS
  • AllowDomain{domain} / DenyDomain / RemoveDomain (a correspondência mais específica vence; deny vence em empate de especificidade igual)
  • BulkUpdate{mode, cidrs, dns_config} - sincronização completa de estado

Quando o plano de controle recebe Subscribed, ele deve responder com um SubscribedAck completo. Para um novo anexo, o daemon normalmente aguarda esse ack antes de retornar sucesso ao chamador local. Para um anexo restaurado, o handshake ocorre em segundo plano enquanto a política fixada conhecida continua a ser aplicada. Use os metadados para identificar a VM/tenant/container e retorne o modo desejado completo, CIDRs (incluindo TTLs) e estado DNS; uma configuração DNS omitida significa desabilitado com listas de domínios vazias.

Reconexões e idempotência (obrigatório)

SyncRequest é o ponto de reconciliação autoritativa: em cada (re)conexão, é o primeiro evento no stream e lista o conjunto completo de anexos atuais do daemon. Reconcilie sua visão em relação a ele — adicione anexos que você não conhecia, remova aqueles ausentes da lista. Na reconexão, o daemon limpa eventos que estavam enfileirados na conexão anterior (a sincronização os substitui), portanto você não verá Heartbeats obsoletos, Unsubscribeds para anexos já ausentes da sincronização, ou CommandResults da conexão morta reproduzidos após ela. Dois casos extremos permanecem por design, e seu plano de controle DEVE tratá-los de forma idempotente:

  • Um Subscribed pode seguir um SyncRequest que já lista o mesmo id. Isso acontece quando o ack de um novo anexo estava pendente durante a reconexão, e deliberadamente para cada anexo restaurado até que um ack autoritativo seja aplicado completamente. Trate-o como uma atualização, responda com um SubscribedAck completo e atualizado, e nunca o descarte como duplicado. SyncRequest reconcilia o inventário de anexos; SubscribedAck reconcilia a política desejada.
  • Um evento gerado concorrentemente com a (re)conexão pode competir com o snapshot de sincronização em qualquer direção. Trate um Unsubscribed para um id de anexo desconhecido ou já removido como uma operação nula.

Tempos de vida das regras (TTLs)

  1. Entradas CIDR (comandos AllowCIDR/DenyCIDR e as listas CIDR em SubscribedAck/BulkUpdate) carregam um TTL opcional. Regras com TTL são removidas por um janitor do daemon quando expiram (intervalo de verificação ttl_janitor_interval, padrão 1s); regras sem TTL são permanentes.
  2. Readições incrementais de AllowCIDR/DenyCIDR estendem um CIDR para o prazo posterior — nunca encurtam — e uma readição incremental sem TTL o torna permanente. Em contraste, o estado completo em SubscribedAck/BulkUpdate substitui cada tempo de vida do plano de controle exatamente, portanto a reconciliação autoritativa pode encurtar um TTL ou mudar permanente para finito sem remover/readicionar a entrada do mapa ativa. Use RemoveCIDR para descartar uma regra incremental precocemente.
  3. IPs resolvidos por DNS entram apenas no nível exato com o TTL do registro limitado pelo piso de dns.min_filter_ttl (padrão 60s; zero/não definido significa o padrão, não "sem piso"). Um TTL upstream de zero, portanto, dura pelo piso; um TTL PROXY omitido é explicitamente padronizado para 300s antes da aplicação do piso. Uma regra CIDR permanente ou de maior duração cobrindo o mesmo endereço permanece instalada independentemente no nível LPM protegido quando a propriedade exata de DNS expira.
  4. CIDRs allow/deny autoritativos/sistema permanecem em quatro mapas LPM protegidos e não expulsáveis, dimensionados por filter.max_rule_entries por anexo (padrão 4096 cada); consulte "Capacidade de CIDR protegido e recuperação com falha fechada" acima. Endereços de host derivados de DNS usam mapas HASH de correspondência exata separados, dimensionados por filter.max_dns_rule_entries (padrão 4096 por família IP), portanto não podem consumir ou expulsar capacidade autoritativa ou deny. A admissão completa de resposta DNS valida/canonicaliza cada endereço e verifica a capacidade física e lógica antes da mutação. Um erro no mapa exato de DNS do kernel restaura seu snapshot exato anterior à chamada; se essa reversão não puder ser comprovada, o resolvedor suprime a resposta e coloca o anexo em quarentena no IP durável BLOCK_ALL antes de aceitar outra mutação.