هل يمكن لسكربت خطاف OpenVPN استخدام النفق الذي بناه للتو؟
يشغّل OpenVPN الخطافات داخل بدء تشغيله، لذا ينتظر السكربت الذي يحتاج النفق نفقًا ينتظر هو نفسه. افصل العمل واختر الخطاف حسب الحدث.
يعتمد وصول سكربت الخطاف إلى الجانب الآخر من النفق على ثلاثة شروط: الحدث الذي يُربط به، وما إذا كان OpenVPN لا يزال داخل تسلسل بدء التشغيل عند تشغيل السكربت، وما إذا كان العمل قد فُصل عن ذلك التسلسل. تفشل معظم السكربتات في الشرط الثالث. إضافة سكون تعالج مشكلة دورة حياة كمشكلة توقيت، وهي تنجح بما يكفي فقط لتضليل.
حدّد الحدث أولًا، ثم آلية تأجيل العمل، ثم المكان الذي يعمل فيه العمل المؤجل. هذه القرارات الثلاثة مستقلة، وخلطها هو ما يحوّل خطافًا من سطرين إلى أمسية.
لماذا ينتظر الخطاف نفقًا ينتظر هو نفسه
OpenVPN يستدعي خطافاته تزامنيًا، داخل العملية نفسها التي تحرّك الحزم. يعمل up بمجرد وجود جهاز tun أو tap لكن قبل إضافة أي مسار، ويعمل route-up بعد المسارات؛ ولا يضمن أي من اللحظتين أن يجيب الطرف الآخر. وبينما لا يزال سكربت من أي من الخطافين قيد التشغيل، لا يمرّر الخادم، لذا ينتظر أمر يتوقع مرور حركة عبر النفق نفقًا ينتظر الأمر. هذا الانتظار الدائري هو سبب نجاح تشغيل يدوي وفشل الخطاف بمهلة.
الترقيع الواضح، أي السكون بضع ثوانٍ، ينجح أحيانًا لأن الطرف البعيد ظهر من تلقاء نفسه، لا لأن السكون أصلح شيئًا. المساعد الذي يستقصي الجاهزية جواب مختلف: يعود الخطاف، يبدأ التمرير، ويحصل المساعد على ما يحتاجه بمجرد أن يكون موجودًا فعلًا.
اختر الخطاف حسب الحدث، لا حسب الملاءمة
اربط up بعمل يحتاج الجهاز فقط، و route-up بعمل يحتاج المسارات، واحفظ client-connect و client-disconnect لـ المهام لكل عميل على جانب الخادم. يجب أن يتحمل التنظيف عند قطع الاتصال تنفيذه مرتين، لأن إعادة الاتصال عبر واجهة دائمة لا تتبع التسلسل نفسه لإعادة تشغيل كاملة. على الخادم، يعمل client-connect عندما يوثّق العميل نفسه لا عندما يبدأ الخادم، ويستحق ذلك التأكد قبل نقل المنطق إلى هناك. إذا كان العمل يخص دورة حياة الخدمة لا اتصالًا واحدًا، فإن وحدة systemd مكان أفضل من أي خطاف.
تُنفّذ أوامر الخطاف مباشرةً من الخادم، دون صدفة بينهما، لذا يصل ifconfig_remote في سطر up كنص حرفي. غلّف الأمر في صدفة صريحة، أو أبقِ المنطق في سكربت يقرأ البيئة. ipchange توجيه واحد يضيف وسائطه الخاصة: يسلّم العنوان والمنفذ إلى الأمر، فيرى سكربت كُتب للا شيء وسيطين غير متوقعين.
ثلاث طرق لفصل العمل
الخيار الأول هو تشغيل المساعد في الخلفية من داخل الخطاف: ابدأه بـ setsid أو systemd-run --scope، وأغلق الواصفات الموروثة، واخرج بالصفر فورًا. الثاني أن يكتب الخطاف ملف حالة صغيرًا ويدع مؤقت systemd أو وحدة مسار يتصرف عليه، مما يبقي الخطاف بسيطًا ويضع سياسة إعادة المحاولة حيث توجد السجلات. الثالث هو إسقاط الخطاف كليًا ونقل العمل إلى وحدة مرتبة بعد خدمة VPN، وهو الجواب الصحيح عندما لا يكون العمل لكل اتصال أصلًا. تشترك الثلاثة في قاعدة واحدة: على الخطاف أن يعود قبل أن يحتاج النفق.
أيًا كان اختيارك، اجعل المساعد متساوي الأثر وقابلًا للمراقبة. لا ينبغي لاستدعاءين متداخلين تنفيذ الفعل غير القابل للرجوع مرتين، وعلى المساعد الذي يفشل أن يترك شيئًا يستطيع الإنسان قراءته.
افصل أعطال الحماية من أعطال دورة الحياة
عندما يعمل سكربت من موجه الجذر ويفشل كخدمة، فالسبب عادةً حماية (sandbox) لا الشبكة. ProtectHome المقيّد، أو ProtectSystem للقراءة فقط، أو دليل عمل غير موجود قد تجعل السكربت أو بياناته غير قابلة للوصول، وعلى مضيف SELinux يحتاج الملف أيضًا إلى سياق يسمح بالتنفيذ. اقرأ الوحدة بـ systemctl cat openvpn-server@server والسجل بـ journalctl -u openvpn-server@server -n 80، ثم ابحث عن خطأ صلاحية أو سياق بدلًا من مهلة اتصال. تعطيل الحماية يجب أن يكون الخطوة الأخيرة، لا الأولى.
يبدو عطل دورة الحياة مختلفًا: يبدأ الخطاف، يعمل المساعد، والمقبس ببساطة ليس هناك بعد. اكتب سطرًا بطابع زمني من الخطاف نفسه، واقطع الاتصال وأعد وصله، واقرأ الأحداث بالترتيب. عندما يحتاج المساعد النفق فعلًا، دعه يستقصي الجاهزية بدلًا من السكون لفترة ثابتة.
ثلاثة حقول تجعل التقرير التالي مفيدًا: أي خطاف عمل، وأي حساب استخدم، وأين توقف. بدونها تبدأ كل تحقيق لاحق من الصفر.