OpenVPN في Docker: عقد التشغيل الذي يحتاجه عميل بسيط
صورة مراجَعة، وملف تعريف واحد محمي، ووصول إلى TUN، وصلاحية NET_ADMIN. ما الذي تحصل عليه الحاوية، وما الذي يجب ألا تحصل عليه، وكيف تثبت أن النفق يعمل.
ضع عميل VPN داخل حاوية فتحصل على نطاق أسماء واحد فيه نفق، لا على مضيف يوجّه كل حركة المرور عبر VPN. هذان مشروعان منفصلان، والتعامل معهما كمشروع واحد هو السبيل إلى أن تصبح الحاويات مميّزة بامتياز على نحو غير مقصود. يغطي هذا الدليل الأول فقط: صورة واحدة مراجَعة، وملف تعريف واحد محمي، وعملية عميل محصورة في نطاق الأسماء الشبكي الخاص بها.
ابدأ من أثر وافق عليه أحدهم مسبقًا. تفترض الخطوات أدناه صورة تحتوي على الملف التنفيذي openvpn وملف تعريف عميل كاملًا جرى حل شهادته ومادة مفتاحه داخل نقطة التحميل. وهي لا تبني برمجيات، ولا تصدر بيانات اعتماد، ولا تغطي أوقات التشغيل بلا صلاحيات جذر ولا حاويات Windows الأصلية، وكل منها يغيّر الجواب.
ما الذي تحصل عليه الحاوية وما لا تحصل عليه
احتفظ بملف التعريف وكل ما يشير إليه في دليل محلي بأذونات مقيّدة، محمّلًا للقراءة فقط. يجب أن تُحل المسارات النسبية داخل ملف التعريف ضمن نقطة التحميل تلك، وهذه تفصيلة يفوّتها الناس حين تكون الشهادة في دليل أعلى من ملف التعريف فلا تراها الحاوية أبدًا.
services:
vpn:
image: ${VPN_CLIENT_IMAGE:?set the reviewed image reference}
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ./vpn:/vpn:ro
working_dir: /vpn
entrypoint: ["openvpn"]
command: ["--config", "/vpn/client.ovpn"]
يمنح ذلك الملف ثلاثة أشياء ولا يطلب شيئًا آخر. ينبغي تثبيت مرجع الصورة بالبصمة إذا كانت سياسة النشر لديك تقول ذلك، وينبغي أن يكون ملف التعريف هو المراجَع لا نسخة عدّلها أحدهم في مكانها على المضيف.
القراءة فقط هنا تعني أن الحاوية لا تستطيع تعديل ملفات مضيفك؛ ولا تعني أنها لا تستطيع قراءة المفتاح الخاص الذي حمّلته. فهذا هو المقصود كله من التحميل، وهو أيضًا سبب بقاء أذونات الملفات وملكيتها وطريقة وصول السر إلى المضيف أمورًا مهمة. لا تمرر كلمة مرور على سطر الأوامر ولا تخبزها في طبقة صورة، لأن كليهما يبقى في مواضع لا يمكنك تنظيفها لاحقًا.
الصلاحيات ونقطة الدخول والفخ بينهما
يلزم إعدادان وهما يقومان بعملين غير مترابطين. يسمح NET_ADMIN بالتلاعب بالواجهات والمسارات داخل نطاق أسماء الحاوية. ويسمح إدخال الجهاز /dev/net/tun للعملية بفتح جهاز النفق أصلًا. ومنح أحدهما دون الآخر يُنتج فشلًا واضحًا، وهو أمر مريح: غياب جهاز TUN يفشل وقت الفتح، بينما تفشل الصلاحية الغائبة عندما يحاول العميل تهيئة الواجهة.
لا يبرر أي منهما privileged: true. فالحاوية المميزة بالامتياز تحصل على كل الصلاحيات إضافة إلى الوصول إلى الأجهزة، وهو منح أكبر بكثير مما تحتاجه هذه الحملة. و مرجع امتيازات وقت التشغيل والصلاحيات يوضح مدى وصول هذا المنح. وإذا طلبه chart أو منشور مدونة، فتحقق أولًا مما إذا كان الشرط في الحقيقة مسار جهاز مفقودًا أو صلاحية مفقودة.
تجاوز entrypoint هو الجزء الذي يلسع. استدعاء openvpn مباشرة صحيح لصورة عادية وخاطئ لأي صورة منتج يهيّئ سكربت تشغيلها قواعد جدار الحماية أو ملفات المحلل أو بيانات الاعتماد قبل تنفيذ البرنامج الخفي. وتجاوز ذلك السكربت يتخطى التهيئة ويتركك تصحح نفقًا قام في نطاق أسماء نصف مهيأ.
شغّل العميل في المقدمة ودعه يكون العملية الرئيسية للحاوية. العملية التي تتخفى كبرنامج خفي تنتهي فورًا، فتعيد الحاوية التشغيل في حلقة، وتتوزع السجلات التي تحتاجها على عمليات إعادة تشغيل تبدو كلها متطابقة.
تحقق من داخل نطاق الأسماء، لا من المضيف
أطلق المكدس، وأكّد في السجل أن العميل وصل إلى حالة متصلة، ثم افحص نطاق الأسماء الذي يحتوي النفق فعليًا.
docker compose up -d
docker compose logs --tail 40 vpn
docker compose exec vpn ip -br addr
docker compose exec vpn ip route
ينبغي أن تُظهر قائمة العناوين واجهة نفق بعنوان من التجمع، وأن يُظهر جدول المسارات الوجهات المدفوعة مشيرة إليها. إذا غابت الواجهة، فالعميل لم يصل قط إلى نقطة إنشائها. وإذا وُجدت لكن لا مسار يستخدمها، فموضع البحث هو الدفع أو خيارات المسارات.
ثم اختبر الوجهة التي تهمك، من نطاق الأسماء المفترض أن يستخدم النفق، باستخدام التطبيق لا ببينغ فقط. حل الأسماء فشل منفصل يستحق الاختبار صراحة: عنوان DNS المدفوع لا يحدّث محلل الحاوية من تلقاء نفسه، والعميل المستقل الذي يتجاهل الدفع سيحل الأسماء عبر ما هيأته الصورة.
الحالة غير المتصلة جزء من العقد
يحتاج سلوك إعادة الاتصال إلى فحص خاص به، لأن سياسة إعادة التشغيل تثبت أن العملية تعيد التشغيل ولا تقول شيئًا عما تفعله الحركة في الأثناء. أوقف النفق، وجرّب الوجهة المقصودة، وراقب هل يُرفض الاتصال أم يعلّق أم ينجح بهدوء عبر المسار العادي للحاوية. النتيجة الأخيرة هي المهمة، وهي غير مرئية في سجل سليم.
إذا كان يجب ألا تستخدم الحملة المسار العادي أبدًا، فصمّم ذلك صراحة بقواعد جدار الحماية وإشارة جهوزية داخل نطاق الأسماء. مفتاح الإيقاف سياسة تكتبها وتختبرها، لا خاصية ترثها من تشغيل عميل في حاوية.
أخيرًا، تذكّر الحد: هذه المسارات تعيش في نطاق أسماء الحاوية ولا تحل محل جدول التوجيه في المضيف. فقواعد إعادة التوجيه والترجمة وجدار الحماية لدى Docker تبقى جزءًا من المسار الخارجي، والمنفذ المنشور ليس واجهة VPN.