لماذا لا يستطيع خطاف OpenVPN العثور على الملف المجاور لسكربته
المسارات النسبية في الخطاف تُحل مقابل العملية لا مقابل السكربت. ثبّتها بـ __file__، ثم افحص نطاق الأسماء والهوية قبل الأذونات.
الخطافات التي تعمل دون خطأ وتُبلّغ مع ذلك عن ملف مفقود تنظر عادةً في المكان الخطأ، لا أنها تفشل في مهمتها. المسارات النسبية في السكربت تُحل مقابل دليل العمل الخاص بالعملية، والخدمة تبدأ بدليل عمل لم تختره. الملف المجاور للسكربت والملف الذي تفتحه العملية ليسا الشيء نفسه إلا عندما يثبّتهما شيء ما. هذه الفجوة تفسّر نصيبًا كبيرًا من إخفاقات الخطافات التي يُساء تفسيرها كمشكلات أذونات.
افحص المسار قبل الأذونات، لأن خطأ الأذونات على الملف الخطأ يبقى خطأً على الملف الخطأ.
المسار النسبي يُحل مقابل العملية، لا مقابل الملف
عندما يستدعي سكربت open("policy.json")، تحل النواة الاسم مقابل دليل العمل الحالي للعملية. لا يشارك شيء من موقع السكربت نفسه في ذلك البحث. OpenVPN يبدأ الخطافات من دليل يختاره، ويمكن لخيار cd أن يغيّره، لذا يتصرف سكربت اختُبر يدويًا من مجلده الخاص بشكل مختلف حين يشغّله الخادم الخفي. ينجح الاختبار اليدوي، ويفشل تشغيل الخدمة، ولا يذكر سطر السجل سوى الاسم المفقود.
يكشف Python عن موقع السكربت باسم __file__، وهو ما يجعل التثبيت ممكنًا. Path(__file__).resolve().parent / "policy.json" يبني مسارًا من الملف بدلًا من العملية. هذا السطر الواحد يزيل الاعتماد على دليل العمل دون تغيير أي شيء في الصلاحيات.
ثبّت المسار عمدًا، ثم افحص ما تفعله resolve
resolve() يتبع الروابط الرمزية، لذا فإن تثبيتًا يربط الخطاف في مكانه يثبّت عند هدف الرابط، لا عند الدليل الذي يحمل الرابط. هذا عادةً ما تريده لنشر مُصدَّر بإصدارات، وهو خطأ في تخطيط يتوقع البيانات بجانب الرابط الرمزي. حدّد أيّ المعنيين يقصده النشر، واكتب الشيفرة لتطابقه بدلًا من اكتشاف الفرق في الإنتاج. Path(__file__).parent بدون resolve() يحتفظ بدليل الرابط نفسه.
إذا كانت البيانات تخص النسخة لا البرنامج، فخُذ مسارًا مطلقًا من الإعدادات. كتابة الموقع مباشرةً تربط الخطاف بتخطيط مضيف واحد.
اعبر أربع حدود بالترتيب
ابدأ بدليل العمل: قارن WorkingDirectory الخاص بالوحدة، وخيار cd في OpenVPN، وأي غلاف يغيّر الدليل قبل exec. ثم أكّد الموقع المحلول، لأن ملفًا مشابه الاسم في نشر آخر قد يجيب البحث ويخفي الخطأ. يأتي بعد ذلك منظور نظام الملفات: قد يجعل chroot أو نطاق تحميل الخدمة مسارًا مطلقًا على المضيف غائبًا ببساطة. وأخيرًا هوية الوصول، وهي مسألة منفصلة عن الرؤية.
ProtectHome و PrivateTmp يخفيان مواقع مختلفة، ولا يظهر أي منهما في سرد دليل مأخوذ من المضيف. المسار الموجود لـ root في صدَفة قد يكون غير مرئي داخل نطاق الوحدة، ولهذا يثبت القليل جدًا اختبار الأمر نفسه خارج الخدمة.
أصلح البحث دون توسيع الخدمة
تثبيت المسار يحل الحد الأول فقط. لا يكشف دليلًا تخفيه الخدمة، ولا يمنح حسابًا غير مميز وصولًا إلى ملف مخصص لـ root. رفع script-security يغيّر أي السكربتات سيشغّلها OpenVPN؛ وليس له أثر في مكان حل الاسم النسبي. تغيير دليل العمل للخدمة كلها أوسع من المشكلة، لأن كل مسار نسبي آخر لشهادة أو سجل أو أداة مساعدة ينتقل معه.
فضّل الإصلاح الضيق: مسار حتمي في السكربت، وقيمة إعداد صريحة لبيانات النسخة، وملكية ملفات تطابق الحساب الذي يعمل به الخطاف فعلًا.
سجّل أدلة تسمّي الحد
تقرير إخفاق مفيد يحتوي ثلاثة أشياء: المسار الدقيق الذي حاوله الخطاف، والبيئة التي أنتجته، والحد الذي رفض. اطبع المسار المحلول والاستثناء دون إفراغ محتويات الملف. هذا الثلاثي يفصل التثبيت الخاطئ عن التحميل المخفي وعن رفض الأذونات، وهي إصلاحات ثلاثة مختلفة.
الزعم بأن root يستطيع قراءة الملف من طرفية يجيب أحد تلك الأسئلة ولا يجيب أيًا من الأخرى. اختبر من سياق الخدمة نفسها بدلًا من ذلك.