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

الشروط الثلاثة التي تحدد القرار

مطالبة الطرفية هي المسار الوحيد الذي يحتاج إلى إنسان، لذلك هي الوحيد الذي يفشل بصمت داخل وحدة systemd أو حاوية. إذا بدأ شخص العميل وكتب كلمة المرور، فلا وجود لملف ولا يصل أي شيء حساس إلى القرص. أما إذا بدأ العميل عند الإقلاع، تتحول المطالبة نفسها إلى تعليق، ويظهر انتهاء المهلة عادةً كعطل اتصال عام بدلًا من خطأ في بيانات الاعتماد.

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

  • إنسان أمام الطرفية، بلا أتمتة: مطالبة تفاعلية.
  • بدء دون إشراف، يجوز تخزين السر: ملف بيانات اعتماد بوضع 0600.
  • بدء دون إشراف، لا يجوز تخزين السر: استبدال العملية أو واصف ملف يكتبه وكيل أسرار.
  • عملية أخرى تملك العميل: واجهة الإدارة.

عندما يناسبك جوابان، فاختر ما يُبقي السر خارج argv وخارج سجل shell. فكلمة المرور التي تُمرَّر كوسيط برنامج يمكن لكل مستخدم محلي قراءتها عبر قائمة العمليات، وهذا الانكشاف يصعب التراجع عنه.

ما يفعله كل مسار إدخال فعليًا

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

المساران الآخران هما تدفق ومقبس. لا يحتاج OpenVPN إلا إلى واصف ملف قابل للقراءة، لذا يقبل استبدال عملية، أو مدخلًا /dev/fd مدعومًا بأنبوب، أو اسم ملف هو في الحقيقة FIFO. وواجهة الإدارة مختلفة في طبيعتها: فالعميل يسأل عملية مشرفة عن القيم بدلًا من قراءتها بنفسه. وهذا الفرق مهم عند إعادة الاتصال: فملف بيانات الاعتماد، وFIFO يستطيع مساعد أسرار الكتابة فيه مجددًا، وواجهة الإدارة، كلها تستطيع إعادة التزويد دون إنسان؛ أما استبدال العملية فلا يستطيع، لأن الواصف يُستهلك عند أول قراءة.

يجب أن يتكون الملف من سطرين بالضبط

يغطي نمطا فشل معظم ملفات الاعتماد المعطوبة. محرر Windows يترك نهايات CRLF، فيصبح حرف الإرجاع جزءًا من كلمة المرور، ويرفض الخادم قيمة تبدو صحيحة على الشاشة. والسطر الثاني الفارغ - سطر خالٍ حيث يجب أن تكون كلمة المرور - يترك العميل بكلمة مرور فارغة، يرفضها الخادم رغم أن القيمة على الشاشة تبدو صحيحة. أما سطر جديد زائد بعد كلمة المرور فغير ضار. اكتب الملف من shell لا من محرر، وافحص البايتات:

install -m 600 /dev/null /etc/openvpn/client.creds
printf '%s\n%s\n' "$VPN_USER" "$VPN_PASS" > /etc/openvpn/client.creds
od -c /etc/openvpn/client.creds | head -3

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

اختبر الملف قبل أن تلوم الخادم. فتشغيل openvpn --config client.ovpn --auth-user-pass /etc/openvpn/client.creds --verb 4 يطبع ما إذا فُتح الملف، ولاحقًا ما إذا قُبلت بيانات الاعتماد، وهذا يخبرك بأي طبقة تغيّرها بعد ذلك.

التدفقات وstdin والأخطاء التي تنتجها

توجيه كلمة المرور عبر أنبوب هو أول ما يجربه معظم الناس، وهو المسار الأكثر مفاجآت. يعمل استبدال العملية لأن العميل يحتاج فقط إلى واصف قابل للقراءة: openvpn --config client.ovpn --verb 3 --auth-user-pass <(printf '%s\n%s\n' "$VPN_USER" "$VPN_PASS"). وتمرير /dev/stdin لا يعمل إلا حين يبقى stdin مفتوحًا وقابلًا للقراءة في اللحظة التي يسأل فيها العميل، وهذا غير صحيح في أنبوب استهلكه بالفعل.

لـ ERROR: could not read Auth username from stdin سببان مختلفان. في بعض عملاء 2.3.x جاءت الرسالة من عيب في معالجة stdin أصلحته إصدارات 2.3 اللاحقة، لذا لا يتحسن عميل بهذا القدم إلا بالترقية. وفي عميل حديث تعني الصياغة نفسها أن العميل طلب إدخالًا من الطرفية ولم يكن stdin طرفية - وهو بالضبط ما يحدث في وحدة systemd أو مهمة cron أو حاوية بدأت دون -t.

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

واجهة الإدارة عندما تملك عملية أخرى المطالبة

المتحكّم الذي يشرف بالفعل على النفق يستطيع أيضًا الإجابة عن أسئلته. ابدأ العميل بـ --management 127.0.0.1 7505، واقرأ أسطر الحالة، وراقب >PASSWORD:Need 'Auth' username/password. يجيب المتحكّم بـ username "Auth" alice متبوعًا بـ password "Auth" ... على أسطر أوامر منفصلة. والرمز 'Auth' ينسخ المعرّف من الطلب ويجب أن يطابقه، وإلا تُهمل الإجابة وينتظر العميل.

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

أعطال تبدو كمشاكل في بيانات الاعتماد

مصافحة TLS لا تكتمل، أو خطأ VERIFY ERROR: depth=0, error=certificate signature failure، هو مشكلة ثقة تحدث قبل قراءة أي اسم مستخدم. وعميل proto tcp موجّه إلى مستمع UDP يفشل في الموضع نفسه: فالحزم لا تبلغ مرحلة المصادقة قط، لذا تبدو كل كلمة مرور خاطئة بالارتباط. أبقِ التحقق من الشهادة مفعّلًا قبل إرسال بيانات الاعتماد؛ فالمطالبة المألوفة لا تقول شيئًا عن هوية من يتلقى كلمة المرور. افحص سلسلة الشهادات والاسم الشائع والنقل قبل أن تلمس بيانات الاعتماد.

أخطاء shell في النص الذي يجمع الملف الشخصي هي الفخ الآخر. فمسافة مفقودة داخل [ ! -f "$file" ] تنتج command not found، فيتوقف النص مبكرًا، ولا يُعاد بناء الملف الشخصي أبدًا، ويبدأ العميل بالملف السابق. ذلك الخطأ ينتمي إلى shell لا إلى المصادقة، وإعادة كتابة كلمة المرور لن تمحوه.

ليست كل مطالبة في هذا التدفق هي بيانات الاعتماد هذه. فعبارة مرور المفتاح الخاص، وتسجيل دخول وكيل HTTP، وكلمة مرور الخدمة، ثلاثة أسرار مختلفة تُطلب في أطوار مختلفة، وعبارة مرور المفتاح يعالجها عادةً العميل أو وكيل بدلًا من auth-user-pass. وإذا ظهرت المطالبة قبل أن تكون جلسة TLS قائمة، فأنت تجيب عن السؤال الخطأ ولن تساعد أي إعادة تعيين للحساب. وبعد استبعاد المطالبة الخطأ، تبدأ الفحوص المتبقية من الطبقة الأقدم:

  • اقرأ أول خطأ بروتوكول، لا آخر سطر بيانات اعتماد: TLS، ثم المصادقة، ثم push.
  • تأكد أن الملف من سطرين، ولا يحتوي على أحرف إرجاع، ويُشار إليه بمسار مطلق.
  • تأكد أن الملف الشخصي وسطر الأوامر لا يختلفان بشأن التوجيه.
  • افصل "قُبلت بيانات الاعتماد" عن "تدفق حركة المرور" - فالتوجيه وDNS طبقة لاحقة.

حين يبلغ العميل طور push، توقف عن النظر في بيانات الاعتماد وابدأ بقراءة المسارات وخوادم DNS التي أرسلها الخادم. تلك هي المشكلة التالية، ولا علاقة لها بكلمة المرور التي أصلحتها للتو.

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