
Детерминированная сетевая песочница для тестирования правил nftables. Использует эфемерные сетевые пространства имён Linux (netns) и Scapy для безопасной проверки логики межсетевого экрана.
Зачем NSE? • Возможности • Требования • Установка • Быстрый старт • Как это работает • Структура проекта
Тестирование наборов правил межсетевого экрана на работающей системе Linux сопряжено со значительными рисками: некорректные правила могут разорвать SSH-сессии управления, допустить утечку трафика в открытом виде во время тестирования или оставить осиротевшие таблицы межсетевого экрана активными на хосте.
Network Sandbox Engine (NSE) предоставляет безопасный воспроизводимый стенд для тестирования. Он создаёт эфемерные сетевые пространства имён Linux, соединяет пары виртуальных ethernet-интерфейсов, компилирует наборы правил nftables и внедряет синтетические пакеты уровня 2 и уровня 3 с помощью Scapy. Вся оценка происходит внутри пространства имён песочницы: состояние межсетевого экрана хоста никогда не изменяется.
Ключевые архитектурные свойства:
nse_<uuid>) и полностью удаляются при демонтаже.nse/ — это ~1150 операторов при 98% покрытии тестами.NSE создаёт сетевые пространства имён, загружает наборы правил nftables и читает события трассировки ядра, поэтому работает от имени root. Он не открывает сокет, порт или RPC-эндпоинт какого-либо рода — это библиотека и CLI, которые вы вызываете, и он обладает привилегиями только на время выполнения.
Версия 2.1.0 удалила веб-интерфейс на FastAPI/Svelte, который поставлялся в более ранних релизах. Этот интерфейс работал внутри процесса от имени root начиная с версии 2.0.0, что представляло собой большую поверхность атаки для инструмента тестирования; код остаётся в истории git под тегом v2.0.0, если он вам нужен.
Тест межсетевого экрана — это отрицательное утверждение — «этот пакет не прошёл» — а отрицательное утверждение ничего не стоит, если не известно, что инструмент работает. Монитор трассировки, который так и не подключился к ядру, и межсетевой экран, который заблокировал всё, дают побайтово идентичный вывод.
Поэтому NSE отказывается сообщать вердикт, который он не может показать, что измерил:
| Гарантия | Механизм |
|---|---|
| Монитор был подключён до первого тестового пакета | Канарейка готовности внедряется и повторно внедряется до тех пор, пока не будет зафиксирована её трассировка ядра. Нет наблюдения — нет запуска. |
| Монитор был всё ещё подключён после последнего | Канарейка живости запускается после внедрения. Если она пропущена, поток вердиктов объявляется усечённым. |
| Парсер понял то, что сказало ядро | Строки трассировки, не совпавшие ни с одним шаблоном, подсчитываются, и любое значение больше нуля является ошибкой, а не отладочным логом. |
| Монитор не умер тихо | Цикл чтения записывает почему он завершился — чистая остановка, неожиданный EOF, тайм-аут или сбой — и только чистая остановка приемлема. |
| Отсутствующий вердикт — это не прохождение | CLI-раннер завершается с ошибкой, когда число наблюдаемых вердиктов отличается от ожидаемого, в любую сторону. |
Канареечные пакеты исключаются из результатов по trace id, поэтому они никогда не появляются в вашем потоке вердиктов.
Набор тестов доказывает, что это выполняется, а не утверждает это: make test-blind заставляет парсер не понимать ничего, и сборка завершается с ошибкой, если раннер не выходит с ненулевым кодом. Эта задача запускается в CI при каждом push.
run_test_pipeline), возвращающий структурированные модели Pydantic (TestRequest, TraceEvent).nse_<id>), подключённое напрямую к хосту.nse_router_<id>) и сервера (nse_server_<id>) для тестирования пересылки и NAT.nse-runner). Завершается с ненулевым кодом при неверном вердикте и при вердикте, который не удалось наблюдать.mypy --strict), контроль архитектурных границ (import-linter), форматирование ruff и храповик покрытия (make test-cov, порог 98%).nft)ip)ip netns и операций трассировки ядра)В системах Debian или Ubuntu:
sudo apt update && sudo apt install -y nftables iproute2 conntrack
Установите основной движок с поддержкой CLI:
pip install "network-sandbox-engine[cli]"
Для локальной разработки:
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())
Создайте файл теста 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 задаётся для каждого пакета. Неизвестные ключи отклоняются, а не принимаются по умолчанию, поэтому опечатка приводит к провалу набора тестов, а не тихо превращается в ожидание, которое вы никогда не писали.
Запустите набор тестов с привилегиями root:
sudo nse-runner --file firewall_test.yaml
Коды выхода: 0 — все пакеты совпали; 1 — вердикт был неверным или движок не смог его наблюдать. Ошибки оракула сообщаются отдельно от сбоев межсетевого экрана, потому что они означают, что сломалось измерение, а не набор правил.
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
Полезно для фиксации версии nftables, против которой тестируются ваши правила.
NSE оркестрирует сетевые подсистемы ядра Linux и интерфейсы трассировки через структурированный многоэтапный конвейер выполнения:
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() выполняет пробный запуск набора правил с помощью nft --check -f.NetnsController создаёт изолированное сетевое пространство имён и настраивает интерфейсы виртуального ethernet (veth).meta nftrace set 1).ScapyInjector внедряет синтетические кадры через veth-соединение.TraceHarvester захватывает события nft monitor trace и возвращает структурированные объекты TraceEvent.Полные технические спецификации см. в Руководстве по технической архитектуре.
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
Один тег. git push origin vX.Y.Z собирает, подписывает с помощью Sigstore, публикует GitHub Release, загружает в TestPyPI, устанавливает из TestPyPI и выполняет дымовой тест, и только затем загружает в PyPI. Отрепетируйте с помощью make release-dry.
См. docs/RELEASING.md.
Полная интерактивная веб-документация доступна по адресу:
https://onyks-os.github.io/nse/
Соберите документацию локально:
make docs
Запустите документацию с горячей перезагрузкой на http://127.0.0.1:8000:
make docs-serve
Запустите статический линтинг и модульные тесты:
make verify
Запустите полную локальную проверку CI (включает линтинг, модульные тесты, сборку фронтенда, сборку документации, дымовой тест PyPI и привилегированные интеграционные тесты):
make ci-local
Этот проект лицензирован под MIT License.