ConfigMap، أو بتشيّفر ملفات .env على Git، في 3 أوامر تقدر تخلّي Kubernetes يسحب الأسرار مباشرة من AWS Secrets Manager أو HashiCorp Vault — ويعمل rotation مركزي من غير ما حد يلمس الكلاستر.
External Secrets Operator: الطريقة الصناعية لإدارة أسرار Kubernetes
المشكلة باختصار
Kubernetes Secrets بتتخزّن في etcd بصيغة base64. وbase64 مش تشفير — هو encoding عادي أي حد بيفكّه في ثانية. النتيجة: معظم الفرق بتعمل workaround يدوي — git-crypt، SOPS، sealed-secrets، أو الأسوأ، صور Docker فيها مفاتيح hardcoded.
اللي بيحصل فعلاً: rotation ليّ db password بيبقى عملية مدّتها 15–20 دقيقة عبر 12 environment، وفيها دايمًا إنسان بينسى namespace. الحل الصناعي اسمه External Secrets Operator (ESO) — مشروع CNCF Sandbox بيحوّل الـ Kubernetes Secret من حاجة يدوية لـ resource يتسحب من provider خارجي تلقائيًا.
إزاي ESO بيشتغل (مثال المستشفى قبل الكلام التقني)
تخيّل مستشفى كبيرة فيها خزنة مركزية (AWS Secrets Manager) فيها كل الأدوية الحساسة. الدكاترة (الـ Pods) مش من حقهم يدخلوا الخزنة مباشرة. في ممرضة مسؤولة (ESO Controller) كل ساعة بتنزل تجيب اللي محتاجينه، وتحطه في صيدلية صغيرة جنب كل غرفة (native Kubernetes Secret). لما طبيب الصيدلة يغيّر الجرعة في الخزنة المركزية، الممرضة بتعيد التوزيع تلقائيًا على كل الغرف بدون ما حد يوقف العمل.
بالتعريف التقني: ESO هو Kubernetes operator بيراقب custom resources (ExternalSecret، SecretStore، ClusterSecretStore) ويعمل reconciliation loop دوري. بيجيب القيم من provider خارجي مدعوم — AWS Secrets Manager، AWS Parameter Store، GCP Secret Manager، Azure Key Vault، HashiCorp Vault، 1Password، Doppler، وغيرهم — ويحوّلها لـ Secret عادي جوا الـ cluster. الـ Pods ما بتشوفش الفرق، وده اللي بيخلّي التبنّي سهل.
التثبيت في 3 أوامر
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set installCRDs=trueبعد الـ install، هتلاقي 3 CRDs جديدة: ExternalSecret، SecretStore، ClusterSecretStore. الـ controller نفسه Pod واحد بيشتغل في namespace منفصل.
مثال شغّال: AWS Secrets Manager
السيناريو: عندك app في namespace prod محتاج DB_PASSWORD، و بتحطه دلوقتي يدوي. الخطوات:
- أنشئ IAM role معاه permission
secretsmanager:GetSecretValueعلى prefixprod/app/*بس. - اربطه بـ ServiceAccount عبر IRSA (IAM Roles for Service Accounts).
- عرّف SecretStore بيشاور على الـ provider.
- عرّف ExternalSecret بيحدد أنهي مفتاح من AWS هيتحول لـ Kubernetes Secret.
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-store
namespace: prod
spec:
provider:
aws:
service: SecretsManager
region: eu-central-1
auth:
jwt:
serviceAccountRef:
name: eso-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: prod
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-store
kind: SecretStore
target:
name: app-db-secret
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/app/db
property: passwordالـ Pod بيستهلك الـ Secret بالطريقة العادية، من غير ما يعرف حاجة عن AWS:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-db-secret
key: DB_PASSWORDأرقام واقعية — التكلفة و الـ API quota
الناس بتخاف إن ESO هيضرب AWS API في الدقيقة. الحقيقة:
refreshInterval: 1h= نداء API واحد كل ساعة لكلExternalSecret.- 50
ExternalSecret× 24 ساعة = 1200 request يوميًا. - AWS Secrets Manager بيتسعّر $0.05 لكل 10,000 request (حسب التوثيق الرسمي لـ AWS) → التكلفة الشهرية تقريبًا $0.18 للـ 50 سر.
- Rate limit: AWS بيسمح بحوالي 5,000 request/sec على Secrets Manager؛ ESO مستحيل يوصل الرقم ده إلا لو ضبطت
refreshIntervalبقيم مجنونة زي 5s.
قياس قبل/بعد من تجربة فريق حقيقي: rotation لـ DB password كان بياخد 22 دقيقة يدوي عبر 14 deployment في 4 namespaces. بعد ESO، التعديل بيحصل في AWS مرة واحدة، والـ cluster بيعكسه في دقيقة واحدة بـ refreshInterval: 1m (أو فوريًا لو شغّلت PushSecret أو webhook-triggered refresh).
Trade-offs بصراحة
- هتكسب: مركزية الأسرار، audit log كامل في CloudTrail/Vault، separation of duties (مطور ما يشوفش قيمة الـ secret أبدًا)، rotation بنقرة واحدة.
- هتخسر: dependency تشغيلي جديد. لو ESO controller وقع، الـ Secrets الموجودة في الـ cluster بتفضل شغالة، لكن أي
ExternalSecretجديد أو تحديث مش هيحصل. لازم تراقبexternal_secrets_sync_calls_totalوexternal_secrets_sync_calls_errorمن الـ Prometheus metrics بتاعته. - الافتراض: الشرح ده مبني على إنك عندك provider خارجي فعلي (AWS/GCP/Vault). لو مشروعك صغير ومفيش cloud provider، sealed-secrets أبسط.
متى لا تستخدم هذه الطريقة
- مشروع فيه 1–3 أسرار ثابتة ما بتتغيّرش. الـ overhead مش مبرر.
- بيئة air-gapped مفيهاش وصول لأي cloud provider. الحل المناسب: Vault on-prem أو sealed-secrets.
- فريق لسه مبيفهمش RBAC أصلًا. ESO بيعتمد على IAM/IRSA/workload identity بشكل صريح، ولو الـ base ضعيف هتتكسر في أول أسبوع.
Best practices سريعة
- استخدم
ClusterSecretStoreللـ providers المشتركة بين namespaces، وSecretStoreلو في تفرقة صارمة. refreshIntervalالافتراضي 1h كافي لـ 90% من الحالات. قلّله لـ 5m فقط للأسرار اللي بتتبدّل كل يوم.- فعّل
metricsو اربطها بـ Prometheus، واعمل alert علىexternal_secrets_sync_calls_error > 0. - استخدم
creationPolicy: Ownerعلشان لو مسحت الـExternalSecret، الـ Secret المولّد يتمسح معاه. ده بيمنع orphaned secrets.
الخطوة التالية
نصب ESO في cluster staging، اختار سر واحد (DB password مثلًا)، عرّفه كـ ExternalSecret، وشوف audit log في Secrets Manager قبل وبعد. لو الـ error metrics نضيفة لمدة 48 ساعة، كرّر العملية في production. لو external_secrets_sync_calls_error طلع غير صفر، راجع الـ IAM policy — 95% من المشاكل هناك.
المصادر
- External Secrets Operator — التوثيق الرسمي: external-secrets.io/latest
- Kubernetes Secrets — التوثيق الرسمي: kubernetes.io/docs/concepts/configuration/secret
- AWS Secrets Manager Pricing: aws.amazon.com/secrets-manager/pricing
- HashiCorp Vault Kubernetes Integration: developer.hashicorp.com/vault/docs/platform/k8s
- CNCF Landscape — Key Management: landscape.cncf.io
- IRSA (IAM Roles for Service Accounts): docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts