خطاف OpenVPN بلغة PHP: trusted_ip مقابل ifconfig_pool_remote_ip
عنوانان بمعنيين مختلفين، يقرأهما عملية لم تُشغّلها أنت. ما هي متغيرات الخطاف، ولماذا لا يثبت $_ENV الفارغ شيئًا.
مصفوفة $_ENV فارغة داخل سكربت خطاف تبدو دليلًا على أن OpenVPN لم يمرّر شيئًا. لكنها عادةً دليل على أمر آخر تمامًا: ضُبط PHP على ألّا يملأ تلك المتغيّرة الشاملة، وكانت القيم جالسة في بيئة العملية طوال الوقت. الاستنتاج الخاطئ الثاني أدقّ، لأن متغيّري العنوان في خطاف الاتصال يعنيان أمرين مختلفين ويُستخدمان بالتبادل. والخطأان كلاهما يكلّفانك بعد ظهر كامل إن بدأت من الخطأ.
عنوانان، وادّعاءان مختلفان
الطرف الخارجي هو ما رصده الخادم عند اتصال العميل، ويتاح باسم trusted_ip لعنونة IPv4، وباسم trusted_ip6 لعنونة IPv6. أما العنوان الافتراضي فهو ما خصّصه المجمّع لهذه الجلسة، ويتاح باسم ifconfig_pool_remote_ip.
هما يجيبان عن سؤالين مختلفين، والسجل عادةً يحتاج أحدهما لا كليهما. الأول هو المكان الذي وصل منه الاتصال، وقد يكون عنوان NAT مشتركًا يستخدمه مئات الناس. والثاني هو المكان الذي تعيش فيه هذه الجلسة داخل النفق، ويمكن إعادة تخصيصه لجلسة أخرى لاحقًا. والخلط بين الاثنين ينتج سجلات تبدو معقولة وهي خاطئة.
القراءة الدنيا تبدو هكذا.
$outer4 = getenv('trusted_ip');
$outer6 = getenv('trusted_ip6');
$inner = getenv('ifconfig_pool_remote_ip');
$outer = $outer4 !== false ? $outer4 : $outer6;لا قيمة من الاثنين تعرّف شخصًا. الواحدة رصدٌ شبكي والأخرى عقد إيجار، وأي سجل يتعامل مع أحدهما كمعرّف جهاز ثابت سيكون خاطئًا في أول مرة يعيد فيها العميل الاتصال من شبكة مختلفة أو يستلم عنوان مجمّع مختلف. اختر الحقل حسب السؤال الذي يجب أن يجيب عنه السجل، ثم اكتب ذلك السؤال بجانب العمود.
تحقّق من كيفية تشغيل PHP، لا من طرفيتك
يعمل الخطاف طفلًا لعملية الـ VPN الخفية ببيئة تبنيها تلك العملية. طرفيتك التفاعلية لها بيئة مختلفة، وتشغيل السكربت يدويًا يختبر بيئة الطرفية لا بيئة الخطاف.
ابدأ بالتأكد من أن الخطاف انطلق فعلًا في المرحلة التي توقّعتها. السكربت المربوط بمرحلة خاطئة، أو الذي لم يعمل قط لأن التوجيه يشير إلى مسار آخر، يعطي العَرَض نفسه تمامًا كمتغيّر ناقص. أضف سطرًا علامةً مقصودًا في أعلى السكربت وتأكد من ظهوره قبل أن تفحص أي شيء آخر. سطر واحد بطابع زمني في السجل يفصل بين «لم يعمل قط» و«عمل دون القيمة».
ثم تأكد من المفسّر ومن ملف الإعداد الذي يستخدمه ذلك الاستدعاء. تعديل إعدادات PHP لخادم ويب لا يغيّر شيئًا في عملية سطر أوامر تبدأها OpenVPN، وقد يحمّل الاثنان ملفات php.ini مختلفة تمامًا.
لماذا لا يثبت $_ENV الفارغ شيئًا
توثّق مرجع getenv قراءة مباشرة لبيئة العملية، وتعيد false عند غياب الاسم. أما المتغيّرة الشاملة $_ENV فتُملأ على حدة، وتوجيه variables_order هو الذي يحدد أي المتغيّرات الشاملة تُملأ.
إن أسقط ذلك التوجيه E، بقي $_ENV فارغًا مهما احتوت البيئة. والكود الذي يفهرسها يبلّغ حينها عن متغيّر ناقص هو في الحقيقة موجود، ويتجه التحقيق نحو OpenVPN بينما الجواب في php.ini. تحقّق من التوجيه الخاص بإعداد سطر الأوامر تحديدًا، لا بخادم الويب.
القراءة عبر getenv تتجنّب هذا النوع كله من الالتباس، والسكربت الغلافي الذي ينظّف البيئة قبل استدعاء المفسّر يعيده في صورة أخرى.
خزّن ارتباطًا، لا تحديثًا لسطر
حدّد أية قيمة يحتاجها السجل قبل كتابته. إن كان الهدف تسجيل مصدر الجلسة، فخزّن الطرف الخارجي وسمّه رصدًا. وإن كان الهدف ربط جلسة بعنوان نفق مدة الاتصال، فخزّن عنوان المجمّع واربطه بالجلسة.
حدّث بحسب الهوية الموثّقة والجلسة الحالية لا بحسب كل سطر يتشارك عنوان نفق، لأن عناوين المجمّع تُعاد استخدامها وقد يطمس حدث انقطاع متأخر بيانات اتصال أحدث. تعامل مع IPv6 صراحةً بدل أن تكتب قيمة IPv4 وحدها بصمت. سطر بعنوان خارجي NULL أفضل من سطر بعنوان خاطئ، لأن الأول ناقص بشكل ظاهر.
اربط الكتابة بمعرّف الجلسة الذي توفره OpenVPN كي يُنشئ إعادة الاتصال سجلًا جديدًا بدل تحرير القديم. استخدم عبارات مُعامَلة وحسابًا لا يملك إلا الصلاحيات التي تحتاجها العملية. الخطاف يعمل بسلطة العملية الخفية، فاستعلام متسرّع مشكلة أوسع من سطر سجل خاطئ.
شخّص دون تفريغ البيئة
سجّل أي الأسماء كانت موجودة، وأي مرحلة عملت، وأي مفسّر استُخدم. لا تطبع البيئة كاملة، لأن عملية الخطاف قد ترث قيمًا غير ذات صلة لا يجب أن تنتهي في ملف سجل.
تحقّق من اتصال واحد من طرفه إلى طرفه: اقرأ الطرف الخارجي، واقرأ العنوان المخصّص، وأكّد الاثنين مقابل رؤية الخادم نفسه لتلك الجلسة. وبعد تأكيد الزوج لعميل واحد، يمكن الوثوق بالارتباط في مواضع أخرى.