
صندوق رمل شبكي حتمي لاختبار قواعد nftables. يستخدم فضاءات أسماء الشبكة Linux العابرة (netns) و Scapy للتحقق من منطق جدار الحماية بأمان.
لماذا NSE؟ • الميزات • المتطلبات • التثبيت • البدء السريع • كيف يعمل • هيكل المشروع
اختبار مجموعات قواعد جدار الحماية على نظام Linux حي يحمل مخاطر كبيرة: القواعد المشوهة قد تُسقط جلسات إدارة SSH، أو تُسرّب حركة نصية واضحة أثناء الاختبار، أو تترك جداول جدار حماية يتيمة نشطة على المضيف.
يوفر Network Sandbox Engine (NSE) بيئة اختبار آمنة وقابلة للتكرار. فهو يبني فضاءات أسماء شبكة Linux مؤقتة، ويوصل أزواج إيثرنت افتراضية، ويُجمّع مجموعات قواعد nftables، ويحقن حزمًا اصطناعية من الطبقة الثانية والثالثة باستخدام Scapy. تحدث كل عملية تقييم داخل فضاء اسم العزل: لا يتم تغيير حالة جدار الحماية على المضيف أبدًا.
الخصائص المعمارية الرئيسية:
nse_<uuid>) وتُزال بالكامل أثناء التفكيك.nse/ حوالي 1150 عبارة بتغطية اختبار 98%.ينشئ NSE فضاءات أسماء شبكة، ويحمّل مجموعات قواعد nftables، ويقرأ أحداث تتبع النواة، لذا يعمل بصلاحيات الجذر. وهو لا يفتح مقبسًا ولا منفذًا ولا نقطة نهاية RPC من أي نوع — إنه مكتبة وواجهة سطر أوامر تستدعيها أنت، ولا يحتفظ بالصلاحيات إلا طوال مدة التشغيل.
أزالت الإصدارات 2.1.0 واجهة الويب FastAPI/Svelte التي كانت تُشحن مع الإصدارات
السابقة. كانت تلك الواجهة تعمل داخل العملية بصلاحيات الجذر بدءًا من الإصدار
2.0.0، وهو ما شكّل سطح هجوم كبير لأداة اختبار؛ لا يزال الكود موجودًا في سجل
git عند الوسم v2.0.0 إذا كنت بحاجة إليه.
اختبار جدار الحماية هو تأكيد سلبي — "هذه الحزمة لم تمر" — والتأكيد السلبي لا قيمة له ما لم يكن من المعروف أن الأداة تعمل. فمراقب التتبع الذي لم يتصل بالنواة أبدًا وجدار حماية حجب كل شيء ينتجان مخرجات متطابقة بايتًا ببايت.
لذلك يرفض NSE الإبلاغ عن حكم لا يستطيع إثبات أنه قاسه:
تُستبعد حزم canary من النتائج بواسطة معرّف التتبع، لذا لا تظهر أبدًا في تدفق أحكامك.
تُثبت مجموعة الاختبارات أن هذا صحيح، بدلًا من مجرد تأكيده: يُجبر make test-blind
المحلل على عدم فهم أي شيء، ويفشل البناء ما لم يخرج المشغّل بقيمة غير صفرية.
تعمل تلك المهمة في CI عند كل دفعة.
run_test_pipeline) تُرجع نماذج Pydantic منظمة (TestRequest، TraceEvent).nse_<id>) موصول مباشرة بالمضيف.nse_router_<id>) وServer (nse_server_<id>) لاختبار التمرير وNAT.nse-runner). يخرج بقيمة غير صفرية عند حكم خاطئ وعند حكم فشل في ملاحظته.mypy --strict)، وفرض حدود معمارية (import-linter)، وتنسيق ruff، ومسنّنة تغطية (، الحد الأدنى 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 لكل حزمة. تُرفض المفاتيح غير المعروفة بدلًا من
استخدام قيم افتراضية، لذا يفشل خطأ إملائي في المجموعة بدلًا من أن يصبح بصمت
توقعًا لم تكتبه أبدًا.
شغّل المجموعة بصلاحيات الجذر:
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 فضاء اسم الشبكة المعزول ويهيّئ واجهات إيثرنت الافتراضية (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، ويرفع إلى 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.
| الضمان | الآلية |
|---|
| كان المراقب متصلًا قبل أول حزمة اختبار | يتم حقن canary الجاهزية وإعادة حقنه حتى تتم ملاحظة أثره في النواة. لا ملاحظة، لا تشغيل. |
| كان المراقب لا يزال متصلًا بعد آخر حزمة | يعمل canary الحيوية بعد الحقن. إذا فُقد، يُعلن أن تدفق الأحكام مبتور. |
| فهم المحلل ما قالته النواة | تُعد أسطر التتبع التي لا يطابقها أي نمط، وأي عدد أكبر من صفر يُعد خطأً وليس سجل تصحيح. |
| لم يمت المراقب بصمت | تسجّل حلقة القراءة سبب انتهائها — توقف نظيف، EOF غير متوقع، مهلة أو انهيار — والتوقف النظيف وحده مقبول. |
| الحكم المفقود ليس نجاحًا | يفشل مشغّل CLI عندما يختلف عدد الأحكام الملاحظة عن العدد المتوقع، في أي من الاتجاهين. |
make test-cov