المدونة

هل يجب أن يحصل كل مستأجر على جهاز افتراضي خاص به؟

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

ملخص

الاستضافة متعددة المستأجرين تفرض عليك اختيار مدى قدرة المستأجرين على الوصول إلى بعضهم البعض. تستخدم الحاويات مساحات أسماء لينكس ومجموعات التحكم (cgroups) لعزل العمليات والموارد، لكنها تشارك نواة المضيف. تضيف الأجهزة الافتراضية حدودًا على مستوى العتاد، على حساب السرعة والعبء التشغيلي. النهج الهجين—الحاويات داخل الأجهزة الافتراضية—يمكن أن يمنحك كليهما، لكنه يضاعف السطح الذي يجب عليك ترقيعه. يرشدك هذا المقال خلال قرار قائم على المخاطر، ومقارنة جنبًا إلى جنب، وخطوات تقوية Docker المهمة حتى داخل جهاز افتراضي. في النهاية، ستعرف أي نموذج عزل يناسب مستأجريك وما يجب تكوينه قبل الإطلاق.

تطبيقك متعدد المستأجرين شبه جاهز. لديك ملف Docker Compose يشغّل مجموعة لكل عميل، وهو سريع. ثم يسأل صديق يدير شركة استضافة: 'هل تمنح كل مستأجر جهازًا افتراضيًا خاصًا به؟' تتجمد. لم تخطط لهذا السؤال. يمنحك هذا المقال طريقة للإجابة عليه اليوم، دون فريق أمني. أنت تفعل ذلك وحدك، لذا يجب أن يكون القرار بسيطًا بما يكفي للدفاع عنه في الثانية صباحًا.

توقف عن محاولة إيجاد النموذج 'الأفضل'. ابدأ بكتابة ما يحدث إذا سيطرت شفرة أحد المستأجرين على مضيفك. حدد نطاق الانفجار قبل اختيار أي أداة. هذا التمرين سيخبرك أكثر من أي معيار أداء.

النواة هي رفيق السكن الذي لا يمكنك طرده

الحاويات فعّالة لأنها تشارك نواة المضيف. هذه المشاركة هي الحيلة كلها، والخطر كله. تمنح مساحات أسماء لينكس كل حاوية رؤيتها الخاصة للعمليات والشبكة ونظام الملفات. تتيح لك مجموعات التحكم (cgroups) تحديد سقف لوحدة المعالجة المركزية والذاكرة وإدخال/إخراج القرص بحيث لا يمكن لمستأجر واحد تجويع الآخرين. لكن لا شيء منهما ينشئ جدارًا من العتاد.

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

لنقل أنك تستضيف أداة B2B صغيرة بحاوية واحدة لكل عميل. يثبّت عميل مكوّنًا إضافيًا مشبوهًا يحتوي على ثغرة تنفيذ برمجي عن بُعد. مع إعدادات Docker الافتراضية، تعمل هذه العملية بصلاحيات الجذر (root) داخل الحاوية. الجذر في الحاوية لا يزال UID 0، والنواة لا تميز هذا UID عن جذر المضيف ما لم تقم بتعيين المستخدمين صراحة. يمكن للمهاجم محاولة الخروج، والنواة المشتركة هي هدفه.

لا يحتاج الفشل إلى أن يكون دراميًا. تسريب ذاكرة من مستأجر واحد يمكن أن يدفع المضيف إلى استخدام التبديل (swap)، مما يبطئ كل مستأجر آخر. بدون حدود cgroup، حلقة واحدة سيئة السلوك هي هجوم توافر. معها، تكون عملية محظورة وتنبيهًا.

هل يعني هذا أن الحاويات غير آمنة؟ لا. يعني أنك يجب أن تتعامل مع النواة كمنطقة ثقة مشتركة. قبل أن تختار، اكتب بيان مخاطر من فقرة واحدة: 'إذا تم اختراق حاوية أحد المستأجرين، يمكن للمهاجم الوصول إلى: [قائمة]. ستكون التكلفة التجارية: [مبلغ أو تأثير].' إذا أخافتك تلك الفقرة، فأنت لست مصابًا بجنون العظمة. أنت صادق.

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

ثلاث طرق لتقسيمها (اختر واحدة قبل النشر)

هناك في الواقع ثلاث بنى لعزل متعدد المستأجرين. كل 'أفضل ممارسة' هي مزيج من هذه.

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

حاويات لكل مستأجر. هذا هو الافتراضي لمعظم مؤسسي SaaS. يحصل كل مستأجر على حاويته الخاصة أو مجموعة Compose صغيرة. التجهيز فوري، الصور صغيرة، CI/CD مباشر. حدود الموارد تمنع الجيران المزعجين من التهام الخادم. المقايضة هي النواة المشتركة. إذا استطعت إبقاء أعباء العمل غير مميزة وترقية المضيف بانتظام، فهذه غالبًا الخطوة الأولى الصحيحة.

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

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

جهاز افتراضي لكل مستأجر. امنح كل مستأجر جهازًا افتراضيًا كاملًا. يضيف المشرف (hypervisor) حدودًا على مستوى العتاد، وهو بالضبط ما يجب أن يعبره استغلال النواة للوصول إلى المضيف. هذا مهم للبيئات المنظمة أو عندما يكون المستأجرون غير موثوقين. التكلفة هي الكثافة والوقت. أنت الآن تدير أسطولًا من أنظمة التشغيل، وليس مجرد حاويات. كل جهاز افتراضي يحتاج إلى تحديثات وعوامل أمان ومراقبة. بالنسبة لمؤسس منفرد، هذا عمل حقيقي.

الأنماط التي تعمل في هذا المستوى: استخدم البنية التحتية كرمز (infrastructure-as-code) لإنشاء جهاز افتراضي من نفس الصورة الأساسية، ودمج التحديثات في صور جديدة بدلاً من ترقية الأنظمة الحية، وإنهاء أعباء العمل التي لا تتعرف عليها. أبقِ منفذ إدارة الجهاز الافتراضي مغلقًا عن الإنترنت.

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

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

أي واحد يجب أن تختار؟ الجدول هو قائمتك المختصرة. الأقسام التالية تجعل القرار ملموسًا.

إذا اخترت الحاويات، قم بهذه الأشياء الستة أو لا تهتم

الحاويات لكل مستأجر جيدة إذا تعاملت مع كل حاوية كمهاجم محتمل. يبدأ ذلك بالتهيئة، وليس بالتفكير التمني.

0. حدد الموارد قبل أن تثق بأي شخص. مجموعات التحكم (cgroups) هي آلية عدالة ودفاع عن التوافر. اضبط --memory و --cpus لكل حاوية. المستأجر الذي يسرّب الذاكرة يجب أن يصطدم بحدوده هو، وليس حدود خادمك. هذا ليس حدًا أمنيًا، لكن الجار المزعج هو هجوم بدون سطر واحد من التعليمات البرمجية. بداية عملية: --memory 512m --cpus 0.5. لعملية عامل، ابدأ بأقل ثم زد.

1. شغّل كمستخدم غير جذر. لا تدع عملية الحاوية تستخدم UID 0 أبدًا ما لم تكن بحاجة مطلقة لذلك. عيّن مستخدمًا في Dockerfile ومرر --user كحارس إضافي. الاستغلال الذي يعمل كمستخدم غير مميز لديه مسارات أقل بكثير إلى النواة. في Dockerfile، أنشئ مستخدمًا: RUN useradd -u 10001 app و USER app. لا تتخطى هذا لتوفير الوقت.

2. أسقط كل صلاحية لا تحتاجها. تقسم صلاحيات لينكس (capabilities) قوة الجذر إلى أجزاء صغيرة. معظم تطبيقات الويب لا تحتاج أيًا منها تقريبًا. ابدأ بـ --cap-drop=ALL وأضف فقط ما تعرف أنك تحتاجه. الحاوية بدون CAP_SYS_ADMIN أصعب بكثير في استخدامها لحيل مساحات الأسماء. إذا حاول تطبيقك ربط منفذ مميز، شغّله على منفذ عالٍ وضع بروكسي أمامه بدلاً من منح NET_BIND_SERVICE.

3. اجعل نظام الملفات للقراءة فقط. يجب ألا يكتب تطبيقك في طبقة الحاوية الخاصة به. قم بتركيب tmpfs للحالة. المهاجم الذي لا يستطيع الكتابة على القرص يصعب عليه جدًا زرع الاستمرارية. تطبيق PHP مخترق يحاول كتابة webshell سيفشل عندما يكون نظام الملفات الجذر للقراءة فقط. يمكنك تركيب وحدة تخزين مسماة لدليل قابل للكتابة يحتاجه تطبيقك حقًا.

4. طبّق seccomp و AppArmor أو SELinux. هذه ترسل استدعاءات النظام الخطيرة إلى سلة المهملات. يوفر Docker ملف تعريف seccomp افتراضيًا؛ استخدمه. أضف ملف تعريف AppArmor لطبقة أخرى. لست بحاجة إلى إتقان كل استدعاء نظام. تحتاج إلى رفض ما لا يتطلبه عامل ويب عادي أبدًا. لا تشغّل أبدًا مع --privileged. هذه العلامة تعطل تقريبًا كل دفاع قمت بإعداده للتو.

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

بداية عملية:

docker run --user 10001 --cap-drop=ALL --security-opt no-new-privileges --read-only --tmpfs /tmp:rw,size=64M --security-opt seccomp=default.json --memory 512m --cpus 0.5 myimage

ضع نفس العلامات في ملف Compose وطبقها على كل مستأجر. هذا ليس مكتملًا، لكنه افتراضي أقوى بكثير مما يقدمه docker run افتراضيًا.

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

العزل المحسن للحاويات في Docker هو الاستثناء الذي يجب أن تعرفه

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

يمكنك تقريب جزء من هذا باستخدام إعادة تعيين مساحة اسم المستخدم (userns-remap) في خادم Docker. هذا ليس مكتملًا مثل وقت تشغيل آمن، لكنه أفضل من لا شيء. إذا استخدمته، تحقق من أن تعيين UID يعمل قبل أن تثق به.

مغالطة الجهاز الافتراضي: الانتقال إلى الأجهزة الافتراضية ليس تقوية

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

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

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

تذكر أيضًا أن مضيف المشرف (hypervisor) هو هدف حاسم. المشرف المخترق يمكنه رؤية جميع الضيوف. قم بترقية المضيف، وليس فقط الضيوف. الجهاز الافتراضي لا يعفيك من ترقية المضيف؛ إنه يرفع المخاطر عند تفويتها.

ملاحظة حول النهج الهجين: لا تفترض أن الحاويات داخل جهاز افتراضي تمنحك 'طبقتين من الأمان' مجانًا. الجهاز الافتراضي يضيف حدودًا؛ الحاوية لا تزال بحاجة إلى غير الجذر والصلاحيات وseccomp. وإلا فإن الطبقة الأولى تكون فقط بقوة أضعف حاوية.

أربعة أسئلة تحسم الجدل في عشر دقائق

لا تحسّن بشكل تجريدي. اسأل نفسك هذه الأسئلة الأربعة بالترتيب. اكتب الإجابات.

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

2. كم ستكلفني اختراق مستأجر واحد؟ اجمع خسارة العملاء والتعرض القانوني والثقة. إذا كان الرقم أكبر من تكلفة تشغيل الأجهزة الافتراضية، أنفق المال. إذا لا، فالحاويات خيار عقلاني.

3. كم عدد المستأجرين لدي وكم يدفعون؟ مشتركون صغار كثيرون: كثافة الحاويات مهمة. حفنة من الحسابات الكبيرة: امنح كل واحد جهازًا افتراضيًا واجعله يدفع وفقًا لذلك. المستأجرون الذين يدفعون لك أقل من فنجان قهوة لا ينبغي أن يتطلب كل منهم نظام تشغيل لإدارته.

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

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

أطلق أقل ما يمكنك الوثوق به، ثم اكسب مزيدًا من العزل

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

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

قبل الإطلاق، راجع قائمة التحقق العملية لأمان عزل Docker—إنها تحول هذه القرارات إلى قائمة يمكنك التحقق منها قبل عرض صفحة على عميل.

Sources (5)