الأسرار والأمان في التكامل المستمر
الأسرار والأمان في التكامل المستمر
تعمل خطوط أنابيب CI مع وصول إلى حسابات سحابتك الإنتاجية، وسجلات الحاويات، ومفاتيح التوقيع، وبيانات اعتماد النشر. هذا يجعلها واحدة من أكثر الأهداف قيمة في بنيتك الهندسية بأكملها. اختراق Solar Winds لسلسلة التوريد، وسرقة بيانات اعتماد Codecov، وعشرات تقارير ما بعد الحوادث المنشورة، تشترك جميعها في خيط واحد: مهاجم وصل إلى نظام البناء ووجد طريقه مباشرة إلى الإنتاج. يغطي هذا الدرس كيف تحمي الفرق المتقدمة الأسرار وتُحصّن خطوط أنابيب CI حتى لا يتحول Runner مخترق إلى حادثة على مستوى الشركة.
لماذا الأسرار المُدوَّنة في الكود تُشكّل فئة ثغرة قائمة بذاتها
النهج الساذج — لصق رمز مميز في ملف سير العمل أو في .env مُودَع في المستودع — شائع جداً لدرجة أن المسح الآلي (GitHub Secret Scanning، TruffleHog، Gitleaks) يجعل منه وظيفة متفرغة. حتى لو حذفت السر من الملف، يحتفظ تاريخ Git به إلى الأبد ما لم تُعيد كتابة التاريخ وتُدوّر بيانات الاعتماد. في شركات التقنية الكبرى السياسة مطلقة: لا يلمس أي سر مستودع المصدر، حتى في مستودع خاص، حتى مشفراً، حتى مُرمَّزاً بـ base64. التعتيم ليس أماناً.
pull_request من Fork يعمل في سياق الـ Fork — ولا يمكنه الوصول إلى أسرار المستودع بشكل افتراضي. لكن إذا استخدمت pull_request_target (الذي يعمل في سياق المستودع الأساسي) ثم سحبت كود PR، فقد منحت المساهم وصولاً كاملاً لقراءة كل سر في مؤسستك. هذا أحد أكثر ناقلات تصعيد الصلاحيات شيوعاً في CI.
مخازن الأسرار: أين تعيش الأسرار فعلياً
كل منصة CI كبرى تأتي بمخزن أسرار مدمج. تُشفّر هذه المخازن الأسرار عند الراحة، وتحقنها في المهام كمتغيرات بيئة أو ملفات، والأهم — تُخفي قيمها في مخرجات السجل حتى لا يمكن سرقتها عبر خطوط تجميع السجلات.
أسرار GitHub Actions هي خط الأساس. أسرار المستودع مقيّدة بمستودع واحد؛ أسرار البيئة تُضيف بوابة موافقة (تتطلب مراجعاً قبل النشر على production)؛ أسرار المؤسسة يمكن مشاركتها عبر مستودعات بقوائم سماح صريحة.
للفرق التي تحتاج إدارة مركزية للأسرار عبر منصات CI متعددة وموفري السحابة وبيئات وقت التشغيل، يُعدّ مدير أسرار مخصص المعيار الإنتاجي: HashiCorp Vault، أو AWS Secrets Manager، أو GCP Secret Manager، أو Azure Key Vault. تُجلب الأسرار عند وقت تشغيل المهمة بتوكن قصير الصلاحية؛ ولا تُخزَّن أبداً في منصة CI نفسها.
بيانات الاعتماد الأقل صلاحية في CI
يجب أن تكون كل بيانات اعتماد CI مقيّدة بالحد الأدنى من الصلاحيات اللازمة لتلك المهمة المحددة — لا أكثر. هذا هو مبدأ الصلاحية الأقل مطبّقاً على خطوط الأنابيب. من الناحية العملية:
- بيانات اعتماد منفصلة لكل مرحلة: المهمة التي تُشغّل الاختبارات تحتاج صلاحية قراءة لسجل الحزم. المهمة التي تدفع صورة Docker تحتاج صلاحية كتابة على ECR. المهمة التي تنشر تحتاج AssumeRole على دور النشر فقط. يجب أن تكون هذه ثلاثة بيانات اعتماد مختلفة، وليس توكناً واحداً ذا صلاحيات شاملة.
- تقييد النطاق بالفروع والبيئات: بيانات الاعتماد التي يمكنها الكتابة على الإنتاج يجب أن تكون متاحة فقط في المهام المُشغَّلة من
mainوفقط في بيئةproduction(مع بوابات الموافقة). - التوكنات قصيرة الصلاحية تفضّل على مفاتيح API طويلة الصلاحية: مفتاح API لا تنتهي صلاحيته أبداً هو التزام يتفاقم مع الوقت. توكنات OIDC تنتهي في دقائق. توكنات AWS STS
AssumeRoleتنتهي في 1-12 ساعة. فضّلها.
OIDC: الطريقة الصحيحة لمصادقة CI على السحابة
يُزيل OpenID Connect (OIDC) بيانات الاعتماد طويلة الصلاحية كلياً. بدلاً من تخزين مفتاح AWS في GitHub Secrets، تُكوّن AWS لـتثق بموفر هوية GitHub. عندما تعمل مهمة ما، يُصدر GitHub للـ Runner توكن JWT موقّعاً يحتوي على ادعاءات مُتحقَّق منها: اسم المستودع، والفرع، والبيئة، واسم سير العمل. تتبادل AWS هذا JWT بتوكن STS مؤقت. لا يُخزَّن أي سر في أي مكان — يُثبت خط الأنابيب هويته تشفيرياً.
يتطلب إعداد OIDC بين GitHub Actions وAWS ثلاث خطوات لمرة واحدة:
sub في JWT الـ OIDC السياق الدقيق: repo:org/repo:ref:refs/heads/main للدفعات إلى main، وrepo:org/repo:environment:production للمهام المقيّدة بالبيئة. قيّد دائماً سياسة ثقة IAM بأكثر نمط sub تقييداً ممكناً. حرف بدل مثل repo:myorg/* يعني أن أي مستودع في مؤسستك يمكنه افتراض دور النشر — تكوين خاطئ بالغ الخطورة.
المتغيرات المُخفية ونظافة السجلات
تُخفي منصات CI تلقائياً قيم الأسرار في مخرجات السجل: إذا ظهرت قيمة سر في stdout أو stderr، تستبدلها المنصة بـ ***. هذا هو خط الدفاع الأخير، وليس الأول. عدة أنماط فشل تتجاوزه:
- حيل الترميز: ترميز سر بـ base64 أو URL قبل طباعته لن يُخفى. المهاجمون الذين يمكنهم تنفيذ خطوات تعسفية في خط أنابيبك يعرفون ذلك.
- التقسيم عبر أسطر: بعض منصات CI تُطابق النص الحرفي فقط. سر مُقسَّم عبر عدة تعليمات طباعة قد لا يُكتشف.
- الإجراءات من طرف ثالث: إجراء يملك وصولاً إلى
GITHUB_TOKENأو أي متغير بيئة مُحقَن يمكنه سرقته عبر الشبكة، متجاوزاً إخفاء السجل كلياً. دائماً قيّد الإجراءات من طرف ثالث بـ SHA كامل للإيداع، وليس بوسم — الوسوم قابلة للتغيير ويمكن اختطافها.
اكتشاف تسريبات الأسرار قبل وصولها إلى المستودع
أرخص إصلاح هو اكتشاف سر مُسرَّب قبل إيداعه على الإطلاق. أداتان متكاملتان:
- خطافات pre-commit مع Gitleaks أو detect-secrets: تفحص التغييرات المرحلية قبل إنشاء الإيداع. تعمل في ملي ثواني. تكتشف الأسرار محلياً قبل لمسها للبُعيد.
- خطوة مسح أسرار في CI: Gitleaks كخطوة مهمة في CI يفحص كل فرق طلب السحب. يعمل Secret Scanning المدمج في GitHub على كل دفعة إلى المستودع ويمكنه حجب الدفعات عبر حماية الدفع.
pre-commit (Python) مع تكوين مشترك في مستودع مركزي. اطلب gitleaks وdetect-secrets كخطافات. اجعل تثبيت الخطاف إلزامياً في سكريبت إعداد المطوّر. هذا ينقل الاكتشاف يساراً بأيام — قبل PR، قبل مراجعة الكود، قبل تشغيل أي CI.
أنماط الفشل الشائعة على نطاق الإنتاج
- الأسرار في وسيطات البناء:
docker build --build-arg API_KEY=$API_KEYيخبّئ القيمة في تاريخ طبقة الصورة. أي شخص يسحب الصورة يمكنه تشغيلdocker history --no-truncوقراءتها. استخدم بنيات متعددة المراحل واحقن الأسرار عبر BuildKit:RUN --mount=type=secret,id=api_key ... - ثقة OIDC ذات نطاق واسع: نسيان تحديد نطاق ادعاء
subيعني أن أي فرع أو PR يمكنه افتراض دور نشر الإنتاج. قيّده بـref:refs/heads/mainواستخدم قواعد حماية البيئة. - تسجيل متغيرات البيئة الحساسة: خطوة تشخيص مثل
env | sortستطبع كل متغير بيئة — بما فيها الأسرار المُحقَنة — في السجل. أزل خطوات التشخيص قبل الدمج؛ والأفضل ألا تطبع البيئة بأكملها أبداً. - مجموعات Runners المشتركة: على Runners عامة في GitHub Actions، تحصل كل مهمة على آلة افتراضية مؤقتة جديدة. على Runners ذاتية الاستضافة، يمكن لمهمة خبيثة ترك ملفات أو تعديلات بيئية تتسرب إلى المهمة التالية على نفس Runner. استخدم Runners مؤقتة ذاتية الاستضافة (يسجّل Runner، يُنفّذ مهمة واحدة، ثم ينتهي) أو اعزل Runners لكل بيئة.