تصليب Kubernetes: أمان الحاويات (Pod Security)
تصليب Kubernetes: أمان الحاويات (Pod Security)
كل حمل عمل يعمل في Kubernetes يُنفَّذ داخل Pod. افتراضياً، يرث ذلك الـPod نواة المضيف، ويمكنه العمل كـroot، وتركيب مسارات المضيف بشكل تعسفي — حاوية واحدة مخترقة يمكنها التحول إلى سيطرة كاملة على الكلستر. أمان الـPod هو ممارسة تجريد هذه الافتراضات بحيث يهبط هروب الحاوية في صندوق رمل محدود بدلاً من shell بصلاحيات root مع وصول إلى كل سر في الكلستر.
يتناول هذا الدرس النموذج ثلاثي الطبقات الذي تعتمده فرق الإنتاج في الشركات الكبرى: معايير أمان الـPod (PSS) على مستوى الـnamespace، وsecurityContext على مستوى الـPod والحاوية، والإعدادات الافتراضية الآمنة التي تُطبّق السلوك الآمن حتى حين ينسى المطورون.
معايير أمان الـPod: السياسة على مستوى الـNamespace
حلّت معايير أمان الـPod (PSS) محل PodSecurityPolicy المُهمَل في Kubernetes 1.25. تُعرّف PSS ثلاثة مستويات سياسة مُسمّاة يُطبّقها متحكم قبول مُدمج — بدون CRDs، ولا webhooks، ولا تبعيات خارجية.
- Privileged: بدون قيود تماماً. محجوز لـnamespaces النظام مثل
kube-systemوإضافات CNI التي تحتاج فعلاً إلى وصول المضيف. - Baseline: يمنع أخطر الانتهاكات (الحاويات المميزة،
hostPID،hostIPC، صلاحيات خطيرة مثلNET_ADMIN) بينما يسمح لمعظم أحمال العمل القديمة بالعمل دون تعديل. - Restricted: مقيّد بشدة — يتطلب UID غير root، ويُسقط كل الصلاحيات، ويمنع تصعيد الامتيازات، ويُطبّق ملف seccomp. المعيار الذهبي لأحمال عمل التطبيقات.
يمكن ضبط كل مستوى في ثلاثة أوضاع: enforce (رفض الـPods المنتهِكة عند القبول)، وaudit (السماح لكنّ إطلاق حدث تدقيق للانتهاك)، وwarn (السماح لكنّ إظهار تحذير لعميل API). النمط المُعتمد في بيئة الإنتاج هو تطبيق warn وaudit بوضع Restricted في كل مكان أولاً، ومراقبة الانتهاكات لفترة، وإصلاح أحمال العمل، ثم تحويل الـnamespaces إلى enforce.
latest. استخدام enforce-version=latest يعني أن ترقية Kubernetes يمكنها رفض Pods كانت متوافقة سابقاً بمجرد إضافة فحوصات أكثر صرامة لملف Restricted. في الإنتاج، حدّد إصداراً محدداً مثل v1.30 وقم بالترقية بتخطيط خلال نوافذ الصيانة.
securityContext: تصليب الـPods والحاويات بشكل فردي
تضبط PSS سقف السياسة. securityContext هو المكان الذي تُطبّق فيه السياسة لكل حمل عمل. يُتيح Kubernetes مستويين: spec.securityContext (على مستوى الـPod، يُطبّق على كل الحاويات) وspec.containers[].securityContext (على مستوى الحاوية، يُلغي مستوى الـPod لتلك الحاوية).
فيما يلي deployment جاهز للإنتاج يستوفي معيار Restricted مع نية موثّقة صريحة لكل حقل:
الإعدادات الافتراضية الآمنة في وقت التشغيل
الاعتماد فقط على تذكّر المطورين لضبط securityContext لا يتوسع. تستخدم منصات الإنتاج آليتين للإنفاذ لجعل المسار الآمن هو المسار الافتراضي.
1. Admission Webhooks مع OPA/Kyverno
يمكن لـwebhook قبول مُحوِّل (mutating) حقن securityContext معقول في كل Pod لا يحدده. يمكن لـwebhook تحقق (validating) بعدها رفض الـPods التي لا تزال تنتهك السياسة بعد التحويل. Kyverno هو الخيار الأبسط للفرق الأصيلة في Kubernetes؛ OPA/Gatekeeper يُتيح مرونة أكبر للسياسات المعقدة المشتركة عبر السحابات.
2. seccomp RuntimeDefault كإعداد افتراضي عالمي
منذ Kubernetes 1.27، يمكنك تفعيل --feature-gates=SeccompDefault=true على الـkubelet وضبط --seccomp-default لتطبيق ملف RuntimeDefault على جميع الـPods التي لا تحدده. هذا هو أقرب ما يصل إليه Kubernetes من إعداد افتراضي آمن عالمي على مستوى العقدة.
RuntimeDefault على Unconfined. يحجب ملف seccomp الخاص بـRuntimeDefault (المُقدَّم من containerd أو cri-o) حوالي 300 syscall خطير تشمل ptrace وmount وunshare — وهي الأدوات الأساسية في سلاسل استغلال الهروب من الحاويات. تكلفة الأداء غير قابلة للقياس في كل أحمال العمل تقريباً. لا يوجد سبب وجيه لتشغيل Unconfined في الإنتاج إلا لأدوات الأمان المخصصة.
أنماط الإخفاق في الإنتاج وتكلفتها
فهم سبب وجود كل حقل يتطلب رؤية ما يحدث بدونه:
- غياب
runAsNonRoot: true: صورة مبنية بدون تعليمةUSERتعمل كـUID 0 داخل الحاوية. إذا كانت هناك ثغرة في kernel، يتطابق UID 0 داخلياً مع root خارجياً. CVE-2019-5736 (كتابة runc) تطلّبت root للاستغلال. - غياب
allowPrivilegeEscalation: false: الثنائيات ذات بتsetuid(مثلsudoوnewgrpوpkexec) يمكنها التصعيد إلى root حتى حين تبدأ الحاوية كمستخدم غير root. هكذا عملت CVE-2021-4034 (PwnKit). - غياب
readOnlyRootFilesystem: true: المهاجم الذي يحقق تنفيذ كود يمكنه كتابة أدوات استمرار، أو سرقة البيانات إلى القرص، أو زرع reverse shell. نظام ملفات للقراءة فقط يحصر نطاق الضرر في العمليات الذاكرية. - غياب إسقاط الصلاحيات: المجموعة الافتراضية من صلاحيات Linux الممنوحة للحاوية تتضمن
NET_RAW(صياغة حزم خام، ARP spoofing) وSYS_CHROOTوMKNOD. أسقط الكل وأضف فقط ما ثبتت حاجته. - غياب
automountServiceAccountToken: false: كل Pod يحصل افتراضياً على token لحساب الخدمة. في حاوية مخترقة، يوفر هذا الـtoken وصولاً لـAPI server وهو المحور الرئيسي لهجمات التحرك الجانبي داخل الكلستر.
runAsUser: 1000 في manifest التوزيع لكنها تنسى إضافة تعليمة USER 1000 إلى Dockerfile. سيُشغّل Kubernetes الحاوية بالـUID الذي تحدده — لكن إذا بُني البرنامج الثنائي في الصورة متوقعاً UID 0، قد يتعطل بسبب أذونات الملفات. ابنِ دائماً مع USER nonroot في Dockerfile وأطبقه على مستوى الـPod أيضاً.
التحقق من التصليب
بعد تطبيق إعدادات securityContext، تحقق منها دون تخمين:
ادمج هذه الفحوصات مع ماسح سياسات مثل Trivy (trivy k8s --report summary cluster) أو kube-bench للتوافق مع معيار CIS. تشغيل هذه الفحوصات في pipeline CI الخاص بك — على الـmanifests المُصيَّرة قبل قبول الكلستر — يكتشف الانحدارات قبل وصولها إلى الإنتاج.