سكربت يشغّل عميل VPN من أحد المورّدين يتوقف حتى تنتهي مهلة الانتظار، ويُظهر السجل أن العميل يطلب رمز تحقق لم يكتبه أحد. لقد تم تمرير كلمة المرور، لذا فالاستنتاج الواضح أن كلمة المرور خاطئة. وهي ليست كذلك. إن طلب الرمز محادثة منفصلة على قناة منفصلة، ولم يفتحها سكربتك قط.

اعمل على الفحوص الخمسة أدناه بالترتيب. توقف عند أول فحص يفشل، لأن كل خطوة لاحقة تفترض أن الخطوات السابقة سليمة.

الفحص 1: هل يقرأ العميل المدخل القياسي أصلاً؟

اختبر قبل أن تكتب أي أتمتة: مرّر سرًّا وهميًّا إلى العميل عبر أنبوب وراقب ما يحدث. إذا طبع طلبه ثم انتظر، فهو لا يقرأ الأنبوب.

كثير من العملاء يفتحون جهاز الطرفية مباشرة، أو يرفضون أخذ سر من تدفق غير طرفي، حتى لا يسرّب أنبوب شارد كلمة مرور. OpenVPN هو الاستثناء الموثق، لأن --auth-user-pass يأخذ مسار ملف. عملاء المورّدين الذين يطلبون رمزًا مرة واحدة ليس لديهم عادةً خيار مكافئ، وطلبهم ينتظر طرفية لم يوفّرها سكربتك.

الفحص 2: هل للعملية الابنة طرفية؟

تخصّص pty طرفية للعملية الابنة، فتحوّل الطلبات إلى مخرجات مقروءة يمكنك الرد عليها بالترتيب.

import pexpect

child = pexpect.spawn("openconnect vpn.example.net", encoding="utf-8", timeout=30)
child.expect("GROUP:")
child.sendline("staff")
child.expect("USERNAME:")
child.sendline("alice")
child.expect("PASSWORD:")
child.sendline(PASSWORD)
child.expect("Connected as", timeout=60)

سلاسل الطلب أعلاه توضيحية؛ انسخ النص الدقيق الذي يطبعه عميلك، بما في ذلك حالة الأحرف وعلامات الترقيم. كل توقع يحمل مهلة، والابنة المغلقة ترفع استثناءً بدلاً من إرجاع حالة، لذا غلّف التسلسل واعتبر نهاية الملف فشلاً. لاحظ ما لا يفعله هذا المقطع: الابنة تموت مع السكربت، لأن pty ليست آلية فصل وdisown أمر مدمج في الصدفة لا يصل إلى عملية أنشأها سكربتك.

الفحص 3: هل هناك إدخال غير تفاعلي مدعوم؟

كشط الشاشة ينكسر عند ترقية العميل، لذا ابحث أولاً عن خيار موثّق.

افحص مخرجات المساعدة بحثًا عن خيار رمز أو رمز لمرة واحدة، وافحص ما إذا كانت الخدمة تقبل الرمز الملحق بحقل كلمة المرور. وحيث يقرأ العميل ملف بيانات اعتماد، أعطه الملف بدل الطلب:

openvpn --config office.ovpn --auth-user-pass <(printf "%s\n%s\n" "$USER_NAME" "$PASSWORD")

استبدال العمليات يمنح العميل مسارًا مقروءًا على الأنظمة التي توفر /dev/fd. السر يبقى سرًّا من وجهة نظر العميل، لذا أبعده عن السجلات وتاريخ الصدفة وقوائم العمليات. إذا رفض العميل /dev/fd أو فتح مسار بيانات الاعتماد أكثر من مرة، فاكتب ملفًا حقيقيًا بأذونات مقيدة واحذفه في السكربت نفسه.

الفحص 4: هل سيعيش النفق أطول من السكربت؟

افتراضيًا يموت النفق مع أبيه، وهو عكس ما يحتاجه سكربت الإقلاع.

disown -h يفيد فقط عندما أطلقت العميل كمهمة صدفة، لأنه يعدّل جدول مهام تلك الصدفة ولا يصل إلى عملية أنشأها سكربتك عبر pty. مدير الخدمات هو الموضع الأفضل لكل ما يجب أن يعيد الاتصال دون إشراف، لأنه يعيد تشغيل العميل ويسجل سبب موته. في الحالتين، توقع رمزًا جديدًا بعد كل إعادة اتصال: الرمز المخصص لمرة واحدة يُستهلك عند أول مصادقة ناجحة.

راقب ما يفعله sudo بالتسلسل. طلب كلمة مرور sudo ليس طلب VPN، والرد عليه بكلمة مرور VPN هو أكثر الطرق شيوعًا لفشل ترتيب توقعات صحيح في ما عدا ذلك.

الفحص 5: هل يجب أن يكون رمز شخص ما داخل سكربت؟

هذا هو الفحص الذي يتخطاه الناس. الرمز يخص شخصًا، أما النفق الذي يجب أن يعمل وفق جدول فيخص الآلة.

إذا كان على مهمة أن تعيد الاتصال دون إشراف، فاسأل هل يمكن لشهادة آلة أو حساب خدمة أن يحل محل تسجيل الدخول التفاعلي. أتمتة رمز شخص لمرة واحدة تحوّل عاملًا ثانيًا إلى سر مخزّن، وهذا تغيير في السياسة لا تسهيل في الكتابة. وحيث تدعم الخدمة الاثنين، اختر بيانات الاعتماد التي تطابق طريقة تشغيل المهمة فعلاً. واكتب الإجابة، لأن الشخص التالي سيسأل لماذا يحمل السكربت رمزًا.

كيف يبدو سكربت يعمل

يطبع السكربت الخطوة التي وصل إليها، ولا يطبع السر أبدًا، ويخرج بقيمة غير صفرية عند أي توقع فاشل.

اختبره بتعطيله عن قصد: كلمة مرور خاطئة، مجموعة خاطئة، رمز منتهي. كل فشل يجب أن يتوقف عند خطوة مسماة بدل أن يتعلق. ثم شغّله مرة على الخدمة الحقيقية وافحص النفق من خارج السكربت، لا من داخله.

مقالات ذات صلة