الأسرار (Secrets)
الأسرار (Secrets)
كل بيئة إنتاجية تحتاج في نهاية المطاف إلى بيانات اعتماد: كلمة مرور قاعدة بيانات، أو مفتاح API، أو شهادة TLS، أو سر عميل OAuth. أسوأ خطأ يمكن أن يرتكبه المهندس هو تضمين تلك البيانات داخل صورة الحاوية أو رفعها إلى مستودع Git. تمثّل Secrets في Kubernetes الإجابة الأصلية للمنصة على هذه المشكلة — غير أنها تأتي مع دقائق مهمة تتعلق بالترميز والتشفير وضبط الصلاحيات يجب على كل محترف فهمها قبل الانتقال إلى الإنتاج.
ما هو السر — وما ليس كذلك
السر هو كائن Kubernetes مُخزَّن في etcd — مخزن القيم-المفاتيح الخاص بالمجموعة — جنبًا إلى جنب مع كل مورد آخر. بشكل افتراضي، قيم السر مُرمَّزة بـ base64 وليست مُشفَّرة. base64 ليس حاجزًا أمنيًا؛ إنه مجرد ترميز نقل لضمان التخزين الآمن للبيانات الثنائية. أي شخص لديه صلاحية kubectl get secret على المساحة يمكنه فك الترميز بأمر واحد. هذا أهم ما يجب استيعابه عن Kubernetes Secrets: كائن الـ API بحد ذاته يوفر العزل، لا السرية.
السرية الحقيقية تتطلب التشفير في حالة السكون (تشفير بيانات etcd على القرص عبر مورد EncryptionConfiguration) مقرونًا بـ RBAC صارم يحصر صلاحيات get/list/watch على كائنات Secret للأحمال العمل والأشخاص المخوّلين فحسب.
أنواع Secrets
يتعرّف Kubernetes على عدة أنواع مدمجة، يتم التحقق من صحة كل منها والتعامل معه بشكل مختلف:
- Opaque — النوع الافتراضي. حقيبة مفاتيح وقيم اعتباطية. استخدمه لكلمات المرور ورموز API وسلاسل الاتصال وأي بيانات اعتماد مخصصة.
- kubernetes.io/dockerconfigjson — بيانات اعتماد سحب الصور من السجلات الخاصة. يُشار إليها عبر
imagePullSecretsفي مواصفة Pod. يُلزم الـ API بوجود بنية.dockerconfigjsonصحيحة. - kubernetes.io/tls — زوج شهادة TLS ومفتاحها. يجب تسمية المفاتيح
tls.crtوtls.key. تستخدمها وحدات تحكم Ingress وcert-manager. - kubernetes.io/service-account-token — تُنشئها لوحة التحكم تلقائيًا لكل ServiceAccount. تُثبَّت تلقائيًا داخل Pods ما لم يُضبط
automountServiceAccountToken: false. - kubernetes.io/basic-auth وkubernetes.io/ssh-auth — أنواع متخصصة بأسماء مفاتيح مُلزَمة لبيانات المصادقة الأساسية ومفاتيح SSH الخاصة.
إنشاء Secrets
الطريقة الأأمن لإنشاء Secret هي إنشاؤه بشكل إلزامي من ملف، حتى لا يظهر النص الصريح في سجل الأوامر. تُقبل ملفات YAML التي تحتوي على قيم base64 فقط عند تخزينها في أداة لإدارة الأسرار (Sealed Secrets أو Vault أو مشغّل external secrets)، وليس أبدًا كـ YAML عادي في Git.
--from-literal تُكتب في ملف سجل الـ shell وقد تظهر في سجلات التدقيق أو سجلات CI أو جلسات العمل المشترك. اكتب دائمًا بيانات الاعتماد إلى ملف أولًا (echo -n لتجنب السطر الجديد الذي يكسر جولة base64)، ثم أشِر إلى الملف، ثم احذفه. في سكريبتات الإنتاج الطارئة، اضبط HISTFILE=/dev/null للجلسة.تثبيت Secrets داخل Pods
هناك طريقتان لإظهار Secret داخل حاوية: كـ متغيرات بيئة أو كـ ملفات مُثبَّتة عبر volume. التثبيت عبر volume هو الأفضل دائمًا في الإنتاج لأن متغيرات البيئة تظهر في قوائم العمليات (/proc/<pid>/environ)، وتُسجَّل بواسطة كثير من الأُطر عند الإقلاع، وتُورَث بواسطة العمليات الفرعية. يُثبِّت Kubernetes وحدات تخزين Secret على نظام ملفات tmpfs (في الذاكرة، لا يُكتب أبدًا على القرص)، وهو تحسين أمني ملموس مقارنةً بمتغيرات البيئة.
التشفير في حالة السكون
بشكل افتراضي، يُخزِّن etcd بيانات Secret بنص واضح (يُفكّ ترميز base64 قبل الحفظ). لتفعيل التشفير في حالة السكون، طبِّق EncryptionConfiguration على API server. تدعم خدمات Kubernetes المُدارة (EKS وGKE وAKS) هذا عبر إعداد مجموعة بسطر واحد (مثلًا --secrets-encryption-key-arn في EKS مع مفتاح AWS KMS).
في المجموعات ذاتية الإدارة، يُشار إلى مانيفست EncryptionConfiguration عبر خيار API server رقم --encryption-provider-config. يستخدم مزوّدا aescbc أو aesgcm مفتاحًا مُدارًا محليًا؛ أما مزوّد kms (الموصى به بشدة) فيُفوِّض الأمر إلى KMS خارجي حتى لا يقطن المفتاح على عقدة المجموعة أبدًا.
kubectl get secret db-creds -o json | etcdctl get ... وتأكد أن قيمة etcd الخام تبدأ بـ k8s:enc:aescbc (أو المزوّد الذي اخترته). كثير من الفرق تُفعِّل الإعداد لكنها تنسى إعادة تشفير Secrets الموجودة عبر kubectl get secrets --all-namespaces -o json | kubectl replace -f -.Secrets على مستوى الإنتاج: Sealed Secrets وExternal Secrets Operator
لا يمكن الالتزام بأمان بمانيفستات Kubernetes Secret العادية في Git لأنها تحتوي على قيم base64 يمكن لأي مطوّر فك ترميزها. يحلّ مشروعان مفتوحا المصدر هذه المشكلة على نطاق واسع:
- Sealed Secrets (Bitnami) — مُتحكِّم يحتفظ بمفتاح RSA خاص على المجموعة. تُشفِّر مانيفست Secret إلى CRD باسم
SealedSecretباستخدام المفتاح العام المقابل عبر أداة سطر الأوامرkubeseal. ملف YAML المختوم آمن للالتزام به في Git — فقط مُتحكِّم المجموعة يمكنه فك تشفيره. يُوفِّق المُتحكِّم كائناتSealedSecretإلى Kubernetes Secrets حقيقية داخل المجموعة. يناسب هذا نموذج GitOps تمامًا: كل شيء في Git، لا شيء بنص صريح. - External Secrets Operator (ESO) — مُتحكِّم يُزامن الأسرار من مخزن خارجي (AWS Secrets Manager وHashiCorp Vault وGCP Secret Manager وAzure Key Vault وغيرها) إلى Kubernetes Secrets. تُعرِّف CRD باسم
ExternalSecretيُحدِّد مسار المخزن الخارجي واسم Secret المستهدف. يتولى ESO التدوير — عند تحديث السر الخارجي، يُعيد ESO المزامنة خلالrefreshIntervalالمُهيَّأ. هذا هو النمط المفضَّل على نطاق كبير لأنه يحتفظ بمصدر الحقيقة الوحيد خارج المجموعة، ويتيح المشاركة عبر المجموعات، ويتكامل مع سياسات إدارة الأسرار الحالية.
RBAC للـ Secrets — مبدأ أقل الصلاحيات
لأن Secrets هي كائنات API من الدرجة الأولى، يتحكم RBAC القياسي في Kubernetes بمن يمكنه قراءتها. الممارسة الأمنية الأكثر تأثيرًا هي ضمان عدم امتلاك أي تطبيق أو إنسان صلاحية list أو watch على Secrets في المساحات التي لا يمتلكها. صلاحية list على Secrets تُعيد جميع القيم — وهي معادلة لاختراق بيانات. حدِّد نطاق كل Role لـ ServiceAccount بـ get على أسماء Secret المحددة التي يحتاجها التطبيق فحسب، لا بحرف بدل.
دقِّق في وصول Secret بانتظام: kubectl auth can-i get secrets --as=system:serviceaccount:payments:payments-app -n payments وقارن مع سجل تدقيق API server بحثًا عن استدعاءات list/watch غير متوقعة على مورد secrets.