كيفية توجيه تطبيق واحد عبر VPN دون الجهاز بأكمله
لا يوفّر أي سطح مكتب غير مُدار مفتاح VPN لكل عملية على حدة. اختر الطبقة التي تختار حركة المرور: مقبس مربوط، أو قاعدة UID، أو ملف تعريف جدار ناري، أو قفزة SOCKS.
يتصل عميل VPN، ويعمل المتصفح، ولا يزال الأمر الوحيد الذي يهمك فعلاً يخرج عبر اتصالك المنزلي. أو يحدث العكس: يستولي النفق على المسار الافتراضي فيختفي خادم الملفات المحلي من سطح المكتب. واجهتُ هذا مع وكيل بناء كان عليه الوصول إلى مرآة حزم داخلية بينما بقي بقية المضيف على شبكة المكتب المحلية، ولم يعرض العميل سوى خيارين: كل الحركة أو لا شيء. الطلب في الاتجاهين هو نفسه، هذا البرنامج وليس الجهاز كله، ولا يملك معظم العملاء مربع اختيار لذلك، لأن جدول التوجيه لم يعرف قط ما هو البرنامج.
لماذا لا يستطيع جدول التوجيه رؤية برنامجك
عندما ترسل عمليةٌ حزمةً، يبحث المكدس عن عنوان الوجهة في جدول التوجيه. لا اسم الملف التنفيذي ولا المنفذ يدخل في ذلك البحث. أسماء النطاقات تفشل للسبب نفسه: النفق يحمل الحركة إلى عنوان، وبحلول الوقت الذي يتحول فيه الاسم إلى عنوان، يكون التوجيه هو الرافعة الوحيدة التي ما زالت النواة تملكها. لهذا ينتهي «VPN لهذا الموقع فقط» إلى كونه وسيطاً، وينتهي «VPN لهذا البرنامج فقط» إلى واحد من مجموعة صغيرة من الآليات.
| Mechanism | What selects | Covers | On tunnel drop |
|---|---|---|---|
| Socket bind | source address | a client you control | connections fail |
| UID rule (Linux) | owning user | every process of one account | connections fail |
| Firewall profile (Windows) | adapter profile | one program | fails closed |
| Network namespace | namespace membership | everything started inside | connections fail |
| SOCKS hop | app proxy setting | one app or one browser profile | connections fail |
لا شيء من هذه مسار لكل عملية. كل واحدة منها تنقل القرار إلى طبقة ترى البرنامج أو المستخدم أو إعدادات التطبيق نفسه.
وجهة واحدة تبقى مساراً، لا قاعدة برنامج. على macOS الوصفة المعتادة هي إلغاء تحديد «إرسال كل الحركة عبر VPN» وإضافة مسار مضيف: sudo route add -host internal-tuner.example -interface ppp0، ويُزال مرة أخرى بـ sudo route delete -host internal-tuner.example -interface ppp0. اقرأ اسم الواجهة من ifconfig أولاً، لأن ppp0 مجرد مثال. الرموز البديلة لا تعمل في مسار المضيف، لذا تحتاج الخدمة التي تتحدث إلى عدة أسماء إلى إدخال واحد لكل اسم، والقاعدة تغطي كل عملية على الجهاز وليس العملية التي كنت تقصدها.
اربط المقبس بعنوان النفق
عندما تتحكم بالعميل، يكون الربط أرخص محدِّد. المقبس المربوط بالعنوان المحلي للنفق يخرج عبر تلك الواجهة ما دام يوجد مسار للوجهة هناك، ويحتفظ كل شيء آخر على الجهاز بمساره الطبيعي. الخفاء أن الربط يختار عنوان المصدر بينما يظل جدول التوجيه يختار المخرج. الربط وحده لا ينقل الحركة التي لا مسار لها داخل النفق، وهذا هو الخطأ الكامن وراء معظم تقارير «ربطته ومع ذلك يخرج في الاتجاه الخطأ».
import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))الأدوات الأعلى مستوى تعرض الفكرة نفسها: curl --interface 10.8.0.2، و CURLOPT_INTERFACE في PHP، والوسيط source_address في عملاء HTTP في Python. يجب أن ينتمي العنوان إلى النفق، وخطأ مطبعي هنا يفشل كخطأ اتصال عادي، لا كشكوى توجيه. الربط باسم جهاز بدلاً من عنوان امتياز في Linux ولا ينتقل إلى Windows أو macOS، ولهذا تمرر السكربتات المحمولة العنوان.
Linux: طابق المالك، لا اسم البرنامج
تستطيع النواة مطابقة المستخدم المالك للمقبس، لذا يصبح حساب مخصص هو المحدِّد. ضع النفق في جدول توجيه خاص به وأرسل حركة ذلك الحساب فقط إليه:
ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100هذا يعمل فقط إذا لم يكن النفق هو المسار الافتراضي أصلاً. ابدأ العميل بـ --route-nopull، أو رشّح إعادة التوجيه المدفوعة بـ --pull-filter ignore "redirect-gateway" على OpenVPN 2.4 وما بعده، ثم أضف إدخال الجدول بنفسك. وإلا فإن كلا الافتراضيين موجود ويفوز صاحب الأولوية الأقل. النسخة المتعمدة من ذلك هي توجيه المسار الافتراضي إلى بوابة النفق، أو فعل ذلك من سكربت فصل: يُبقي الحركة بعيداً عن الإنترنت عند سقوط النفق، لكنه مفتاح إيقاف لا محدِّد، ويبقى حتى يزيله أحد.
النسخة الأقوى هي فضاء أسماء شبكي. أنشئه، وشغّل العميل داخله، وابدأ التطبيق بـ ip netns exec. لا شيء خارج فضاء الأسماء يرى واجهة النفق، لذا لا يمكن للتطبيق أن يعود إلى مسار المضيف بالخطأ. التكلفة حقيقية: يحتاج فضاء الأسماء إلى زوج veth للوصول إلى الشبكة الفيزيائية وإلى ملف محلل خاص به، لأن فضاء اسم التركيب لا يشارك إعدادات التحليل لدى المضيف.
نمطا فشل يفسران معظم التسريبات. تحليل الأسماء لا يزال يحدث في محلل المضيف، لذا تخرج الاستعلامات خارج النفق حتى عندما يدخل اتصال TCP. و IPv6 يحتاج إلى ip -6 rule خاص به، لأن قاعدة v4 لا تغطيه.
Windows و macOS: حدّد نطاق الجدار الناري أو سلّم وسيطاً
لا يملك Windows أيضاً محدِّد مسار لكل تطبيق، لكن قواعد جداره الناري يمكن تحديد نطاقها لملف تعريف، ويمكن تصنيف محول VPN كـ«عام» بينما تبقى شبكة المنزل «خاصة». أنشئ قواعد حجب صادرة للبرنامج على ملفي «الخاص» و«النطاق»، واترك «العام» مفتوحاً، فلا يستطيع التطبيق الوصول إلى الشبكة إلا عبر النفق. اضبط فئة الشبكة للاتصال أولاً، ثم أكّد أن القاعدة طابقت في لوحة المراقبة في الجدار الناري. إنه حجب لا مسار: يفشل مغلقاً عند سقوط النفق، ولا يستطيع إيقاف تطبيق يتجاهل الجدار الناري.
يتبع macOS المنطق نفسه بأدوات مختلفة. مستمع SOCKS يُوصل إليه عبر SSH هو الخيار الأقل تدخلاً، إذ ينشئ ssh -D 1080 user@host منفذاً محلياً واحداً ولا تسلك ذلك المسار إلا التطبيقات المهيأة لاستخدامه. قاعدة pf يمكنها حجب برنامج بحسب المستخدم أو المجموعة، لكنها لا تزال عاجزة عن اختيار النفق لذلك البرنامج وحده. لا يستطيع Tunnelblick الترشيح حسب النطاق إطلاقاً، لأن المسارات تعمل على العناوين؛ ويلجأ مستخدمو OpenConnect إلى vpn-slice عندما يدفع الخادم قائمة بادئات أوسع من اللازم.
حلّان بديلان يغطيان ما تفوته أدوات المنصة. إضافات المتصفح التي تبدل الوسيط حسب النطاق هي الجواب العملي لـ «هذا الموقع فقط»، لأنها تعمل في طبقة التطبيق حيث لا تزال الأسماء موجودة؛ وهي تغطي متصفحاً واحداً ولا شيء غيره. الحل الفظ هو تشغيل عميل المورّد داخل جهاز ظاهري بحيث يملك الضيف تغيير المسار بينما يحتفظ المضيف بمساره، بتكلفة مكدسي شبكة يجب صيانتهما.
قفزة SOCKS للأدوات التي لا خيار ربط فيها
يمكن توجيه معظم التطبيقات إلى وسيط HTTP أو SOCKS حتى وهي لا تعرف شيئاً عن الواجهات. عميل SSH يوفر واحداً دون أي تغيير في جهة الخادم:
ssh -N -D 127.0.0.1:9999 [email protected]أي شيء مهيأ لاستخدام ذلك المنفذ يخرج عند الباستيون بينما يحتفظ بقية سطح المكتب باتصاله العادي. نسخة محلية من 3proxy مربوطة بعنوان النفق تفعل الشيء نفسه لمنفذ واحد دون SSH، و stunnel يغطي منفذاً واحداً ملفوفاً بـ TLS. الحدود عملية: الوسيط لا يحمل إلا البروتوكولات التي تعرف كيف تستخدمه، ويحتاج UDP إلى اقتران SOCKS5 UDP لا يفعّله إعداد «SOCKS5» في كثير من العملاء، ويجب أن يحل الوسيط أسماء DNS (socks5h في curl) وإلا تسرب الاسم إلى المحلل المحلي.
أبقِ المستمع على واجهة الاسترجاع ما لم تحتج إليه آلة أخرى.
تحقق من المسار بالتقاط، لا برسالة حالة
نوافذ حالة التطبيق تصف الإعداد، لا الحزمة. التقط على الواجهتين وقارن:
tcpdump -i any -n port 443
ss -tnp | grep 443إذا ظهر الاتصال على الواجهة الفيزيائية بلا إدخال مطابق على النفق، فالمحدِّد غير فعّال، مهما أبلغ العميل. افحص IPv6 والمحلل كلٌّ على حدة، وافحص وجود مسار ثانٍ مثل عملية مساعدة أو وسيط نظام موروث.
ثم اختر الحد الذي يطابق المتطلب: الوسيط يكفي لموقع واحد، وقاعدة UID تكفي لحساب واحد، وملف تعريف الجدار الناري يكفي عندما يجب أن يفشل الاتصال مغلقاً، وفضاء الأسماء هو الخيار الوحيد الذي يخفي النفق عن كل ما هو خارجه.