
Um sandbox de rede determinístico para testar regras de nftables. Ele usa namespaces de rede Linux efêmeros (netns) e Scapy para validar a lógica de firewall com segurança.
Por que NSE? • Recursos • Requisitos • Instalação • Início Rápido • Como Funciona • Estrutura do Projeto
Testar conjuntos de regras de firewall em um sistema Linux em produção apresenta riscos significativos: regras malformadas podem derrubar sessões SSH de gerenciamento, vazar tráfego em texto claro durante os testes ou deixar tabelas de firewall órfãs ativas no host.
O Network Sandbox Engine (NSE) fornece um ambiente de teste seguro e reproduzível. Ele constrói namespaces de rede Linux efêmeros, conecta pares de ethernet virtuais, compila conjuntos de regras nftables e injeta pacotes sintéticos de Camada 2 e Camada 3 usando Scapy. Toda a avaliação acontece dentro do namespace sandbox: o estado do firewall do host nunca é alterado.
Propriedades arquiteturais principais:
nse_<uuid>) e são completamente removidos durante o encerramento.nse/ tem ~1150 instruções com 98% de cobertura de testes.O NSE cria namespaces de rede, carrega conjuntos de regras nftables e lê eventos de trace do kernel, portanto é executado como root. Ele não abre um socket, uma porta ou um endpoint RPC de qualquer tipo — é uma biblioteca e uma CLI que você invoca, e mantém privilégios apenas durante a execução.
A versão 2.1.0 removeu a interface web FastAPI/Svelte que as versões anteriores
incluíam. Essa interface era executada em processo como root a partir da 2.0.0,
o que representava uma grande superfície de ataque para uma ferramenta de teste;
o código permanece no histórico do git na tag v2.0.0 caso você precise dele.
Um teste de firewall é uma asserção negativa — "este pacote não passou" — e uma asserção negativa não vale nada a menos que o instrumento seja conhecido por estar funcionando. Um monitor de trace que nunca se conectou ao kernel e um firewall que bloqueou tudo produzem saída byte a byte idêntica.
Portanto, o NSE se recusa a reportar um veredito que não pode demonstrar que mediu:
| Garantia | Mecanismo |
|---|---|
| O monitor foi conectado antes do primeiro pacote de teste | Um canário de prontidão é injetado e reinjetado até que seu trace no kernel seja observado. Sem observação, sem execução. |
| O monitor ainda estava conectado após o último | Um canário de vivacidade é executado após a injeção. Se for perdido, o fluxo de vereditos é declarado truncado. |
| O parser entendeu o que o kernel disse | Linhas de trace que nenhum padrão corresponde são contadas, e qualquer contagem acima de zero é um erro em vez de um log de depuração. |
| O monitor não morreu silenciosamente | O loop de leitura registra por que ele terminou — parada limpa, EOF inesperado, timeout ou crash — e apenas uma parada limpa é aceitável. |
| Um veredito ausente não é uma aprovação | O executor da CLI falha quando o número de vereditos observados difere do número esperado, em qualquer direção. |
Pacotes canário são excluídos dos resultados por trace id, portanto nunca aparecem no seu fluxo de vereditos.
A suíte prova que isso se mantém, em vez de apenas afirmá-lo: make test-blind
força o parser a não entender nada, e a build falha a menos que o executor saia
com código diferente de zero. Essa tarefa é executada na CI a cada push.
run_test_pipeline) retornando modelos Pydantic estruturados (TestRequest, TraceEvent).nse_<id>) conectado diretamente ao host.nse_router_<id>) e Servidor (nse_server_<id>) para testes de encaminhamento e NAT.nse-runner). Sai com código diferente de zero em um veredito errado e em um veredito que não conseguiu observar.mypy --strict), imposição de limites arquiteturais (import-linter), formatação ruff e uma catraca de cobertura (make test-cov, piso de 98%).nft)ip)ip netns e operações de trace do kernel)Em sistemas Debian ou Ubuntu:
sudo apt update && sudo apt install -y nftables iproute2 conntrack
Instale o motor principal com suporte a CLI:
pip install "network-sandbox-engine[cli]"
Para desenvolvimento local:
git clone https://github.com/onyks-os/NetworkSandboxEngine.git
cd NetworkSandboxEngine
make setup
import asyncio
from nse.core.netns_controller import NetnsController
from nse.core.pipeline import run_test_pipeline
from nse.models.test_request import TestRequest, PacketSpec
rules = """
table ip filter {
chain input {
type filter hook input priority 0; policy drop;
tcp dport 80 accept
}
}
"""
request = TestRequest(
rules=rules,
packets=[
PacketSpec(protocol="tcp", src_ip="10.0.0.1", dst_ip="10.0.0.2", dst_port=80),
PacketSpec(protocol="tcp", src_ip="10.0.0.1", dst_ip="10.0.0.2", dst_port=22),
],
)
async def main():
controller = NetnsController()
events = await run_test_pipeline(request=request, controller=controller)
for evt in events:
if evt.verdict:
print(f"[{evt.chain}] Verdict: {evt.verdict}")
asyncio.run(main())
Crie um arquivo de teste firewall_test.yaml:
tests:
- name: "Allow HTTP Port 80, Drop SSH Port 22"
topology: simple
rules: |
table ip filter {
chain input {
type filter hook input priority 0; policy drop;
tcp dport 80 accept
}
}
packets:
- protocol: tcp
src_ip: 10.0.0.1
dst_ip: 10.0.0.2
dst_port: 80
expected_verdict: ACCEPT
- protocol: tcp
src_ip: 10.0.0.1
dst_ip: 10.0.0.2
dst_port: 22
expected_verdict: DROP
expected_verdict é por pacote. Chaves desconhecidas são rejeitadas em vez de
receberem valor padrão, portanto um erro de digitação faz a suíte falhar em vez
de silenciosamente se tornar uma expectativa que você nunca escreveu.
Execute a suíte com privilégios de root:
sudo nse-runner --file firewall_test.yaml
Códigos de saída: 0 todos os pacotes corresponderam; 1 um veredito estava errado ou o motor
não conseguiu observar um. Erros do oráculo são reportados separadamente de falhas
do firewall, porque significam que a medição quebrou, não o conjunto de regras.
podman build -t nse .
podman run --rm --cap-add=NET_ADMIN --cap-add=NET_RAW \
-v "$PWD/firewall_test.yaml:/suite.yaml:ro" nse --file /suite.yaml
Útil para fixar a versão do nftables contra a qual suas regras são testadas.
O NSE orquestra subsistemas de rede do kernel Linux e interfaces de trace através de um pipeline de execução estruturado em múltiplos estágios:
graph TD
subgraph Step1["1. Test Specification"]
Req["<b>TestRequest</b><br/>ruleset + packets + topology"]
end
subgraph Step2["2. Ephemeral Netns Sandbox"]
direction TB
Netns["<b>Netns Setup</b><br/>nse_<id> & veth links"]
RuleEng["<b>Rule Engine</b><br/>validate & load nftables"]
Inject["<b>Scapy Injector</b><br/>L2/L3 packet injection"]
NFT["<b>Kernel nftables</b><br/>meta nftrace set 1"]
Netns --> RuleEng
RuleEng --> Inject
Inject --> NFT
end
subgraph Step3["3. Trace Evaluation & Oracle"]
direction TB
Harvester["<b>Trace Harvester</b><br/>nft monitor trace stream"]
Oracle["<b>Deterministic Oracle</b><br/>TraceEvents & verdicts"]
Harvester --> Oracle
end
Step1 --> Step2
Step2 --> Step3RuleEngine.validate() executa uma simulação do conjunto de regras usando nft --check -f.NetnsController cria o namespace de rede isolado e configura interfaces de ethernet virtual (veth).meta nftrace set 1).ScapyInjector injeta quadros sintéticos através do link veth.TraceHarvester captura eventos de nft monitor trace e retorna objetos TraceEvent estruturados.Para especificações técnicas completas, consulte o Guia de Arquitetura Técnica.
NetworkSandboxEngine/
├── nse/ # Core PyPI package (network-sandbox-engine)
│ ├── core/ # Kernel primitives, pipeline, and naming rules
│ ├── models/ # Pydantic models (TestRequest, PacketSpec, TraceEvent)
│ └── cli/ # Headless YAML runner entrypoint
├── docs/ # Architecture specs and MkDocs web documentation
├── tests/ # Unit, golden file, and privileged e2e tests
│ └── fixtures/nft_trace/ # Golden `nft monitor trace` corpus
├── pyproject.toml # Build backend configuration
└── Makefile # Local automation and CI workflow
Uma tag. git push origin vX.Y.Z compila, assina com Sigstore, publica o
GitHub Release, envia para o TestPyPI, instala a partir do TestPyPI e faz um smoke test,
e só então envia para o PyPI. Ensaie com make release-dry.
Veja docs/RELEASING.md.
A documentação web interativa completa está disponível em:
https://onyks-os.github.io/nse/
Compile a documentação localmente:
make docs
Sirva a documentação com hot-reload em http://127.0.0.1:8000:
make docs-serve
Execute linting estático e testes unitários:
make verify
Execute a verificação completa de CI local (inclui linting, testes unitários, build do frontend, build da documentação, smoke test do PyPI e testes de integração privilegiados):
make ci-local
Este projeto está licenciado sob a Licença MIT.