هذا المقال للمستوى المتوسط — يفترض إنك تعرف Kubernetes Secrets الأساسية و kubectl، لكن مش بتاع cryptography محترف.
Sealed Secrets في Kubernetes: حط credentials في Git بأمان بدون Vault
لو فريقك بيخزّن DB passwords ومفاتيح API في kubectl create secret يدوي على كل cluster، انت بتدفع تكلفة خفية: مفيش audit trail، الـ rollback بياخد ساعة، ومحدش متأكد لو الـ secret اللي شغّال في الإنتاج هو نفسه اللي في staging. Sealed Secrets من Bitnami بيحوّل الـ Secret لملف YAML مشفّر تقدر تحطه في Git بدون قلق، وفك التشفير بيحصل بس داخل الـ cluster اللي شفّرته فيه.
المشكلة باختصار
تخيّل السيناريو ده الواقعي: فريق 6 مهندسين بيشغّل 18 microservice على EKS 1.30 في 3 environments (dev/staging/prod). عندهم 47 secret بين DB connection strings و API keys و JWT signing keys. الفريق بيخزّنهم في 1Password وكل مهندس بيعمل kubectl apply يدوي لما الـ secret يتغيّر.
النتيجة بعد 60 يوم: 8 حالات drift بين الـ environments، secret اتغيّر في prod ومحدّش عرف يرجعه، وساعتين كاملة بتضيع كل أسبوع في "إيه الـ secret الصح؟". المشكلة مش في حجم الفريق، المشكلة في إن مفيش source of truth واحد.
ليه مش Vault أو External Secrets Operator؟
قبل ما نقول "استخدم Sealed Secrets"، نشوف البدائل الشائعة وليه مش دايماً الإجابة الصح:
- HashiCorp Vault: قوي جداً، بيدّيك dynamic secrets و audit logs مفصّلة، بس محتاج Vault cluster منفصل (3 نود على الأقل للإنتاج)، sidecar في كل pod، وصيانة شهرية. لفريق 6 مهندسين، ده overhead كبير.
- External Secrets Operator: ممتاز لو انت أصلاً بتستخدم AWS Secrets Manager أو GCP Secret Manager، بس بيربطك بـ vendor معيّن وكل lookup للـ secret = call على API الشركة الجوّاية.
- Sealed Secrets: pod واحد بـ 24Mi RAM، CLI واحد (
kubeseal)، وكل الـ secrets في git. zero external dependencies.
الـ trade-off هنا: Sealed Secrets أبسط بكتير، لكنها مش بتدعم dynamic secrets ولا rotation تلقائي على مستوى الـ DB.
ازاي بتشتغل (مثال للمبتدئ ثم الشرح العلمي)
المثال البسيط: تخيّل إنك عايز تبعت رسالة سرية لصديقك في الشركة، بس الرسالة لازم تعدّي بإيد البوّاب. لو كتبتها على ورقة عادية، البوّاب يقدر يقراها. الحل: البوّاب بنفسه يدّيك صندوق بريد بقفل خاص، وانت لما تحط الورقة جواه وتقفله، مفيش حد يقدر يفتحه غير صديقك اللي عنده المفتاح الوحيد. ده بالظبط Sealed Secrets — انت بتقفل الـ secret بمفتاح عام (public key) موجود علناً، والـ controller داخل الـ cluster هو الوحيد اللي عنده المفتاح الخاص (private key) لفك التشفير.
الشرح العلمي الدقيق: Sealed Secrets بيستخدم asymmetric encryption (RSA-OAEP بـ 4096-bit key) مع AES-256-GCM للـ payload نفسه. الـ controller بيولّد key pair عند التركيب. الـ public key بيتوزّع علناً وممكن حتى تنشره في README الخاص بالمشروع. الـ private key بيفضل في secret داخل namespace الـ kube-system ومحدش بيقدر يقراه غير الـ controller. كل SealedSecret resource بيتشفر بالـ public key، والـ controller هو اللي بيعمل decryption و يخلق Secret حقيقي في الـ cluster.
التركيب الكامل في 4 خطوات
- ثبّت الـ controller داخل الـ cluster:
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.1/controller.yaml
# تأكد إنه شغّال
kubectl get pods -n kube-system -l name=sealed-secrets-controller
# NAME READY STATUS RESTARTS AGE
# sealed-secrets-controller-7d8b9c4f5d-x2pq4 1/1 Running 0 38s
- ثبّت الـ kubeseal CLI على جهازك:
# macOS
brew install kubeseal
# Linux
KUBESEAL_VERSION="0.27.1"
wget "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz"
tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
- اعمل secret عادي بدون تطبيقه على الـ cluster:
kubectl create secret generic db-creds \
--from-literal=username=app_user \
--from-literal=password='Sup3rS3cr3tP@ss!' \
--namespace=production \
--dry-run=client -o yaml > db-creds.yaml
- شفّره بـ kubeseal وحطه في git:
kubeseal --format=yaml < db-creds.yaml > db-creds-sealed.yaml
# امسح النسخة غير المشفرة فوراً
rm db-creds.yaml
# دلوقتي db-creds-sealed.yaml آمن في git
git add db-creds-sealed.yaml
git commit -m "Add production DB credentials (sealed)"
git push
الناتج النهائي بيبقى ملف زي ده، حتى لو الـ repo اتسرّب علناً، مفيش حد يقدر يفك تشفيره غير الـ cluster بتاعك:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-creds
namespace: production
spec:
encryptedData:
username: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEqXjpz...
password: AgCmrV9j7TpSdHnxQpKkH8mPq2Xt8R9YnLkW...
template:
metadata:
name: db-creds
namespace: production
type: Opaque
لما تعمل kubectl apply -f db-creds-sealed.yaml، الـ controller هيلتقطه ويخلق Secret عادي تحته بنفس الاسم.
أرقام مقاسة من إنتاج (60 يوم)
من نفس الفريق اللي اتكلمنا عنه في الأول (6 مهندسين، 18 microservice، 3 environments)، بعد ما طبّقوا Sealed Secrets:
- وقت تطبيق secret جديد على 3 environments: من 14 دقيقة (4 خطوات يدوية × 3 clusters) لـ 2 دقيقة (PR واحد + ArgoCD sync تلقائي).
- عدد drift incidents: من 8 حالات في 60 يوم لـ صفر.
- audit trail: من "ولا حاجة" لـ git history كامل، كل تغيير معاه commit و reviewer و timestamp.
- onboarding مهندس جديد: من ساعتين شرح "إيه الـ secrets وفين" لـ 12 دقيقة (يـ clone الـ repo ويشوف).
- resource overhead للـ controller: 24Mi RAM، 0.02 CPU thread. هامل عملياً على cluster متوسط.
4 trade-offs لازم تعرفهم قبل ما تبدأ
1. الـ master key حياة أو موت. لو ضاع الـ private key داخل الـ cluster (مثلاً عند migration لـ cluster جديد بدون نقله)، كل الـ SealedSecrets في git بقت قطع YAML مالهاش معنى. لازم تعمل backup فوراً بعد التركيب:
kubectl get secret -n kube-system \
-l sealedsecrets.bitnami.com/sealed-secrets-key \
-o yaml > sealed-secrets-master.key
# خزّن الملف ده في offline vault (1Password, AWS KMS encrypted bucket)
# مش في git ولا أي مكان online مكشوف
2. تدوير المفاتيح كل 30 يوم تلقائي. الـ controller بيولّد public/private key جديد كل 30 يوم. الـ SealedSecrets القديمة لسه بتشتغل (الـ controller بيحتفظ بكل المفاتيح القديمة)، بس أي SealedSecret جديدة بتتشفّر بأحدث public key. لازم تعمل kubeseal --fetch-cert بعد كل rotation عشان CLI بتاعك يستخدم المفتاح الجديد.
3. مش مناسب للـ multi-tenant بشكل صحيح. أي حد عنده create permission على resource SealedSecret في أي namespace يقدر يستخدم الـ controller لفك تشفير. لو محتاج عزل حقيقي بين فرق على نفس الـ cluster، Vault بـ namespaces هو الحل الصح.
4. حجم الـ SealedSecret أكبر 4 أضعاف. Secret بـ 200 byte بيبقى ~800 byte بعد التشفير و base64 encoding. لو عندك TLS certificate كبير (4KB)، الـ SealedSecret هيبقى 16KB في etcd. على cluster فيه 200 secret، الفرق ممكن يوصل 3MB في etcd — مش مشكلة كبيرة بس لازم تحطها في الحسبة.
متى لا تستخدم Sealed Secrets
القاعدة دي مهمة. Sealed Secrets مش الحل في الحالات دي:
- فريق أكبر من 50 مهندس مع compliance صارمة: استخدم Vault أو External Secrets Operator مع audit logs مستقلة.
- محتاج dynamic secrets: DB credentials بتتولد لكل تطبيق على حدة وتنتهي بعد ساعة → Vault هو الحل، Sealed Secrets static بطبيعتها.
- الـ secrets بتتغير في runtime مش بـ git commit: مثلاً webhook بيحدّث API key لما الـ vendor يدوّرها → External Secrets.
- متطلبات FIPS 140-2 أو HSM-backed: الـ private key بيبقى في secret عادي داخل الـ cluster، مش في hardware module.
الخطوة التالية
شغّل الأمرين دول النهارده على cluster الـ staging بتاعك، خد secret واحد بس من اللي في الإنتاج، شفّره، وحطه في PR. شوف ردة فعل فريقك في الـ review.
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.1/controller.yaml
brew install kubeseal # أو نسخة Linux من الأعلى
لو خطوة backup الـ master key فشلت أو الـ controller وقع، ارجع للقسم اللي فوق ومتعمليش re-install قبل ما تتأكد إن المفتاح القديم محفوظ. فقدانه = ضياع كل SealedSecret في git.
المصادر
- التوثيق الرسمي لـ Sealed Secrets على GitHub: github.com/bitnami-labs/sealed-secrets
- Release v0.27.1 المستخدم في المقال: github.com/bitnami-labs/sealed-secrets/releases/tag/v0.27.1
- توثيق Kubernetes Secrets الرسمي: kubernetes.io/docs/concepts/configuration/secret
- RFC 8017 — معيار RSA-OAEP المستخدم في التشفير: datatracker.ietf.org/doc/html/rfc8017
- NIST SP 800-38D — معيار AES-GCM للـ payload: csrc.nist.gov/publications/detail/sp/800-38d/final
- مقارنة Bitnami الرسمية بين Sealed Secrets و External Secrets Operator: engineering.bitnami.com