نماذج تنفيذ CI/CD وافتراضات الثقة: دليل أمني

مقدمة

تُعدّ أنابيب CI/CD من أكثر المكونات امتيازاً في أي مؤسسة برمجية حديثة. فهي تستنسخ الشيفرة المصدرية، وتصل إلى الأسرار، وتبني الحزم البرمجية، وتنشرها في بيئة الإنتاج — وغالباً بأدنى حد من الإشراف البشري. ومع ذلك، وعلى الرغم من هذا المستوى الاستثنائي من الوصول، نادراً ما تُحدَّد نماذج الثقة التي تقوم عليها هذه الأنابيب بشكل صريح.

عندما تعمل أنبوبة معالجة ما، فإنها تجيب ضمنياً عن سلسلة من الأسئلة الأمنية: من الذي أطلق هذا التنفيذ؟ ما الشيفرة التي يتم تشغيلها؟ ما الهوية التي تتبناها الأنبوبة؟ ما الموارد التي يمكنها الوصول إليها؟ في معظم المؤسسات، تتم الإجابة عن هذه الأسئلة من خلال الإعدادات الافتراضية بدلاً من قرارات أمنية مدروسة.

يرسم هذا الدليل خريطة لكيفية عمل نماذج تنفيذ CI/CD المختلفة، وأين تُفترض الثقة مقابل التحقق منها، وكيفية تقوية أنابيبك ضد أنماط الهجوم الواقعية التي تستغل هذه الثغرات. سواء كنت تستخدم GitHub Actions أو GitLab CI أو منصة أخرى، فإن ديناميكيات الثقة الأساسية عالمية — وفهمها ضروري لتأمين سلسلة توريد البرمجيات الخاصة بك.

ما هو نموذج تنفيذ CI/CD؟

يُحدد نموذج تنفيذ CI/CD دورة الحياة الكاملة لكيفية تشغيل شيفرة الأنبوبة، والمكان الذي تعمل فيه فعلياً، والهوية التي تتبناها أثناء التنفيذ، والموارد التي يمكنها الوصول إليها. إنه في جوهره البنية الأمنية لطبقة الأتمتة الخاصة بك.

يجب على كل نموذج تنفيذ أن يجيب عن أربعة أسئلة جوهرية:

  • المُحفِّز (Trigger): ما الحدث الذي يبدأ الأنبوبة، ومن أو ما المُخوَّل بإحداث ذلك الحدث؟
  • البيئة (Environment): أين تُنفَّذ شيفرة الأنبوبة — على أي بنية تحتية، وبأي نظام تشغيل، وبأي درجة من العزل؟
  • الهوية (Identity): ما بيانات الاعتماد أو الرموز المميزة أو حسابات الخدمة التي تمتلكها الأنبوبة أثناء التشغيل؟
  • الوصول (Access): ما الأنظمة النهائية والأسرار والسجلات وأهداف النشر التي يمكن للأنبوبة الوصول إليها؟

تختلف طريقة الإجابة عن هذه الأسئلة اختلافاً كبيراً عبر بيئات التنفيذ:

المُشغّلات المستضافة عبر SaaS

توفر منصات مثل GitHub Actions (المُشغّلات المستضافة من GitHub) وGitLab.com shared runners آلات افتراضية مؤقتة تُدار بواسطة مزوّد CI/CD. عادةً ما يحصل كل مهمة على آلة افتراضية جديدة تُدمَّر بعد التنفيذ. تتولى المنصة إدارة التحديثات والعزل ودورة الحياة. المقايضة هنا هي أنك تثق بضمانات العزل التي يقدمها المزوّد — فلا يمكنك فحص البنية التحتية الأساسية أو التحكم فيها.

المُشغّلات ذاتية الاستضافة (Self-Hosted Runners)

تنشر المؤسسات وكلاء التشغيل الخاصة بها على بنية تحتية تتحكم فيها — آلات افتراضية أو خوادم مادية أو pods في Kubernetes. يمنح هذا تحكماً كاملاً في بيئة التنفيذ، لكنه ينقل مسؤولية العزل والتحديث وإدارة بيانات الاعتماد بالكامل إلى المُشغّل. يُعدّ المُشغّل ذاتي الاستضافة المُهيَّأ بشكل خاطئ من أكثر نواقل الحركة الجانبية شيوعاً في هجمات CI/CD.

التنفيذ في حاويات (Containerized Execution)

تُشغّل العديد من الأنابيب المهام داخل حاويات، سواء على بنية تحتية ذاتية الاستضافة أو على مجموعات Kubernetes مُدارة. يوفر التنفيذ القائم على الحاويات عزلاً على مستوى العمليات وبيئات قابلة للتكرار، لكن الحاويات ليست حدوداً أمنية بنفس الطريقة التي تكون بها الآلات الافتراضية. يمكن أن يُقوّض الوصول المشترك إلى النواة والأقراص المُركَّبة وكشف Docker socket نموذج العزل.

التنفيذ بدون خادم وعند الطلب (Serverless and On-Demand Execution)

تُنشئ بعض أنظمة CI/CD الحديثة (مثل AWS CodeBuild أو بعض تكوينات Buildkite) موارد حوسبة عند الطلب لكل مهمة. تقدم هذه النماذج ضمانات عزل قوية حيث يحصل كل تنفيذ على مثيل حوسبة مخصص قصير العمر، لكنها تُدخل تعقيداً حول تهيئة بيانات الاعتماد والتحكم في الوصول إلى الشبكة.

إن فهم النموذج الذي تستخدمه مؤسستك — والخصائص الأمنية التي يوفرها والتي لا يوفرها — هو الأساس للتفكير في ثقة CI/CD.

حدود الثقة في CI/CD

يوجد حد ثقة حيثما ينتقل التحكم من كيان أو نظام إلى آخر. في CI/CD، هناك عدة حدود ثقة حرجة، والإخفاق عند أي منها يمكن أن يؤدي إلى اختراق كامل للأنبوبة.

من مستودع الشيفرة المصدرية إلى مُحفِّز الأنبوبة

حد الثقة الأول يقع بين مستودع الشيفرة ومُحفِّز الأنبوبة. عندما يدفع مطوّر التزاماً أو يفتح طلب سحب (pull request)، تقرر منصة CI/CD ما إذا كانت ستنفذ أنبوبة وكيف. السؤال الحرج هو: من يمكنه تشغيل تنفيذ الأنبوبة، وهل يمكنه التحكم في الشيفرة التي تُشغّلها الأنبوبة؟

في كثير من التكوينات، يمكن لأي شخص يستطيع فتح طلب سحب — بما في ذلك المساهمون الخارجيون في المستودعات العامة — تشغيل تنفيذ الأنبوبة. إذا كان تعريف الأنبوبة نفسه يأتي من فرع طلب السحب، فإن المساهم يتحكم فعلياً في الشيفرة التي تعمل في بيئة CI الخاصة بك.

من تعريف الأنبوبة إلى بيئة التنفيذ

حد الثقة الثاني يفصل تعريف الأنبوبة (ملف YAML أو Jenkinsfile أو سكريبت البناء) عن البيئة التي يُنفَّذ فيها. تشمل الأسئلة الرئيسية: هل يملك المُشغّل وصولاً إلى الشبكة؟ هل يمكن للأنبوبة تثبيت برامج عشوائية؟ هل يمكنها تعديل المُشغّل نفسه للمهام المستقبلية؟

على المُشغّلات المشتركة أو المستمرة، يمكن لتعريف أنبوبة خبيث تثبيت باب خلفي يستمر عبر عمليات التنفيذ اللاحقة — مما يؤثر على مستودعات وفرق مختلفة تماماً.

من بيئة التنفيذ إلى الأسرار وبيانات الاعتماد

تحتاج الأنابيب إلى بيانات اعتماد للقيام بعمل مفيد: رموز API ومفاتيح مزودي السحابة وكلمات مرور السجلات ومفاتيح التوقيع. يحدد حد الثقة بين بيئة التنفيذ ومخزن الأسرار ما يمكن لأنبوبة مخترقة الوصول إليه. يُعدّ الوصول المفرط إلى الأسرار من أكثر الأخطاء شيوعاً وخطورة في تكوينات CI/CD.

من مخرجات البناء إلى هدف النشر

حد الثقة الأخير يقع بين ما تنتجه الأنبوبة (صورة حاوية أو ملف ثنائي أو خطة Terraform) والنظام الذي تُنشر فيه تلك المخرجات. إذا كانت هوية الأنبوبة التي تبني الحزمة هي نفس الهوية التي تنشرها في الإنتاج، فلا يوجد فصل في المسؤوليات. يمكن أن تؤدي خطوة بناء واحدة مخترقة مباشرة إلى اختراق بيئة الإنتاج.

رسم خريطة مناطق الثقة

من الناحية المفاهيمية، تمر أنبوبة CI/CD عبر أربع مناطق ثقة:

Zone 1: Source Control (Developer workstations, branches, PRs)
   ↓ [Trigger boundary]
Zone 2: Pipeline Definition (YAML/config parsed by CI platform)
   ↓ [Execution boundary]
Zone 3: Execution Environment (Runner, container, VM — with secrets)
   ↓ [Deployment boundary]
Zone 4: Deployment Targets (Production, staging, registries, cloud APIs)

يمثل كل سهم حد ثقة. يجب أن توجد ضوابط أمنية عند كل انتقال: قواعد حماية الفروع عند حد المُحفِّز، وعزل المُشغّل عند حد التنفيذ، وبيانات اعتماد محدودة النطاق عند حد الأسرار، وموافقات النشر عند حد النشر.

نموذج تنفيذ GitHub Actions

تُعدّ GitHub Actions واحدة من أكثر منصات CI/CD اعتماداً، ونموذج تنفيذها يتمتع بعدة خصائص ثقة فريدة تستحق الفهم المعمّق.

المُشغّلات المستضافة من GitHub مقابل المُشغّلات ذاتية الاستضافة

المُشغّلات المستضافة من GitHub هي آلات افتراضية مؤقتة تُوفّرها GitHub لكل مهمة. تعمل على بنية Azure التحتية، وتُدمَّر بعد اكتمال كل مهمة، وتوفر عزلاً قوياً بين المهام. أما المُشغّلات ذاتية الاستضافة فهي أجهزة تسجّلها لدى GitHub. تستمر بين المهام ويمكنها تجميع حالة، والأهم من ذلك أن أي مستودع في المنظمة لديه وصول إلى المُشغّل يمكنه تنفيذ شيفرة عليه.

بالنسبة للمُشغّلات ذاتية الاستضافة، تحذر GitHub صراحةً: لا تستخدم المُشغّلات ذاتية الاستضافة مع المستودعات العامة. يمكن لأي fork تقديم طلب سحب يُشغّل سير عمل، وهذا السير يُنفَّذ على بنيتك التحتية مع وصولك للشبكة.

أذونات ونطاق GITHUB_TOKEN

يتلقى كل تشغيل سير عمل تلقائياً GITHUB_TOKEN بأذونات محددة النطاق للمستودع. افتراضياً، يمتلك هذا الرمز أذونات قراءة/كتابة واسعة لمحتويات المستودع والحزم والمشكلات وأكثر. يسمح مفتاح permissions بتقييد هذا الرمز إلى ما هو مطلوب فقط:

permissions:
  contents: read
  packages: write
  id-token: write   # For OIDC federation

يُعدّ تعيين الأذونات على مستوى أعلى إلى read-all أو حتى فارغة ({}) ثم منح أذونات محددة لكل مهمة خطوة تقوية حاسمة. بدون ذلك، فإن أي خطوة مخترقة في أي مهمة تملك وصول كتابة إلى مستودعك.

سير عمل Fork PR: pull_request مقابل pull_request_target

هذا أحد أخطر حدود الثقة في GitHub Actions. يُشغّل حدث pull_request تعريف سير العمل من فرع head الخاص بطلب السحب — مما يعني أن المساهم يتحكم في شيفرة سير العمل — لكنه بشكل حاسم لا يملك وصولاً إلى أسرار المستودع. أما حدث pull_request_target فيُشغّل سير العمل من الفرع الأساسي (الفرع الرئيسي لمستودعك) ويملك وصولاً إلى الأسرار.

ينشأ الخطر عندما تقوم سير عمل pull_request_target بسحب شيفرة فرع head الخاص بطلب السحب:

# DANGEROUS: pull_request_target with explicit checkout of PR code
on: pull_request_target

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      # This now runs UNTRUSTED CODE with access to SECRETS
      - run: npm install && npm test

يمنح هذا النمط المهاجم القدرة على تنفيذ شيفرة عشوائية مع الوصول إلى أسرار مستودعك. وهو المثال النموذجي لتنفيذ الأنبوبة المسمومة (poisoned pipeline execution) في GitHub Actions.

سير العمل القابلة لإعادة الاستخدام وتفويض الثقة (Reusable Workflows)

تتيح لك سير العمل القابلة لإعادة الاستخدام مركزة منطق الأنبوبة في مستودع مشترك واستدعاءها من مستودعات أخرى. عند استدعاء سير عمل قابل لإعادة الاستخدام، يعمل بأذونات وأسرار سير العمل المُستدعي. يُنشئ هذا سلسلة تفويض ثقة: أنت تثق بأن شيفرة سير العمل القابل لإعادة الاستخدام (في مستودع آخر) ستتعامل مع أسرارك بمسؤولية.

ثبّت سير العمل القابلة لإعادة الاستخدام على SHA commit محدد، وليس على فرع أو وسم:

jobs:
  deploy:
    uses: my-org/shared-workflows/.github/workflows/deploy.yml@a1b2c3d4e5f6
    secrets: inherit

قواعد حماية البيئة (Environment Protection Rules)

توفر GitHub Environments حد ثقة حاسماً لسير عمل النشر. يمكنك تكوين مراجعين مطلوبين ومؤقتات انتظار وقيود فروع على البيئات. عندما تشير مهمة إلى بيئة، يجب أن تستوفي قواعد الحماية قبل إتاحة الأسرار المرتبطة بتلك البيئة:

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - name: Deploy
        run: ./deploy.sh
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.PROD_AWS_KEY }}

يضمن هذا أنه حتى لو تم تشغيل سير العمل، لن تُكشف بيانات اعتماد الإنتاج بدون موافقة بشرية.

نموذج تنفيذ GitLab CI

يتميز GitLab CI بنموذج تنفيذ مختلف مع خصائص ثقة خاصة به، لا سيما حول نطاق المُشغّلات وحماية المتغيرات.

Shared Runners مقابل Group Runners مقابل Project Runners

يقدم GitLab ثلاثة مستويات من نطاق المُشغّلات. Shared runners (على GitLab.com، تُدار بواسطة GitLab) متاحة لجميع المشاريع. Group runners متاحة لجميع المشاريع ضمن مجموعة GitLab. Project runners مخصصة لمشروع واحد. يحدد النطاق نصف قطر الانفجار لمُشغّل مخترق — اختراق shared runner يؤثر على جميع المشاريع، بينما اختراق project runner محصور بمشروع واحد.

للأحمال الحساسة، استخدم دائماً مُشغّلات خاصة بالمشروع مع وسوم مناسبة:

deploy-production:
  stage: deploy
  tags:
    - production-runner
    - isolated
  script:
    - ./deploy.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

الفروع المحمية والمتغيرات المحمية (Protected Branches and Protected Variables)

تُعدّ آلية المتغيرات المحمية في GitLab ضابط ثقة رئيسياً. المتغيرات المُعلَّمة كـ “محمية” تُكشف فقط للأنابيب التي تعمل على فروع محمية أو وسوم محمية. هذا يعني أن أنبوبة تُشغَّل بواسطة طلب دمج من فرع ميزة — أو أسوأ من ذلك، من fork — لن تملك وصولاً إلى المتغيرات المحمية.

هذه هي الآلية الأساسية في GitLab لمنع كشف الأسرار للشيفرة غير الموثوقة:

# In .gitlab-ci.yml, protected variables are only available on protected branches
deploy:
  stage: deploy
  script:
    - echo "Deploying with $PRODUCTION_API_KEY"
  rules:
    - if: $CI_COMMIT_BRANCH == "main"  # main is a protected branch
  environment:
    name: production

نطاق وقيود CI_JOB_TOKEN

تتلقى كل مهمة GitLab CI رمز CI_JOB_TOKEN، وهو رمز يُنشأ تلقائياً ومحدد النطاق للمشروع. افتراضياً، يمكن لهذا الرمز الوصول إلى موارد مشاريع أخرى، مما يُنشئ علاقة ثقة ضمنية. يسمح GitLab بتقييد وصول CI_JOB_TOKEN من خلال تكوين قائمة مسموح بها من المشاريع التي يمكن الوصول إليها — خطوة تقوية حاسمة تحد من الحركة الجانبية إذا تم اختراق أنبوبة.

في إعدادات مشروعك ضمن CI/CD → Token Access، قيّد نطاق الرمز إلى المشاريع التي تحتاج أنبوبتك فعلاً للتفاعل معها فقط.

أنابيب طلب الدمج وحدود الثقة (Merge Request Pipelines)

يميّز GitLab بين أنابيب الفرع وأنابيب طلب الدمج. تعمل أنابيب طلب الدمج في سياق طلب الدمج ولها وصول إلى مجموعة محدودة من المتغيرات المُحددة مسبقاً. بالنسبة للأنابيب المُشغَّلة بواسطة طلبات دمج من forks، لا يكشف GitLab المتغيرات المحمية أو أسرار مستوى المشروع — هذا حد ثقة مقصود.

ومع ذلك، فإن الأنابيب التي تعمل على النتيجة المدمجة (merge_request_event مع تمكين أنابيب النتائج المدمجة) لا تزال تُنفّذ الشيفرة من الـ fork. إذا سمح تعريف أنبوبتك بتنفيذ شيفرة عشوائية وكانت المهمة تملك وصولاً إلى الأسرار من خلال متغيرات غير محمية، فلا يزال من الممكن استغلال ذلك.

إخفاقات افتراضات الثقة الشائعة

فهم نماذج التنفيذ مهم، لكن القيمة الحقيقية تأتي من التعرف على الأنماط التي تؤدي إلى الاختراق. هذه هي إخفاقات افتراضات الثقة التي تظهر بشكل متكرر في اختراقات CI/CD الواقعية.

تنفيذ الأنبوبة المسمومة (Poisoned Pipeline Execution – PPE)

يحدث تنفيذ الأنبوبة المسمومة عندما يستطيع مهاجم تعديل تعريف الأنبوبة الذي يعمل في سياق ذي امتيازات. هذا هو الفئة الأكثر انتشاراً من ثغرات CI/CD. يحدث عندما:

  • يُشغّل طلب سحب سير عمل يستخدم نسخة طلب السحب من ملف الأنبوبة
  • يملك سير العمل هذا وصولاً إلى الأسرار أو بيانات اعتماد النشر
  • لا يوجد بوابة مراجعة أو موافقة بين طلب السحب وتنفيذ الأنبوبة

يعدّل المهاجم ملف YAML الخاص بالأنبوبة (أو سكريبت تستدعيه) لاستخراج الأسرار أو حقن أبواب خلفية في حزم البناء أو التمحور إلى الأنظمة الداخلية.

افتراض عزل المُشغّل على البنية التحتية المشتركة

عندما تتشارك فرق أو مشاريع متعددة في المُشغّلات — خاصة المُشغّلات ذاتية الاستضافة — غالباً ما يكون هناك افتراض ضمني للعزل لا يوجد فعلياً. يمكن لمهمة تعمل على مُشغّل ذاتي الاستضافة مشترك أن:

  • تقرأ ملفات تركتها مهام سابقة (بيانات اعتماد مخبأة، حزم بناء)
  • تصل إلى Docker socket وتفحص أو تعدّل حاويات أخرى
  • تصل إلى موارد الشبكة الداخلية المتاحة لمضيف المُشغّل
  • تثبّت أبواباً خلفية مستمرة على المُشغّل للمهام المستقبلية

حسابات الخدمة ذات الامتيازات المفرطة

من الأنماط الشائعة بشكل مقلق منح حساب خدمة CI/CD وصولاً إدارياً واسعاً — “فقط لجعل الأمور تعمل”. دور AWS IAM مع AdministratorAccess، أو حساب خدمة Kubernetes مع cluster-admin، أو حساب cloud SQL بامتيازات DBA. عند اختراق أي خطوة في الأنبوبة، يرث المهاجم جميع هذه الأذونات.

الثقة الضمنية في Actions والقوالب من جهات خارجية

استخدام GitHub Actions أو قوالب GitLab CI من المجتمع يعني تنفيذ شيفرة شخص آخر في أنبوبتك مع أسرارك. عندما تشير إلى uses: some-org/some-action@v2، فأنت تثق بأن:

  • شيفرة الـ action ليست خبيثة
  • مشرفو الـ action لم يتعرضوا للاختراق
  • وسم v2 لم يُنقل ليشير إلى شيفرة مختلفة
  • تبعيات الـ action جديرة بالثقة

مراجع الوسوم قابلة للتغيير. يمكن لمهاجم يخترق مستودع action نقل وسم v2 إلى commit خبيث، وكل أنبوبة تشير إلى ذلك الوسم ستُنفّذ الشيفرة الجديدة في تشغيلها التالي.

الخلط بين هوية وقت البناء وهوية وقت النشر

تستخدم العديد من الأنابيب هوية واحدة (حساب خدمة أو دور IAM أو رمز مميز) لكل من البناء والنشر. هذا الدمج يعني أن الاختراق أثناء مرحلة البناء — التي تتعامل مع شيفرة غير موثوقة — يمنح وصولاً مباشراً إلى أهداف النشر. يجب أن تكون هوية البناء قادرة فقط على إنتاج الحزم. يجب استخدام هوية نشر منفصلة وأكثر تقييداً لنشر تلك الحزم في الإنتاج.

تقوية افتراضات الثقة

مع وضوح نموذج التهديد، إليك التدابير الملموسة التي تُوائم الضوابط مع حدود الثقة.

شروط التشغيل الصريحة ومرشحات الفروع

لا تسمح أبداً بمُحفِّزات أنبوبة غير مقيدة. حدّد الأحداث التي يمكنها تشغيل سير العمل، وتأكد من أن الأنابيب ذات الامتيازات تعمل فقط على فروع موثوقة:

# GitHub Actions: restrict deployment to main branch only
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
    # Only trigger on PRs targeting main; PR code runs without secrets

jobs:
  deploy:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh
# GitLab CI: use rules to restrict sensitive jobs
deploy-production:
  stage: deploy
  script:
    - ./deploy.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE != "merge_request_event"
      when: manual
      allow_failure: false
  environment:
    name: production

أذونات الرموز المميزة بالحد الأدنى

طبّق مبدأ الحد الأدنى من الامتيازات على كل رمز في أنبوبتك. في GitHub Actions، عيّن أذونات افتراضية مقيّدة وامنح أذونات محددة لكل مهمة:

# Set restrictive defaults at the workflow level
permissions: read-all

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build

  deploy:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write  # Only for OIDC, no write to repo
    environment: production
    steps:
      - run: ./deploy.sh

في GitLab، قيّد نطاق CI_JOB_TOKEN في إعدادات المشروع واستخدم المتغيرات المحمية حصرياً لبيانات الاعتماد الحساسة.

مُشغّلات مؤقتة ومعزولة

حيثما أمكن، استخدم مُشغّلات مؤقتة تُنشأ من جديد لكل مهمة وتُدمَّر فور انتهائها. يقضي هذا على الهجمات القائمة على الاستمرارية وتسرب البيانات بين المهام. للبيئات ذاتية الاستضافة، يمكن لأدوات مثل Actions Runner Controller (ARC) لـ Kubernetes من GitHub أو مُشغّل GitLab القابل للتوسع التلقائي على AWS/GCP توفير pods أو آلات افتراضية مؤقتة للمُشغّل لكل مهمة.

الخصائص الرئيسية لتكوين مُشغّل مقوّى:

  • لا تخزين مستمر بين المهام
  • لا Docker socket مشترك
  • تجزئة شبكية تحدّ الوصول إلى النقاط النهائية المطلوبة فقط
  • لا قدرة للمهمة على تعديل تكوين المُشغّل نفسه

تثبيت Actions والصور بواسطة SHA

يمكن تغيير المراجع القابلة للتغيير (أسماء الفروع، وسوم مثل v2) بواسطة المشرفين الأصليين — أو المهاجمين. التثبيت على SHA commit محدد يضمن أن الشيفرة التي راجعتها بالضبط هي ما يعمل في أنبوبتك:

# Instead of this (mutable tag):
- uses: actions/checkout@v4

# Use this (immutable SHA):
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1

ينطبق نفس المبدأ على صور الحاويات. استخدم image digests بدلاً من الوسوم:

# Instead of:
image: node:20-alpine

# Use:
image: node@sha256:a1b2c3d4e5f6...  # pin to specific digest

يمكن لأدوات مثل Dependabot وRenovate إنشاء طلبات سحب تلقائياً لتحديث SHAs المُثبَّتة عند إصدار نسخ جديدة، فتحصل على الأمان وسهولة الصيانة معاً.

فصل هويات البناء والنشر

نفّذ هويات مميزة لمرحلتي البناء والنشر. يجب أن تملك هوية البناء:

  • وصول قراءة للشيفرة المصدرية
  • وصول كتابة لتخزين الحزم (container registry أو S3 bucket)
  • لا وصول لبيئات الإنتاج

يجب أن تملك هوية النشر:

  • وصول قراءة لتخزين الحزم
  • وصول كتابة لهدف النشر المحدد
  • لا وصول للشيفرة المصدرية أو القدرة على تشغيل عمليات البناء

استخدم OIDC federation حيثما أمكن لإزالة بيانات الاعتماد طويلة الأمد تماماً. يدعم كل من GitHub Actions وGitLab CI رموز OIDC التي يمكن استبدالها ببيانات اعتماد مزود سحابي قصيرة الأمد:

# GitHub Actions OIDC with AWS
jobs:
  deploy:
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502
        with:
          role-to-assume: arn:aws:iam::123456789012:role/deploy-production
          aws-region: us-east-1
# GitLab CI OIDC with AWS
deploy:
  stage: deploy
  id_tokens:
    AWS_TOKEN:
      aud: https://gitlab.com
  script:
    - >
      STS_CREDENTIALS=$(aws sts assume-role-with-web-identity
      --role-arn arn:aws:iam::123456789012:role/deploy-production
      --web-identity-token $AWS_TOKEN
      --role-session-name "gitlab-ci-${CI_JOB_ID}")
    - export AWS_ACCESS_KEY_ID=$(echo $STS_CREDENTIALS | jq -r '.Credentials.AccessKeyId')
    - ./deploy.sh

خاتمة

لكل أنبوبة CI/CD نموذج ثقة. السؤال هو ما إذا كان نموذج الثقة هذا قد صُمم عمداً أم نشأ بشكل عرضي من الإعدادات الافتراضية والحلول السريعة.

يحدد نموذج التنفيذ الذي تختاره — المستضاف عبر SaaS أو ذاتي الاستضافة أو المُحتوى أو بدون خادم — الخصائص الأمنية الأساسية لأنبوبتك. لكن نموذج التنفيذ وحده لا يكفي. يجب تحديد الثقة صراحةً عند كل انتقال: من الشيفرة المصدرية إلى المُحفِّز، ومن المُحفِّز إلى التنفيذ، ومن التنفيذ إلى الأسرار، ومن البناء إلى النشر.

الأنماط المُغطاة في هذا الدليل — تنفيذ الأنبوبة المسمومة وإساءة استخدام المُشغّلات المشتركة والهويات ذات الامتيازات المفرطة ومراجع actions القابلة للتغيير وهويات البناء/النشر المدمجة — ليست نظرية. إنها التقنيات الفعلية المُستخدمة في هجمات سلسلة التوريد الواقعية، من اختراق SolarWinds إلى اختراق Codecov وما بعدهما.

ابدأ برسم خريطة حدود الثقة الحالية. حدد أين تُفترض الثقة بدلاً من التحقق منها. ثم طبّق تدابير التقوية بشكل منهجي: قيّد المُحفِّزات، وقلّل الأذونات، واعزل المُشغّلات، وثبّت التبعيات، وافصل الهويات. تعامل مع أنبوبة CI/CD الخاصة بك بنفس الصرامة التي تطبقها على بنية الإنتاج التحتية — لأنها في الممارسة العملية هي الباب الأمامي لبنية الإنتاج التحتية الخاصة بك.