يتطلب GitOps أن تعيش كل قطعة من الحالة المطلوبة في مستودع Git — لكن Git ليس مخزن أسرار. كتابة كلمة مرور قاعدة بيانات، أو مفتاح API، أو مفتاح TLS الخاص في نص عادي داخل مستودع هو واحد من أكثر الأخطاء الأمنية شيوعاً وعواقبها وخيمة في البنية التحتية الحديثة. حتى في مستودع خاص، نطاق الضرر الناتج عن تعرض غير مقصود يشمل كل مطور لديه صلاحية الاستنساخ، كل عداء CI، كل نسخة fork، وكل إدخال في reflog. على نطاق شركات التقنية الكبرى، يلمس آلاف المهندسين نفس المستودعات — commit واحد بسر عادي هو مسؤولية دائمة.
هذا الدرس يغطي الأنماط الثلاثة السائدة المستخدمة في بيئات GitOps الإنتاجية لحل هذه المشكلة: Sealed Secrets، وSOPS، وExternal Secrets Operator. كل منها يحل جزءاً مختلفاً من الفضاء المشكلة. بنهاية الدرس، ستعرف أي أداة تناسب أي سياق وكيف تُنفّذ كل واحدة بشكل صحيح.
التوتر الجوهري: GitOps يقول "ضع كل شيء في Git." الأمن يقول "لا تضع الأسرار أبداً في Git." الحل ليس كسر أحد هذين القاعدتين — بل ضمان أن ما يدخل Git هو تمثيل مشفّر أو مرجع للسر، وليس السر نفسه. الكلاستر، وفقط الكلاستر، يمكنه فك التشفير أو استرداد القيمة الحقيقية.
لماذا Kubernetes Secrets وحدها لا تكفي
كائن Kubernetes Secret مُرمَّز بـ base64، لا مُشفَّر. أي شخص يمكنه قراءة قاعدة بيانات etcd أو تشغيل kubectl get secret my-db-creds -o yaml مع صلاحيات RBAC المناسبة يحصل على القيمة الخام. إذا ألزمت مانيفست Secret في Git، فأنت قد ألزمت السر بنص عادي مع تمويه خفيف. هذه هي نقطة البداية التي وُجدت كل أداة إدارة الأسرار في GitOps لإصلاحها.
الخيار الأول: Sealed Secrets (Bitnami)
Sealed Secrets هو كنترولر Kubernetes وأداة CLI من Bitnami. النموذج هو تشفير غير متماثل مُدمج في الكلاستر نفسه. يحتفظ الكنترولر بمفتاح خاص داخل الكلاستر؛ تستخدم المفتاح العام المقابل (يُجلب عبر CLI) لتشفير Kubernetes Secret عادي إلى مورد مخصص SealedSecret. من الآمن ألزام SealedSecret في Git — فقط الكنترولر الذي يحتفظ بالمفتاح الخاص يمكنه فك تشفيره.
مانيفست SealedSecret الناتج يبدو هكذا (النص المشفّر مختصر):
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
password: AgBy3i4OJSWK+PiTySYZZA24... # مشفّر بتشفير غير متماثل
username: AgCH4T2L8tuxMGfD3...
template:
metadata:
name: db-credentials
namespace: production
type: Opaque
عندما يطبّق ArgoCD أو Flux هذا المانيفست، يعترضه كنترولر Sealed Secrets، يفك تشفير القيم باستخدام مفتاحه الخاص، وينشئ Kubernetes Secret قياسياً في نفس الـ namespace. تثبّت Pods السر بشكل طبيعي — لا تعلم أبداً عن طبقة الختم.
حدّد نطاق الأسرار المختومة بشكل صحيح. بشكل افتراضي، ينتج kubeseal سراً مختوماً محدد النطاق بـ namespace يمكن فك تشفيره فقط في الـ namespace المُعلن. استخدم --scope cluster-wide فقط إذا كنت حقاً تحتاج إلى سر يمكن الوصول إليه في namespace متعددة — يُمثّل نطاق انفجار أوسع إذا تسرب الملف المختوم.
تدوير مفاتيح الكنترولر هو مسؤولية إنتاجية. ينشئ كنترولر Sealed Secrets مفتاح ختم جديداً كل 30 يوماً بشكل افتراضي. تُحتفظ المفاتيح القديمة لفك التشفير لكن الأسرار الجديدة تُختم بأحدث مفتاح. إذا حذفت الكنترولر دون نسخ احتياطي لمفاتيحه (المخزنة كـ Secrets في kube-system)، تفقد بشكل دائم القدرة على فك تشفير SealedSecrets الموجودة. اعمل نسخة احتياطية من مفتاح السر: kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml > sealed-secrets-master-key-backup.yaml — وخزّن هذه النسخة الاحتياطية في قبو آمن، لا في Git.
الخيار الثاني: SOPS (Mozilla) — ملفات مشفّرة في Git
SOPS (Secrets OPerationS) هي أداة تشفير على مستوى الملف من Mozilla. بدلاً من كنترولر خاص بـ Kubernetes، يعمل SOPS على مستوى نظام الملفات: يُشفّر القيم في ملف YAML أو JSON أو ENV بينما يترك المفاتيح قابلة للقراءة. يمكن أن يكون الخلفية التشفيرية AWS KMS أو GCP KMS أو Azure Key Vault أو HashiCorp Vault أو age (أداة تشفير حديثة وبسيطة). في الممارسة، age هو الافتراضي للفرق بلا KMS سحابي، بينما يُفضَّل AWS/GCP KMS في بيئات السحابة لأن الوصول للمفاتيح مرتبط بـ IAM — لا مواد مفتاح طويلة الأمد للإدارة.
تشفير Kubernetes Secret باستخدام SOPS + age:
# ثبّت age و sops
brew install age sops # macOS
# apt install age sops # Debian/Ubuntu
# أنشئ زوج مفاتيح age (افعل هذا مرة واحدة لكل هوية مشغّل/CI)
age-keygen -o age-key.txt
# المفتاح العام: age1qldzds...
# المفتاح الخاص مخزّن في age-key.txt -- لا تُلزم هذا الملف أبداً
# أضف المفتاح العام age إلى .sops.yaml في جذر المستودع -- هذا يُلزَم
cat > .sops.yaml << 'EOF'
creation_rules:
- path_regex: secrets/.*\.yaml$
age: age1qldzds6xnekn3yjcxgkuq6y8w6g5hkzxm... # استبدل بمفتاحك العام الحقيقي
EOF
# أنشئ ملف السر بنص عادي مؤقتاً
cat > /tmp/db-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
stringData:
username: appuser
password: S3cur3P@ssw0rd!
EOF
# شفّر باستخدام sops -- يُخرج النسخة المشفّرة
SOPS_AGE_KEY_FILE=age-key.txt sops --encrypt /tmp/db-secret.yaml \
> gitops-config/secrets/db-secret.yaml
# تحقق من قراءة المفاتيح (القيم مشفّرة):
# apiVersion: v1
# kind: Secret
# ...
# stringData:
# username: ENC[AES256_GCM,data:4wd...,type:str]
# password: ENC[AES256_GCM,data:rB2...,type:str]
# ألزم الملف المشفّر -- آمن للدفع
git add .sops.yaml gitops-config/secrets/db-secret.yaml
git commit -m "feat: add SOPS-encrypted db credentials"
git push
# على جانب الكلاستر (Flux مع دعم فك تشفير SOPS):
# أنشئ Kubernetes Secret يحتوي على المفتاح الخاص age
kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey=age-key.txt
# أشر إليه في مورد Flux Kustomization:
# decryption:
# provider: sops
# secretRef:
# name: sops-age
ثلاثة أساليب لإدارة الأسرار في GitOps — Sealed Secrets (مفتاح داخل الكلاستر)، SOPS (قيم مشفّرة في Git)، و External Secrets Operator (مرجع فقط في Git).
الخيار الثالث: External Secrets Operator (ESO)
يتبنى ESO نهجاً فلسفياً مختلفاً: الأسرار لا تُخزَّن أبداً في Git، حتى مشفّرة. بدلاً من ذلك، تُلزم مورداً مخصصاً خفيف الوزن من نوع ExternalSecret يصف أين تجد السر (مسار AWS Secrets Manager، مسار HashiCorp Vault، اسم GCP Secret Manager، إلخ) وكيف تُعيّنه إلى Kubernetes Secret. يقوم كنترولر ESO، الذي يعمل في الكلاستر، باستطلاع القبو الخارجي وتوليف Kubernetes Secret تلقائياً. عندما يتدوّر السر في القبو، يلتقط ESO القيمة الجديدة تلقائياً خلال فترة التحديث المُهيّأة.
# ثبّت ESO عبر Helm
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace \
--set installCRDs=true
# أنشئ SecretStore يشير إلى AWS Secrets Manager.
# يستخدم الكنترولر دور IAM مرتبطاً بـ ServiceAccount (IRSA / EKS Pod Identity).
cat > gitops-config/secrets/cluster-secretstore.yaml << 'EOF'
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secretsmanager
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
namespace: external-secrets
EOF
# أنشئ ExternalSecret يعيّن مسار Secrets Manager إلى k8s Secret
cat > gitops-config/secrets/db-external-secret.yaml << 'EOF'
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h # كم مرة يستطلع ESO القبو
secretStoreRef:
name: aws-secretsmanager
kind: ClusterSecretStore
target:
name: db-credentials # اسم k8s Secret المراد إنشاؤه
creationPolicy: Owner
data:
- secretKey: username # مفتاح في k8s Secret
remoteRef:
key: prod/db/credentials # مسار في Secrets Manager
property: username # حقل JSON داخل السر
- secretKey: password
remoteRef:
key: prod/db/credentials
property: password
EOF
# كلا الملفين YAML عادي بلا قيم سرية -- آمن للإلزام
git add gitops-config/secrets/
git commit -m "feat: add ExternalSecret for db credentials (ESO)"
git push
اختيار الأداة المناسبة
على نطاق شركات التقنية الكبرى، الأدوات الثلاث ليست متنافية — تستخدم كثير من المؤسسات ESO للأسرار المُدارة سحابياً (AWS SM، GCP SM، Vault) وSOP أو Sealed Secrets لأسرار التشغيل الأساسي التي يجب أن تكون موجودة قبل الوصول إلى القبو:
Sealed Secrets — الأفضل للكلاسترات المُستضافة ذاتياً بلا KMS سحابي، للفرق الصغيرة، ودورات حياة الأسرار البسيطة. عبء تشغيلي منخفض. الضعف: الأسرار مقيّدة بالكلاستر الذي يحتفظ بمفتاح الختم؛ فقدان المفتاح كارثي.
SOPS — الأفضل عندما تريد أسراراً مشفّرة مخزّنة في Git مع تاريخ كامل لتغييرات الأسرار، ولديك KMS (AWS KMS، age) تثق به. يعمل مع ArgoCD (عبر إضافة Helm Secrets أو حاوية init للفك) وFlux (دعم SOPS أصلي). أوضح مسار تدقيق — كل تغيير سر هو git diff (للنص المشفّر).
External Secrets Operator — الأفضل للمؤسسات cloud-native التي لديها مدير أسرار موجود. تتدور الأسرار تلقائياً، يُحكم الوصول بسياسة IAM (لا صلاحيات الملفات)، ونطاق الانفجار عند اختراق مستودع Git يساوي صفراً — المستودع يحتوي فقط مراجع، لا قيم مشفّرة. العبء هو أنك تحتاج مدير أسرار يعمل ومُهيّأ بشكل صحيح.
النمط الإنتاجي في المؤسسات الكبيرة: استخدم ESO كنمط أسرار أساسي لأسرار التطبيقات (بيانات اعتماد قواعد البيانات، مفاتيح API، شهادات TLS). استخدم SOPS لتشفير بيانات اعتماد التشغيل الأساسي لكنترولر ESO نفسه (مفتاح IAM أو رمز Vault الذي يحتاجه للمصادقة على مدير الأسرار عند الإقلاع الأول) — تلك لا يمكن جلبها من القبو لأن اتصال القبو لم يُنشأ بعد. هذا النهج ذو الطبقتين يمنحك تدويراً تلقائياً، لا نص عادي في Git، ومسار تشغيل أساسي مُدار بالكامل عبر GitOps.
دقّق تاريخ Git قبل اعتماد أي من هذه الأدوات. إذا أُلزم سر بنص عادي في أي وقت — حتى في commit أُعيد لاحقاً — فهو لا يزال في reflog وفي أي نسخة مستنسخة. شغّل أداة مثل truffleHog أو git-secrets على كامل تاريخك. إذا وجدت واحداً، دوّر السر فوراً، ثم استخدم BFG Repo Cleaner أو git filter-repo لتطهير التاريخ وإجبار الدفع. تعامل مع السر كمخترق حتى تؤكد التدوير.
الدرس التالي ينتقل إلى ترقية البيئة — كيف تتدفق التغييرات من التطوير عبر الاختبار إلى الإنتاج في pipeline GitOps، بما في ذلك تحديثات عناوين الصور التلقائية وبوابات الترقية.
نستخدم ملفات تعريف الارتباط لتشغيل هذا الموقع وتحليل الزيارات وعرض إعلانات مخصّصة. يمكنك قبول كل ملفات تعريف الارتباط أو رفض غير الأساسية منها.
سياسة الخصوصية