فشل RAND_bytes في OpenVPN: خطأ في المكتبة أو عقدة مفقودة
قد يعني فشل RAND_bytes مصدرًا للإنتروبيا مرفوضًا أو عقدة يخفيها سجن. اقرأ طابور الأخطاء وعرض العملية قبل إنشاء الأجهزة.
RAND_bytes() failed عبارة عن أداة أولية محلية واحدة، لا عن الحساب أو المفتاح المشترك مسبقًا أو المنفذ البعيد. عطلان مختلفان جدًا ينتجان السطر نفسه ويتركان أدلة مختلفة. أحدهما خلفية تشفيرية ترفض إصدار البايتات؛ والآخر عملية لا تصل إلى مصدر الإنتروبيا الذي بُني رمزها من أجله. يتطلب الفصل بينهما قراءة واحدة لطابور الأخطاء ونظرة واحدة إلى عرض نظام الملفات للعملية المسجونة، وهذا الفحص ينبغي أن يسبق أي شيء يُنشأ تحت /dev.
ما الذي تميّزه قيمة الإرجاع
العقد الموثّق يفصل ثلاث نتائج لا نتيجتين: 1 للنجاح، و-1 عندما لا تدعم طريقة RAND المحمّلة الطلب، و0 للإخفاقات الأخرى، مع ترك التفسير لطابور الأخطاء. يضيف مرجع RAND_bytes نقطة يسهل إغفالها: يولّد المولّد الافتراضي بذرته من نظام التشغيل على المنصات المدعومة، وعندما يفشل هذا التوليد يبقى في حالة خطأ بدلًا من إرجاع بايتات يمكن التنبؤ بها.
واحد فقط من تلك الأرقام يصف البيئة. فـ -1 مسألة بناء أو مزوّد أو ميزة، لأن الرمز طلب شيئًا لا تقدّمه التنفيذات النشطة. أما 0 مع شكوى تتعلق بالتوليد فمسألة بيئة، لأن المولّد لم يستطع الحصول على إنتروبيا إطلاقًا. الرقم وحده لا يقول أيهما أبدًا، ولا اسم الدالة أيضًا.
متى تكون عقدة الجهاز المخفية السبب الحقيقي
الخدمة المقيّدة ترى شجرة مختلفة عن الصدفة التي تكتب فيها. فسجون chroot وصور الحاويات وملفات الوحدات المقسّاة تعرض عادةً /dev يحتوي عقدًا أقل من عقد المضيف. وإذا كانت التنفيذات الفاشلة تفتح /dev/urandom بالمسار، فهذا الفرق كافٍ لكسر بدء التشغيل بينما يبدو المضيف نفسه سليمًا تمامًا. وجود جهاز خارج السجن لا يثبت شيئًا عن الأشياء التي يُسمح للعملية المسجونة بفتحها.
ليست كل واجهة عشوائية تحتاج مسارًا، مع ذلك. فاستدعاء النظام getrandom في لينكس لا يقبل اسم ملف ولا واصف ملف، وتشير صفحة دليله إلى أن هذه الخاصية مفيدة تحديدًا حيث يخفي chroot مسارات الأجهزة.
لذا فإن تفسير العقدة المفقودة فرضية عن فرع واحد من الرمز، وليس قاعدة عن الأرقام العشوائية عمومًا. وهو يصمد عندما تذكر الأدلة مسارًا، ويبقى تخمينًا عندما تذكر الأدلة شيئًا آخر.
اقرأ طابور الأخطاء ثم عرض العملية
أسرع طريقة لحسم المسألة هي قراءة ما سجّلته المكتبة بالفعل، ثم النظر مباشرة إلى العرض داخل السجن. ابدأ من بناء OpenSSL الذي حمّلته الخدمة فعليًا، لأن أكوام المزوّدين تختلف بين الإصدارات الرئيسية وبين التوزيعات. ثم اسأل systemd أي عملية تعمل وأي قيود تُطبق عليها. ولا تستحق قائمة العقد التي تراها تلك العملية العناء إلا بعد وضع الحقيقتين على الطاولة.
openssl version -a
systemctl show openvpn-server@lab -p MainPID -p PrivateDevices -p DevicePolicy
pid=$(systemctl show -p MainPID --value openvpn-server@lab)
sudo ls -l /proc/"$pid"/root/dev/urandom /proc/"$pid"/root/dev/random
نفّذ تلك القراءات على العملية نفسها، لا على الصدفة المصادف أنها مفتوحة. ومقارنة /dev للمضيف بعرض عملية لا علاقة لها هي الطريقة التي يُصلح بها الاعتماد الخاطئ مرتين.
لا تضف الاعتماد إلا بعد أن تستطيع تسميته
عندما تذكر الأدلة مسارًا فعليًا، يكون الإصلاح صغيرًا: اجعل ذلك المسار الواحد متاحًا داخل السجن واترك كل شيء آخر. والربط bind لعقدة واحدة يكفي عادةً، كما أن mknod بأرقام major و minor القياسية للعقدة يعطي النتيجة نفسها في التنصيبات التي لا يكون فيها الربط خيارًا. أما توسيع الصندوق الرملي أو منح الوصول إلى كل جهاز محارف أو إيقاف PrivateDevices فيستبدل إصلاحًا مستهدفًا بارتداد أمني دائم.
برنامج خفي للإنتروبيا ليس بديلًا عن ذلك الإصلاح. فالحزم مثل haveged تغيّر كيفية تغذية بركة النواة؛ ولا يمكنها إنشاء مسار يُسمح لعملية مسجونة بفتحه. وقد يجعل تثبيت واحد الرسم البياني للصحة يبدو أفضل بينما يظل استدعاء الفتح فاشلًا. أبقِ المشكلتين منفصلتين، وإلا نُسب الفشل التالي مرة أخرى إلى الطبقة الخاطئة.
تحقق من المولّد والنفق كلًّا على حدة
إعادة التشغيل هي الاختبار الوحيد الذي يعيد استخدام الإعدادات نفسها وملف الوحدة نفسه والحبس نفسه الذي أنتج الفشل. شغّله، ثم اقرأ أحدث مدخلات السجل بدلًا من عينة من إقلاع سابق.
sudo systemctl restart openvpn-server@lab
sudo journalctl -u openvpn-server@lab --since -5min --no-pager | grep -iE 'rand|entropy|Initialization Sequence'
ابحث في ذلك الخرج عن شيئين منفصلين: اختفاء رسالة مصدر العشوائية، ووصول النفق إلى محطته المعتادة في بدء التشغيل. فمولّد يعمل مجددًا يزيل فشلًا محليًا واحدًا ولا يقول شيئًا عن هوية الخادم أو بيانات الاعتماد أو التوجيه. اختبر ذلك بإعدادات العميل نفسها التي فشلت سابقًا، ليكون الناتج قابلًا للمقارنة وليس أفضل فقط.