اختيار عشوائي في OpenVPN ضمن systemd: لماذا يفشل ExecStart
systemd ليس مفسّر أوامر، لذا تموت نسخة عشوائية داخل ExecStart. انقل الاختيار إلى ExecStartPre، أو اترك OpenVPN يختار الخادم بنفسه.
أربعون ملفًا بصيغة .ovpn لبلد واحد تصل من مزوّد، والخطة تبدو بديهية: انسخ واحدًا عشوائيًا إلى مكانه، ثم ابدأ النفق. لكن كتابتها على شكل أنبوب أوامر داخل ExecStart تجعل تلك الخطة تموت عند الإقلاع برمز حالة لا يفسّر شيئًا. ملف الوحدة ليس سكربتًا، والإصلاح إما أن تجلب مفسّر أوامر صراحةً أو تتوقف عن اختيار الملفات تمامًا.
العَرَض: وحدة تبدأ ثم تموت فورًا
تُظهر الحالة أن الخدمة فشلت في جزء من الثانية، ويذكر سطر السجل أمرًا لم يكن أي مفسّر أوامر ليتلقاه. لا يظهر شيء عن النفق، لأن النفق لم يبدأ أصلًا.
قراءة سطر الأمر الدقيق من السجل هي أسرع طريقة لتأكيد ذلك. يعيد systemd عرض ما نفّذه، وستُظهر السلسلة محارف الأنبوب كوسائط حرفية لا كمعاملات.
لماذا لا يستطيع ExecStart تشغيل أنبوب أوامر
يقسّم systemd سطر الأمر بنفسه، ويطبّق اقتباسه واستبدال المتغيرات الخاصين به، ثم ينفّذ النتيجة مباشرة. الأنابيب وإعادة التوجيه وتوسيع الأنماط في مفسّر الأوامر واستبدال الأوامر ليست ببساطة جزءًا من تلك القواعد. هذا اختيار تصميمي لا سهو: الوحدة تصريحية بشأن ما يجب تشغيله، ومنطق مفسّر الأوامر ينتمي إلى سكربت تستدعيه الوحدة. الإصلاحان ينبعان من ذلك، ولا يتطلب أي منهما منح الوحدة مزيدًا من الامتيازات.
وهو يفسّر أيضًا رمز الخروج المضلّل. تُعامل الوسيطة الأولى كأنها الملف التنفيذي، لذا يفشل السطر المشوّه قبل أن يعمل أي شيء من منطقك.
نفّذ الاختيار في ExecStartPre
الإصلاح المباشر هو خطوة تحضيرية تستدعي مفسّر أوامر صراحةً، فتبقي الاختيار والبدء في الوحدة نفسها حتى لا يترك فشل أحدهما الآخر يعمل وحده.
[Service]
Type=notify
ExecStartPre=/bin/bash -c "ls /etc/openvpn/profiles/au* | shuf -n 1 | xargs -r -I{} cp -f {} /etc/openvpn/%i.conf"
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/%i.conf
ثلاثة تفاصيل تحدد ما إذا كان هذا يصمد أمام جهاز حقيقي. التسمية هي الفخ الأول: يستبدل systemd %i أثناء تحليل الوحدة، قبل أن يعمل bash بوقت طويل، لذا يجب أن يقف المحدِّد خارج علامات الاقتباس المفردة. يمنع xargs -r تشغيل cp دون وسيطة عندما لا يطابق النمط شيئًا. وخطوة تحضيرية فاشلة توقف الوحدة، وهذه هي النتيجة الصحيحة: البدء بإعدادات الأمس أسوأ من عدم البدء. الاختيار العشوائي الذي يختار الملف نفسه دائمًا ليس عادةً حظًا سيئًا. تحقق مما إذا كان الملف المنسوخ يُستبدل بتحديث حزمة، أو مما إذا كان النمط يطابق مدخلًا قديمًا واحدًا فقط.
أو اترك OpenVPN يختار نقطة النهاية
OpenVPN يستطيع أصلًا أن يحمل عدة نقاط نهاية في ملف تعريف واحد ويختار بينها عشوائيًا، وهذا يزيل مفسّر الأوامر والنسخ وإذن الكتابة من مسار الإقلاع بالكامل.
client
remote-random
<connection>
remote au1.example.net 1194 udp
</connection>
<connection>
remote au2.example.net 1194 udp
</connection>
هذا الشكل يبقي اختيار الخادم داخل الملف الذي كتبه المزوّد، لذا لا تكسر الوحدةَ إعادةُ تسمية الملفات. كما يجعل قائمة المرشحين قابلة للتدقيق، وهو ما لا يوفره نمط على دليل أبدًا. الخلط ما يزال يستحق مكانه عندما تختلف ملفات التعريف في أكثر من نقطة نهاية: شهادات مختلفة، وتوجيهات توجيه مختلفة، وسياسة تشفير مختلفة. في هذه الحالة يكون الاختيار بين إعدادات كاملة، وخطوة مفسّر أوامر هي الطريقة الصادقة للتعبير عنه.
اقرأ الوحدة الفعلية والملف الذي استخدمته
أكّد أي وحدة تعمل فعلًا قبل تتبع أخطاء محتواها. خدمة meta المجمّعة والقالب هما بنية تحتية؛ الوحدات المقابلة لملفاتك هي الوحدات الفعلية المسماة على أسمائها.
systemctl list-units 'openvpn*'
systemctl cat [email protected]
journalctl -u openvpn@au -n 30 --no-pager
الوحدة الفعلية التي لا يتذكر أحد تمكينها تعود عادةً إلى ملف إعدادات شارد في دليل الحزمة. عطّل تلك الوحدة المسماة بدلًا من القالب، وإلا أعادها ترقية الحزمة التالية. بعد التغيير، ابدأ الوحدة واقرأ سطر الأمر الذي يذكره السجل، ثم أكّد مسار الإعدادات الذي فتحه فعلًا. ذلك الفحص الواحد يفصل اختيارًا عشوائيًا ناجحًا عن خدمة تعيد استخدام ملف من الشهر الماضي بهدوء.