كيف توجّه حركة المضيف عبر حاوية VPN على Docker
حاوية VPN هي مساحة أسماء شبكية، لا وكيلًا. اختر مساحة أسماء مشتركة أو مستمع SOCKS حقيقيًا أو بوابة موجّهة، ثم تحقّق من مسار الخروج.
حاوية النفق لديك تعمل، وdocker compose exec vpn curl -s https://ifconfig.me يرد بعنوان المزوّد، لكن المتصفح على سطح مكتبك ما زال يخرج عبر مزوّد خدمة الإنترنت. هذه الفجوة متوقعة. الحاوية التي تشغّل عميل VPN تحمل مساحة أسماء شبكية ومسارات وواجهة نفق، ولا شيء يتحدث HTTP أو SOCKS؛ ونشر منفذ عليها لا يمكن أن ينشئ مستمعًا لم يكن موجودًا قط.
مكان تشغيل العميل يحدد التصميم. حاوية شقيقة تستطيع الانتقال إلى مساحة أسماء النفق، وتطبيق سطح المكتب لا يستطيع، والموجّه يحتاج مسارات لا حقل وكيل. أخطئ في التعيين وسيكون العرض واحدًا: رفض الاتصال، أو حركة تسلك الطريق القديم بهدوء.
طابق الترتيبة مع مكان تشغيل العميل
اسأل سؤالًا واحدًا قبل تعديل Compose: هل يمكن تشغيل العميل داخل مساحة أسماء شبكة حاوية VPN؟ كل ما يعمل أصلًا كحاوية Linux يستطيع. أما المتصفح أو بيئة التطوير أو سطر الأوامر على مكتبك فلا يستطيع، لأن إعداد الوكيل فيه يتوقع خدمة بروتوكول، لا مساحة أسماء.
| Client | Arrangement | What must already exist |
|---|---|---|
| Another Compose service | Shares the vpn namespace via network_mode: service:vpn | A working tunnel and correct start order |
| Host browser, IDE or CLI | Proxy listener inside the namespace, published to host loopback | A configured SOCKS or HTTP proxy and its access rules |
| Router, VM or phone | The container as a layer-3 gateway | Forwarding, NAT and a return route |
هذه الثلاثة لا تختلط. حقل وكيل HTTP الموجّه إلى عنوان الجسر في الحاوية لا يصل إلى شيء ما لم يكن وكيل يستمع هناك، ومستمع الوكيل لا يوجّه حركة الموجّه. قرّر أولًا، ثم اتبع القسم المطابق.
شارك مساحة أسماء النفق مع حاوية شقيقة
هذا أرخص خيار لأن لا بروتوكول وكيل فيه. ينضم التطبيق إلى مساحة أسماء خدمة vpn ويرث واجهاتها ومساراتها وفضاء منافذها:
services:
vpn:
image: your-vpn-client
cap_add:
- NET_ADMIN
healthcheck:
test: ["CMD-SHELL", "ip link show tun0 >/dev/null"]
interval: 10s
retries: 5
app:
image: your-app
network_mode: service:vpn
depends_on:
vpn:
condition: service_healthyثلاثة قيود تتبع كون مساحة الأسماء مملوكة لخدمة أخرى. لا يحصل التطبيق على مفتاح networks خاص به، لأنه لا يملك مساحة أسماء يلتصق بها. نشر المنافذ يقع على vpn، فهناك تعيش مساحة الأسماء. وإعادة إنشاء vpn تدمّر مساحة الأسماء التي انضم إليها التطبيق، لذا يتركك docker compose up -d --force-recreate vpn لإعادة إنشاء app أيضًا؛ وإعادة تشغيل عادية ليست كافية دائمًا. ويغطي مرجع خدمة Compose هذه الترتيبة وقيود نشر المنافذ فيها.
لا تفترض أن المُحلِّل يأتي معك. /etc/resolv.conf ملف يخص كل حاوية وليس جزءًا من مساحة الأسماء الشبكية، لذا يظل التطبيق قادرًا على سؤال مُحلِّل المضيف بينما تغادر حزمه عبر النفق. اقرأه من داخل الحاوية واضبط dns: على التطبيق عندما يجب أن تُحلّ الأسماء الخاصة عبر النفق.
docker compose exec app ip route
docker compose exec app cat /etc/resolv.conf
docker compose exec app curl -s https://ifconfig.meإن احتوت قائمة المسارات على واجهة النفق وكان العنوان عنوان المزوّد، فالتطبيق داخل النفق. وإن غابت الواجهة، فقد بدأ app قبل أن يحصل vpn على نفق، فأعد إنشاء app بعد أن يبلّغ vpn عن صحته.
ضع مستمع وكيل حقيقيًا في مساحة الأسماء لأجهزة المضيف
متصفح سطح المكتب لا يستطيع الانضمام إلى مساحة أسماء، لذا يجب كشف النفق كخدمة. شغّل وكيل SOCKS أو HTTP فعليًا داخل مساحة أسماء vpn وانشر منفذه فقط:
services:
vpn:
ports:
- "127.0.0.1:1080:1080"
proxy:
image: your-proxy
network_mode: service:vpn
command: ["--socks", "0.0.0.0:1080"]عنوان جهة المضيف هو النصف المهم في ذلك التعيين. 127.0.0.1:1080 على اليسار يبقي المستمع بعيدًا عن شبكتك المحلية؛ أما "1080:1080" المجرّد فينشر وكيلًا مفتوحًا لكل جهاز يستطيع الوصول إلى حاسوبك.
لنصف جهة الحاوية فخّه الخاص. المنافذ المنشورة تُسلَّم إلى عنوان الحاوية على الجسر، لا إلى loopback، لذا فإن وكيلًا بدأ بـ --socks 127.0.0.1:1080 داخل مساحة الأسماء يقبل اتصالات الأشقاء ويرفض كل ما يصل عبر المنفذ المنشور. اربطه بـ 0.0.0.0 واعتمد على ربط loopback في جهة المضيف للعزل. والقاعدة نفسها تنطبق على عنوان الاستماع في ملف إعداد الوكيل.
ثم قرّر أين تُحلّ الأسماء. في curl يحلّ --socks5 اسم المضيف محليًا ويرسل --socks5-hostname الاسم إلى الوكيل؛ والمتصفحات تعرض المفتاح نفسه تحت تسمية مختلفة. الحل المحلي يسرّب الاستعلام حتى عندما تُحمّل الصفحة عبر النفق.
لا تعامل الحاوية كبوابة إلا مع مسار عودة
الموجّهات والأجهزة الافتراضية والهواتف لن تستخدم حقل وكيل HTTP، وبعض التطبيقات تتجاهل متغيرات الوكيل تمامًا. توجيه حركتها يعني الطبقة 3: الحاوية تمرّر الحزم وتترجم العناوين على واجهة النفق.
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADEأمران يسهل تفويتهما. يحتاج العميل إلى مسار يوجّه بوابته الافتراضية إلى هذا المضيف، ويجب أن تجد الردود طريقها عائدًا، إما عبر مسار ثابت على الموجّه الأعلى أو عبر NAT على الواجهة المواجهة للعملاء. بدون أحدهما، يحمل النفق الطلب خارجًا ويختفي الرد.
وله أيضًا أوسع نطاق أثر. المضيف الذي يعمل بوابة للشبكة المحلية كلها يرسل كل عميل سيّئ الإعداد عبر حساب VPN لديك، وسقوط النفق إما يبتلع الشبكة أو يرتد إلى مزوّد الخدمة بحسب مفتاح الإيقاف.
تحقّق من مسار الخروج، ثم تراجع بالترتيب
افحص بهذا الترتيب وتوقّف عند أول فشل: عنوان الخروج داخل مساحة الأسماء، ثم عنوان الخروج من المضيف عبر الوكيل، ثم حلّ الأسماء لمضيف خاص.
docker compose exec vpn curl -s https://ifconfig.me
curl -s -x socks5h://127.0.0.1:1080 https://ifconfig.me
curl -s -x socks5h://127.0.0.1:1080 http://internal.example/healthيجب أن يطابق العنوانان الأولان المزوّد، وأن يُحلّ الثالث عبر الوكيل بدلًا من الفشل بخطأ اسم. تطابق السطر الأول وفشل السطر الثاني يعني أن المستمع أو المنفذ المنشور خطأ، لا النفق.
التراجع هو عكس الإعداد. أزل متغيرات بيئة الوكيل من ملف تعريف الصدفة ومن أي وحدة systemd، ثم أنزل الحزمة كي تختفي مساحة الأسماء المشتركة مع مالكها. احذف المنفذ المنشور قبل حذف خدمة الوكيل، وإلا احتفظ المضيف بمستمع loopback لا يقرأه شيء.
قبل أن تمضي، أكّد أربعة أمور: أن العميل الذي يهمّك يصل فعلًا إلى النفق، وأن مالك مساحة الأسماء وحده ينشر المنافذ، وأن الأسماء الخاصة تُحلّ حيث أردت، وأن لا شيء يستمع على عنوان مضيف غير loopback. كل واحد من هذه يفشل بصمت في الإنتاج، وكل واحد منها فحص من سطر واحد أعلاه.