تعليم حركة المرور القادمة من cgroup واحدة هو الجزء السهل. لا بد من توافق ثلاثة قرارات منفصلة قبل أن يخرج تطبيق واحد عبر الواجهة الفيزيائية بينما يبقى كل شيء آخر داخل النفق: أي socket ينتمي إلى الاستثناء، وأي بحث في التوجيه يُشغّله التعليم، وأي عنوان مصدر تحمله الحزمة عند خروجها. معظم المحاولات الفاشلة تصيب الأول، وتصيب الثاني جزئياً، ولا تنظر إلى الثالث أبداً.

ثلاثة قرارات لا قرار واحد

التصنيف يجيب عن من هو مستثنى. التوجيه يجيب عن إلى أين تذهب الحزمة المعلّمة بدلاً من ذلك. اختيار المصدر يجيب عمّا سيراه الطرف الآخر وأين ينبغي أن تعود الإجابة.

هذه تفشل بشكل مستقل. يمكن تصنيف حزمة وإعادة توجيهها إلى الواجهة الفيزيائية، وهي لا تزال تحمل عنواناً خاصاً لا يستطيع أي موجّه أعلى إعادته، ما ينتج مهلة انتهاء تشبه تماماً حجب جدار ناري. لا شيء في خطوة التصنيف ينبهك إلى ذلك. ولا شيء في خطوة التوجيه كذلك.

الترتيب مهم أيضاً. يحدث التصنيف عند توليد الحزمة، ويحدث التوجيه بعد ذلك، وتحدث الترجمة عند نقطة الخروج.

إذا لم تستطع تسمية أي من المراحل الثلاثة معطوب، فأنت لا تُصلح الخطأ، بل تعيد ترتيب القواعد وتأمل.

طابق cgroup ستبقى موجودة غداً

يستطيع nftables اختبار عضوية socket في cgroup v2، وهذا مفيد فعلاً لأنه يتبع العملية لا الوجهة. المشكلة هي المعرّف. التطبيق المكتبي الذي يُشغّل من قائمة يعيش غالباً في scope مؤقت يتغير اسمه عند التشغيل التالي.

nft list ruleset
systemctl status session.slice 2>/dev/null
cat /proc/self/cgroup

نسخ اسم scope من جلسة واحدة وترميزه بشكل ثابت ينتج قاعدة تعمل اليوم وتتوقف بصمت عن المطابقة غداً. ضع التطبيق بدلاً من ذلك في سياق تنفيذ مستقر ومقصود، مثل scope أو وحدة مخصصة تتحكم بها، وطابق ذلك. عندئذ تنجو القاعدة من إعادة التشغيل وتحديثات الحزم. وتصبح أيضاً شيئاً يستطيع زميل أن يستنتج منه دون سجل جلستك.

كن دقيقاً أيضاً بشأن الآلية التي تستخدمها. تصنيف net_cls في cgroup v1 ومطابقة socket في cgroup v2 شيئان مختلفان، ووجود حقل باسم مشابه في التوثيق لا يجعلهما قابلين للتبادل.

سلسلة من النوع route هي التي تُطلق البحث الثاني

بالنسبة للحركة المولدة محلياً، يحدد نوع السلسلة ما إذا كان سيحدث أي شيء بعد ضبط التعليم. سلسلة خرج من النوع filter تستطيع تعليم حزمة وسيستمر النواة في استخدام المسار الذي اختاره بالفعل. أما سلسلة من النوع route فهي الآلية التي تطلب بحثاً جديداً.

table inet bypass {
  chain mark {
    type route hook output priority mangle; policy accept;
    socket cgroupv2 level 2 "app.slice" meta mark set 0x1 comment "app bypass"
  }
}
ip rule add fwmark 0x1 lookup 200
ip route add default via 203.0.113.1 dev wlan0 table 200

يحتاج التعليم بعد ذلك إلى قاعدة سياسة ترسله إلى جدول، ويجب أن يكون ذلك الجدول كاملاً: مسار افتراضي، وأي وجهات محلية لا يزال التطبيق يحتاجها، وبوابة أعلى تستجيب فعلاً.

اختر قيمة التعليم بوعي. يستخدم WireGuard علامة fwmark خاصة به لحركة النقل الخارجية، والوصول إلى الرقم نفسه بالخطأ قد يتعارض مع استثناء التوجيه الخاص بالنفق بطرق مؤلمة في التشخيص.

عنوان المصدر لا يتبع المسار

الsocket التي اختارت بالفعل عنوان مصدر لمسار النفق تحتفظ بذلك العنوان بعد إعادة التوجيه. لا شيء في تغيير التوجيه يعيد كتابته، ولهذا تخرج الحزمة من الواجهة الفيزيائية بمصدر خاص لا تستطيع الشبكة العليا إعادته.

العرض المرئي هو تطبيق يبدو أنه يرسل بيانات ولا يتلقى إجابة أبداً، وسجل جدار ناري لا يُظهر شيئاً لأن الحزمة لم تعد أبداً.

الإصلاح الضيق هو قاعدة ترجمة مصدر محصورة في تعليم ذلك التطبيق وواجهة خرجه الحقيقية، مدمجة مع أي NAT موجود بالفعل بدلاً من استبداله:

table inet bypass {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    meta mark 0x1 oifname "wlan0" masquerade
  }
}

ترجم الاستثناء ولا شيء غيره. قاعدة masquerade شاملة تُصلح العرض وتُدمر قدرتك على معرفة أي حركة سلكت أي مسار. طابق التعليم وواجهة الخروج الحقيقية، وإن أمكن عنوان التطبيق نفسه. القواعد الواسعة سهلة الكتابة وشبه مستحيلة التدقيق لاحقاً.

ما يزال يفلت من الاستثناء

قد يأتي تحليل الأسماء من عملية resolver منفصلة في cgroup مختلفة، لذا فتصنيف socket المتصفح لا يصنّف استعلاماته. إذا بقيت استعلامات resolver داخل النفق بينما يخرج الاتصال خارجه، فقد لا تتطابق إجابات split-horizon مع المسار. ذلك التطابق الخاطئ ينتج أعطالاً تبدو كأخطاء DNS وليست كذلك.

ترث العمليات الفرعية الcgroup في معظم الإعدادات، لكن الأداة المساعدة التي تنقل نفسها إلى scope خاص بها لا تفعل ذلك. تحقق بدلاً من الافتراض، خصوصاً لأي شيء يعيد تنفيذ نفسه.

أخيراً، قرر ما ينبغي أن يحدث عندما يكون النفق معطلاً. الاستثناء الذي يصبح بصمت تجاوزاً كاملاً هو وضع أمني مختلف عن الذي يفشل مغلقاً، وواحد فقط منهما هو ما قصدته.

مقالات ذات صلة