تقييد مضيف Subversion بمستخدمي VPN عبر Apache 2.4
يختار ServerName المضيف الافتراضي ولا يقيّد من يتصل. اطلب نطاق VPN ومستخدمًا حقيقيًا للمستودع، ثم أثبت كلا الرفضين.
توجيه مضيف افتراضي لـ Subversion إلى عنوان VPN يغيّر الموقع الذي يجيب على الطلب، لا من يُسمح له بإرساله. يحدث فحص الوصول لاحقًا، وفق القواعد في القسم الذي طابق العنوان، ولا يدخل اسم المضيف في ذلك القرار أبدًا. إذا كان ServerName تغييرك الوحيد، فالمستودع متاح تمامًا كما كان بالأمس. يجب الإجابة عن سؤالين منفصلين في الإعداد: أي مقبس قبِل الاتصال، وأي هوية حملها الطلب.
ما يقرره ServerName فعلاً
تقارن الاستضافة الافتراضية القائمة على الاسم ترويسة Host بمدخلات ServerName و ServerAlias، وتختار مضيفًا واحدًا، وتتركه يعالج الطلب. الاتصال نفسه قُبل سابقًا عبر مقبس Listen يخص الخادم لا المضيف. لهذا لا يقدم اسم يبدو خاصًا أي حماية: أي عميل يستطيع الوصول إلى المقبس يستطيع إرسال تلك الترويسة. الاسم مفتاح توجيه، ومفاتيح التوجيه يختارها المتصل.
يمكنك مشاهدة التعيين دون تخمين. يطبع apachectl -S كل مضيف ومنفذه وأسماءه، ويحدد أيها الافتراضي للعنوان.
RequireAll هو الحاوية التي تجعل الفحصين إلزاميين
يغلّف Apache 2.4 توجيهات Require في قسم واحد داخل RequireAny ضمنية، لذا يعني توجيهان مجردان أن أيًّا منهما يكفي. أضف الحاوية فينقلب المعنى:
<Location /svn/repo>
AuthType Basic
AuthName "Subversion"
AuthBasicProvider file
AuthUserFile /etc/apache2/svn.htpasswd
<RequireAll>
Require ip 10.8.0.0/24
Require valid-user
</RequireAll>
</Location>يحتاج RequireAll إلى الشرطين معًا: عنوان مصدر داخل نطاق النفق ومستخدم يقبله المستودع. هذا ما ينبغي أن يعنيه وضع VPN فقط لعنوان Subversion. توثّق مرجع mod_authz_core قواعد الحاوية. أبقِ الكتلة داخل Location القائم للمستودع واترك توجيهات DAV العاملة في مكانها، لأن هذا المقطع لا يستبدل AuthType ولا ملف كلمات المرور ولا التخويل على مستوى المسار.
يأتي فحص المصدر من mod_authz_host، لذا تأكد من تحميله قبل تصحيح أي شيء آخر: apachectl -M | grep authz_host.
العنوان الذي يقيّمه Apache هو عنوان المقبس
يقارن Require ip بالعنوان الذي سجله Apache للاتصال، وهو ليس دائمًا العنوان الذي يظنه العميل.
عندما يوجّه VPN عناوينه دون ترجمة، يرى Apache عنوان النفق ويطابق 10.8.0.0/24. وعندما تترجم بوابة العناوين، يرى Apache البوابة ولا تطابق القاعدة شيئًا بصمت؛ ويحدث الأمر نفسه خلف موازن حمل ينهي TLS قبل المضيف. يوجد mod_remoteip لهذه الحالة. يستبدل RemoteIPHeader X-Forwarded-For مع RemoteIPTrustedProxy الخاص بالموازن العنوان المسجل، وعندها فقط يجد النطاق ما يقارنه به.
الوثوق بترويسة تمرير عشوائية أسوأ من عدم وجود قاعدة إطلاقًا. يستطيع أي عميل إرسال X-Forwarded-For يحوي 10.8.0.9 وتلبية فحص نطاق لم يُثبَّت قط على قفزة موثوقة.
اطبّق الطبقات على المستمع وسياسة المستودع
الربط تحكم منفصل وغالبًا أبسط. يحدد Listen 10.8.0.1:443 أي المقابس توجد، ولا يمكن لطلب اختيار مضيف على مقبس لم يُفتح قط. الثمن هو ترتيب بدء التشغيل: إذا كانت واجهة النفق معطلة عند بدء Apache، يفشل الربط ولا تبدأ الخدمة. اقرنه بترتيب الوحدات، أو اربطه بعنوان موجود بمعزل عن النفق.
تبقى أذونات المسار في AuthzSVNAccessFile. الحساب الصالح للموقع ليس نفس منح الكتابة إلى مسار مشروع، وقاعدة الشبكة لا تغيّر ذلك.
انتبه إلى دمج الأقسام عندما تقع الكتلة في ملف مُضمَّن. يدمج Apache حاويات التخويل عبر الأقسام المتداخلة، وقد يستبدل القسم الأكثر تحديدًا المطلب بدل أن يضيف إليه، لذا قد لا تصل قاعدة على /svn إلى /svn/repo/trunk بمجرد أن يعرّف قسم أضيق قاعدته.
أثبت الرفضين قبل أن تعتبر الأمر منتهيًا
اختبر أربعة طلبات، لا طلبًا واحدًا. داخل VPN بحساب صالح ينبغي أن تحصل على قائمة المستودع. وداخل VPN بكلمة مرور خاطئة ينبغي أن تحصل على 401 لا على قائمة. ومن خارج VPN ببيانات اعتماد صالحة ينبغي أن تحصل على 403، والطلب العام نفسه على اسم مضيف بديل يجب أن يفشل بالطريقة ذاتها.
أمر واحد لكل حالة يكفي: curl -sI -u user:pass https://svn.example.com/svn/repo/، ثم اقرأ سطر الحالة. أعد الحالة العاملة عبر العميل الحقيقي أيضًا، لأن svn info يمارس أساليب DAV التي ستستخدمها عمليات الإيداع والتحديث.
إذا خالف أي من النتائج الأربع السياسة التي أردتها، فالإعداد غير مكتمل، وإعادة تسمية المضيف لن تغيّر ذلك.