
صندوق رمل شبكي حتمي لاختبار قواعد nftables. يستخدم فضاءات أسماء الشبكة Linux العابرة (netns) و Scapy للتحقق من منطق جدار الحماية بأمان.
لماذا NSE؟ • عقد الأوراكل • تحققات على مستوى الشبكة • الميزات • المتطلبات • التثبيت • البدء السريع • كيف يعمل • هيكل المشروع
يمثل اختبار مجموعات قواعد جدار الحماية على نظام Linux حي مخاطر كبيرة: فالقواعد المشوّهة قد تُسقط جلسات إدارة SSH، أو تسرّب حركة نصية صريحة أثناء الاختبار، أو تترك جداول جدار حماية يتيمة نشطة على المضيف.
يوفر Network Sandbox Engine (NSE) بيئة اختبار آمنة وقابلة للتكرار. فهو يبني فضاءات أسماء شبكية Linux مؤقتة، ويصل أزواج إيثرنت افتراضية، ويُجمّع مجموعات قواعد nftables، ويحقن حزمًا اصطناعية من الطبقة الثانية والثالثة باستخدام Scapy. تتم كل عملية تقييم داخل فضاء اسم العزل: لا تُعدَّل حالة جدار حماية المضيف أبدًا.
خصائص معمارية رئيسية:
nse_<uuid>) وتُزال بالكامل أثناء التنظيف.nse/ نحو 1200 عبارة بتغطية اختبار 98%.ينشئ NSE فضاءات أسماء شبكية، ويحمّل مجموعات قواعد nftables، ويقرأ أحداث تتبّع النواة، لذا يعمل بصلاحيات الجذر. وهو لا يفتح مقبسًا ولا منفذًا ولا نقطة نهاية RPC من أي نوع — فهو مكتبة وواجهة سطر أوامر تستدعيها أنت، ولا يحتفظ بالصلاحيات إلا طوال مدة التشغيل.
أزالت النسخة 2.1.0 واجهة الويب FastAPI/Svelte التي كانت تُشحن مع الإصدارات السابقة. كانت تلك الواجهة تعمل داخل العملية بصلاحيات الجذر بدءًا من 2.0.0، وهو ما شكّل سطح هجوم كبير لأداة اختبار؛ ويبقى الكود في تاريخ git عند الوسم v2.0.0 إن كنت بحاجة إليه.
اختبار جدار الحماية هو تأكيد سلبي — "هذه الحزمة لم تمر" — والتأكيد السلبي لا قيمة له ما لم يُعرف أن الأداة تعمل. فمراقب التتبّع الذي لم يتصل بالنواة أبدًا وجدار الحماية الذي حجب كل شيء ينتجان مخرجات متطابقة بايتًا ببايت.
لذلك يرفض NSE الإبلاغ عن حكم لا يستطيع إثبات أنه قاسه:
| الضمان | الآلية |
|---|---|
| تم إرفاق المراقب قبل أول حزمة اختبار | يتم حقن إشارة جاهزية (canary) وإعادة حقنها حتى يتم رصد أثرها في النواة. لا رصد، لا تشغيل. |
| كان المراقب لا يزال مرفقًا بعد آخر حزمة | تعمل إشارة حيوية (liveness canary) بعد الحقن. إذا فاتت، يُعلن أن تدفق الأحكام مبتور. |
| فهم المحلل ما قالته النواة | تُحسب أسطر التتبع التي لا يطابقها أي نمط، وأي عدد أكبر من صفر يُعد خطأً وليس سجل تصحيح. |
| لم يمت المراقب بصمت | تسجّل حلقة القراءة سبب انتهائها — توقف نظيف، أو EOF غير متوقع، أو مهلة، أو انهيار — ولا يُقبل سوى التوقف النظيف. |
| الحكم المفقود ليس نجاحًا | يفشل مشغّل CLI عندما يختلف عدد الأحكام المرصودة عن العدد المتوقع، في أي من الاتجاهين. |
تُستبعد حزم الإشارة (canary) من النتائج عبر معرّف التتبع، لذا لا تظهر أبدًا في تدفق الأحكام الخاص بك.
تُثبت مجموعة الاختبارات أن هذا صحيح، بدلًا من مجرد الادعاء به: يُجبر make test-blind
المحلل على عدم فهم أي شيء، ويفشل البناء ما لم يخرج المشغّل بقيمة غير صفرية. تعمل تلك المهمة في CI عند كل دفعة.
يبلّغ nft monitor trace عمّا قررته مجموعة القواعد. لا يمكنه الإبلاغ عمّا غادر واجهةً لم تطابقها مجموعة القواعد قط، وهو السؤال الذي يطرحه ادعاء انعدام التسريب فعليًا. يغطي PCAPAsserter ذلك: غلاف AsyncSniffer يلتقط على واجهة محددة بالاسم ويعيد الإطارات، بحيث يمكن للاختبار أن يؤكد أن محفّزًا ما أنتج لا شيء.
from nse import PCAPAsserter
asserter = PCAPAsserter(iface="veth-host")
await asserter.start()
# ... send the stimulus ...
leaked = await asserter.stop()
assert not leaked, leaked
تنطبق القاعدة نفسها الخاصة بـ trace oracle، وهي مسؤولية المستدعي أن يفرضها: فمُلتقط الحزم الذي لم يرتبط قط وجدار الحماية الذي حجب كل شيء كلاهما يُعيدان قائمة فارغة. التقط المحفّز مرة واحدة مع تفريغ مجموعة القواعد واشترط أن تكون الإطار مرئيًا قبل تأكيد غيابه.
المرشّح الافتراضي يكبح ضوضاء link-local، ولا شيء غير ذلك.
يستثني DEFAULT_FILTER أنواع ARP وICMPv6 من 133 إلى 137 - أي Neighbour Discovery، الذي يحمل حد قفزات قدره 255 ولا يمكنه الوصول إلى مراقب بعيد. يُلتقط كل ما عدا ذلك، بما في ذلك ICMPv6 echo إلى عنوان قابل للتوجيه عالميًا. تُدمج وسيطة filter مع ذلك الافتراضي؛ أما replace_default_filter=True فيستخدم مرشّحك وحده. يوجد هذا المخرج لأن المرشّح يُصرَّف إلى BPF ويُقيَّم في النواة: فالحزمة التي يستثنيها لا تصل أبدًا إلى مساحة المستخدم، لذا لا يمكن لأي مستهلك استعادتها في المراحل اللاحقة. حتى الإصدار 2.1.2 كان الافتراضي يقرأ not arp and not icmp6، وهو ما كان يتجاهل ICMPv6 القابل للتوجيه أيضًا - انظر issue #14.
run_test_pipeline) تُعيد نماذج Pydantic منظّمة (TestRequest، TraceEvent).nse_<id>) موصولة مباشرة بالمضيف.nse_router_<id>) وخادم (nse_server_<id>) لاختبار التمرير وNAT.PCAPAsserter على واجهة مسمّاة بحيث يمكن للاختبار أن يؤكد أن محفّزًا ما لم يُنتج أي إطار على الإطلاق. انظر Asserting on the wire.nse-runner). يخرج بقيمة غير صفرية عند حكم خاطئ وعند حكم فشل في رصده.mypy --strict)، وفرض الحدود المعمارية (import-linter)، وتنسيق ruff، وسقّاطة تغطية (make coverage، الحد الأدنى 98%).nft)ip)ip netns وعمليات تتبّع النواة)على أنظمة Debian أو Ubuntu:
sudo apt update && sudo apt install -y nftables iproute2 conntrack
ثبّت المحرك الأساسي مع دعم واجهة سطر الأوامر (CLI):
pip install "network-sandbox-engine[cli]"