مختبر: تشغيل Runners ذاتية الاستضافة مؤقتة لـ GitHub Actions باستخدام Actions Runner Controller

نظرة عامة

الـ runners المستضافة على GitHub مشتركة ومؤقتة (ephemeral) افتراضياً — فكل مهمة تحصل على جهاز افتراضي جديد يُدمَّر بعد اكتمال المهمة. أما الـ runners ذاتية الاستضافة، فهي دائمة ومشتركة عبر عمليات تشغيل الـ workflow. وهذا يخلق خطراً أمنياً كبيراً: إذ يمكن أن تتسرّب الأسرار والرموز وartifacts الـ build من مهمة إلى المهمة التالية. ويمكن لـ workflow مخترَق أن يسمّم بيئة الـ runner لكل المهام المستقبلية.

Actions Runner Controller (ARC) يحل هذه المشكلة. فـ ARC مشغّل (operator) أصيل في Kubernetes يمنحك runners مؤقتة، ذاتية التوسّع، قائمة على الحاويات، وذاتية الاستضافة. فكل مهمة تحصل على بود (pod) جديد يُدمَّر عند اكتمال المهمة — تماماً مثل الـ runners المستضافة على GitHub، لكنها تعمل على بنيتك التحتية الخاصة بأدواتك وسياساتك الشبكية.

في هذا المختبر العملي، ستقوم بـ:

  • نشر ARC على عنقود Kubernetes محلي
  • تهيئة مجموعات توسّع runner مؤقتة
  • إثبات العزل بين المهام (الفائدة الأمنية الجوهرية)
  • بناء صور runner مخصّصة
  • تطبيق عزل مجموعات الـ runner لفصل المسؤوليات
  • تهيئة التوسّع التلقائي (autoscaling)
  • تطبيق سياسات شبكية لتقييد وصول الـ runner إلى الشبكة

المتطلبات المسبقة

قبل بدء هذا المختبر، تأكّد من توفّر ما يلي:

  • عنقود Kuberneteskind، أو minikube، أو عنقود مُدار سحابياً (EKS، GKE، AKS)
  • Helm 3 — ثبّته من helm.sh
  • kubectl — مُهيَّأ للتواصل مع عنقودك
  • حساب GitHub — بصلاحية مسؤول (admin) على مستودع أو مؤسسة
  • GitHub App أو رمز وصول شخصي (PAT) — بنطاقات repo وadmin:org (لـ PAT) أو أذونات GitHub App مناسبة
  • Docker — لبناء صور runner مخصّصة (التمرين 4)

إعداد البيئة

سنستخدم kind (Kubernetes in Docker) لإنشاء عنقود محلي. وهذا يُبقي المختبر مكتفياً ذاتياً وسهل التنظيف.

إنشاء عنقود kind

kind create cluster --name arc-lab

تحقّق من أن العنقود يعمل:

kubectl cluster-info --context kind-arc-lab

إنشاء مستودع GitHub اختباري

أنشئ مستودعاً جديداً (مثل arc-lab-test) في حساب GitHub لديك. أضف ملف workflow بسيطاً في .github/workflows/test.yml:

name: ARC Test Workflow
on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Hello from GitHub-hosted runner
        run: echo "This runs on a GitHub-hosted runner"

ادفع (push) هذا إلى مستودعك. سنعدّله لاحقاً لاستهداف runners الخاصة بـ ARC.

التمرين 1: تثبيت ARC عبر Helm

يستخدم Actions Runner Controller v2 مخططات Helm لنشر مكوّنين: وحدة تحكم (controller) تدير دورة حياة بودات الـ runner، وواحدة أو أكثر من مجموعات توسّع الـ runner (runner scale sets) التي تسجّل لدى GitHub وتقبل المهام.

الخطوة 1: إضافة مستودع Helm

helm repo add actions-runner-controller \
  https://actions-runner-controller.github.io/actions-runner-controller
helm repo update

الخطوة 2: تهيئة المصادقة

يحتاج ARC إلى المصادقة مع واجهة API الخاصة بـ GitHub. ولديك خياران:

الخيار A: GitHub App (موصى به للإنتاج)

أنشئ GitHub App في إعدادات مؤسستك أو حسابك:

  1. اذهب إلى Settings → Developer settings → GitHub Apps → New GitHub App
  2. اضبط الأذونات التالية:
    • Repository: Actions (قراءة)، Administration (قراءة/كتابة)، Metadata (قراءة)
    • Organization: Self-hosted runners (قراءة/كتابة)
  3. ولّد مفتاحاً خاصاً ونزّله
  4. ثبّت التطبيق (App) على مؤسستك أو مستودعك
  5. سجّل قيمة App ID وقيمة Installation ID

الخيار B: رمز وصول شخصي (أبسط للمختبرات)

أنشئ PAT (كلاسيكي) بنطاقات repo وadmin:org، أو PAT دقيقاً (fine-grained) بأذونات Actions وAdministration. وفي هذا المختبر، سنستخدم PAT للتبسيط.

الخطوة 3: تثبيت وحدة تحكم ARC

helm install arc \
  actions-runner-controller/gha-runner-scale-set-controller \
  --namespace arc-systems \
  --create-namespace

تحقّق من أن وحدة التحكم تعمل:

kubectl get pods -n arc-systems

ينبغي أن ترى مخرجات مشابهة لـ:

NAME                                     READY   STATUS    RESTARTS   AGE
arc-gha-runner-scale-set-controller-xxx  1/1     Running   0          30s

الخطوة 4: تثبيت مجموعة توسّع runner

الآن انشر مجموعة توسّع runner تسجّل لدى مستودع GitHub لديك:

helm install arc-runner-set \
  actions-runner-controller/gha-runner-scale-set \
  --namespace arc-runners \
  --create-namespace \
  --set githubConfigUrl="https://github.com/<org>/<repo>" \
  --set githubConfigSecret.github_token="<PAT>"

استبدل <org>/<repo> بمسار مستودعك و<PAT> برمز الوصول الشخصي لديك.

تحقّق من مجموعة توسّع الـ runner:

kubectl get pods -n arc-runners

عند هذه النقطة، قد لا تكون هناك بودات runner بعد — فـ ARC يستخدم نموذج التوسّع إلى الصفر (scale-to-zero). فلا تُنشأ البودات إلا عند وجود مهام في طابور الانتظار.

الخطوة 5: التحقق في GitHub

انتقل إلى مستودعك في GitHub: Settings → Actions → Runners. ينبغي أن ترى مجموعة توسّع الـ runner مدرجة باسم arc-runner-set. وتُظهر الحالة أنها جاهزة لقبول المهام.

التمرين 2: تشغيل workflow على runners الخاصة بـ ARC

الآن حدّث الـ workflow الاختباري ليستهدف مجموعة توسّع runner الخاصة بـ ARC بدلاً من الـ runners المستضافة على GitHub.

الخطوة 1: تحديث الـ workflow

عدّل .github/workflows/test.yml لاستخدام وسم runner الخاص بـ ARC:

name: ARC Test Workflow
on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  test:
    runs-on: arc-runner-set
    steps:
      - name: Hello from ARC runner
        run: |
          echo "This runs on an ephemeral ARC runner!"
          echo "Hostname: $(hostname)"
          echo "Runner OS: $(uname -a)"
      - name: Show environment
        run: env | sort

التغيير الأساسي هو runs-on: arc-runner-set — وهذا يطابق اسم إصدار Helm لمجموعة توسّع الـ runner.

الخطوة 2: إطلاق الـ workflow

ادفع ملف الـ workflow المُحدَّث أو استخدم زر “Run workflow” (workflow_dispatch) في واجهة GitHub Actions.

الخطوة 3: مراقبة بود الـ runner

راقب مساحة الأسماء arc-runners أثناء تشغيل الـ workflow:

kubectl get pods -n arc-runners -w

سترى بوداً يُنشأ للمهمة:

NAME                          READY   STATUS    RESTARTS   AGE
arc-runner-set-xxxxx-runner   1/1     Running   0          5s

بعد اكتمال المهمة، يُنهى البود ويُزال:

NAME                          READY   STATUS      RESTARTS   AGE
arc-runner-set-xxxxx-runner   0/1     Completed   0          45s

شغّل kubectl get pods -n arc-runners مجدداً — لقد اختفى البود. هذا هو النموذج المؤقت: فكل مهمة تحصل على حاوية جديدة، وتُدمَّر الحاوية عند انتهاء المهمة. ولا توجد استمرارية للحالة بين المهام.

التمرين 3: إثبات الأمان المؤقت

يبرهن هذا التمرين على الفائدة الأمنية الجوهرية للـ runners المؤقتة: غياب التلوث بين المهام.

الخطوة 1: إنشاء workflow يكتب بيانات حساسة

أنشئ .github/workflows/ephemeral-test.yml:

name: Ephemeral Security Test
on: workflow_dispatch

jobs:
  write-secret:
    runs-on: arc-runner-set
    steps:
      - name: Write sensitive data
        run: |
          echo "SECRET_API_KEY=sk-prod-abc123xyz" > /tmp/secret-data
          echo "DB_PASSWORD=super-secret-password" >> /tmp/secret-data
          echo "Written sensitive data to /tmp/secret-data"
          cat /tmp/secret-data

  read-secret:
    runs-on: arc-runner-set
    needs: write-secret
    steps:
      - name: Attempt to read previous job data
        run: |
          echo "Checking if /tmp/secret-data exists from previous job..."
          if [ -f /tmp/secret-data ]; then
            echo "SECURITY RISK: Found data from previous job!"
            cat /tmp/secret-data
          else
            echo "SECURE: /tmp/secret-data does not exist."
            echo "Each job gets a fresh container — no cross-job contamination."
          fi

الخطوة 2: تشغيل الـ workflow

أطلق الـ workflow عبر workflow_dispatch. تكتب المهمة الأولى (write-secret) بيانات حساسة في /tmp/secret-data. وتعمل المهمة الثانية (read-secret) في بود جديد وتحاول قراءة ذلك الملف.

الخطوة 3: التحقق من النتائج

في سجلات GitHub Actions، سترى:

  • مهمة write-secret: تكتب الملف بنجاح وتطبع محتوياته
  • مهمة read-secret: الملف غير موجود — تُظهر المخرجات SECURE: /tmp/secret-data does not exist.

عملت كل مهمة في بود منفصل أُنشئ حديثاً. وعندما دُمِّر بود write-secret، دُمِّرت معه جميع البيانات — بما في ذلك الملف الحساس.

لماذا يهم هذا

على runner ذاتي الاستضافة دائم، سيظل ملف /tmp/secret-data على القرص عند تشغيل المهمة الثانية. ويمكن لـ workflow خبيث في طلب دمج (pull request) أن يقرأ أسراراً أو رموزاً أو بيانات اعتماد تركتها المهام السابقة. ومع الـ runners المؤقتة، يُقضى على متجه الهجوم هذا.

التمرين 4: صور runner مخصّصة

تستخدم runners الخاصة بـ ARC صورة حاوية أساسية. وللاستخدام الواقعي، تحتاج إلى تخصيص هذه الصورة لتضمين أدوات الـ build لديك.

الخطوة 1: إنشاء Dockerfile مخصّص

أنشئ ملف Dockerfile للـ runner المخصّص لديك:

FROM ghcr.io/actions/actions-runner:latest

USER root

# Install build tools
RUN apt-get update && apt-get install -y \
    curl \
    wget \
    git \
    jq \
    unzip \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Install Go
RUN wget -q https://go.dev/dl/go1.22.4.linux-amd64.tar.gz \
    && tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz \
    && rm go1.22.4.linux-amd64.tar.gz
ENV PATH="$PATH:/usr/local/go/bin"

# Install cosign
RUN curl -sSL -o /usr/local/bin/cosign \
    https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 \
    && chmod +x /usr/local/bin/cosign

# Install Docker CLI (for Docker-in-Docker workflows)
RUN curl -fsSL https://get.docker.com | sh

USER runner

الخطوة 2: بناء الصورة ودفعها

# Build the image
docker build -t ghcr.io/<org>/custom-runner:latest .

# Authenticate to GitHub Container Registry
echo "<PAT>" | docker login ghcr.io -u <username> --password-stdin

# Push the image
docker push ghcr.io/<org>/custom-runner:latest

الخطوة 3: تهيئة ARC لاستخدام الصورة المخصّصة

أنشئ ملف قيم custom-runner-values.yaml:

githubConfigUrl: "https://github.com/<org>/<repo>"
githubConfigSecret:
  github_token: "<PAT>"

template:
  spec:
    containers:
      - name: runner
        image: ghcr.io/<org>/custom-runner:latest
        command: ["/home/runner/run.sh"]
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            cpu: "2"
            memory: "2Gi"

حدّث مجموعة توسّع الـ runner بالصورة المخصّصة:

helm upgrade arc-runner-set \
  actions-runner-controller/gha-runner-scale-set \
  --namespace arc-runners \
  -f custom-runner-values.yaml

الخطوة 4: التحقق من الأدوات المخصّصة

أنشئ workflow يستخدم الأدوات المخصّصة:

name: Custom Runner Tools Test
on: workflow_dispatch

jobs:
  verify-tools:
    runs-on: arc-runner-set
    steps:
      - name: Verify Go
        run: go version
      - name: Verify cosign
        run: cosign version
      - name: Verify Docker CLI
        run: docker --version

الفائدة الأمنية: ببناء صورة الـ runner الخاصة بك، تتحكم بدقة في الأدوات والتبعيات الموجودة في بيئة الـ build. فلا توجد ملفات ثنائية غير متوقعة، ولا برمجيات مثبّتة مسبقاً لم توافق عليها، ويمكنك تثبيت كل أداة على إصدار محدّد. ويمكنك أيضاً فحص الصورة بحثاً عن الثغرات قبل نشرها.

التمرين 5: عزل مجموعات الـ runner

للـ workflows المختلفة مستويات ثقة مختلفة. فالتحقق من طلبات الدمج (pull request validation) لا ينبغي أن يملك وصولاً إلى أسرار الإنتاج. وworkflows النشر تحتاج إلى الأسرار لكن ينبغي أن تعمل فقط من الفرع الرئيسي. ويتيح لك ARC تطبيق هذا الفصل بإنشاء مجموعات توسّع runner متمايزة بوسوم وتهيئات مختلفة.

الخطوة 1: إنشاء مجموعة توسّع runner للتحقق من الـ PR

أنشئ pr-runner-values.yaml:

githubConfigUrl: "https://github.com/<org>/<repo>"
githubConfigSecret:
  github_token: "<PAT>"

template:
  spec:
    containers:
      - name: runner
        image: ghcr.io/<org>/custom-runner:latest
        command: ["/home/runner/run.sh"]
        env:
          - name: RUNNER_GROUP
            value: "pr-validation"
        resources:
          requests:
            cpu: "250m"
            memory: "256Mi"
          limits:
            cpu: "1"
            memory: "1Gi"
helm install arc-runner-pr \
  actions-runner-controller/gha-runner-scale-set \
  --namespace arc-runners \
  -f pr-runner-values.yaml

الخطوة 2: إنشاء مجموعة توسّع runner للنشر

أنشئ deploy-runner-values.yaml:

githubConfigUrl: "https://github.com/<org>/<repo>"
githubConfigSecret:
  github_token: "<PAT>"

template:
  spec:
    containers:
      - name: runner
        image: ghcr.io/<org>/custom-runner:latest
        command: ["/home/runner/run.sh"]
        env:
          - name: RUNNER_GROUP
            value: "deployment"
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            cpu: "2"
            memory: "2Gi"
    serviceAccountName: deploy-runner-sa
    nodeSelector:
      runner-type: deployment
helm install arc-runner-deploy \
  actions-runner-controller/gha-runner-scale-set \
  --namespace arc-runners \
  -f deploy-runner-values.yaml

الخطوة 3: تهيئة الـ workflows للعزل

استخدم وسوم runner مختلفة بناءً على مُشغِّل الـ workflow:

name: CI/CD Pipeline
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  validate:
    if: github.event_name == 'pull_request'
    runs-on: arc-runner-pr
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: make test
      - name: Run linter
        run: make lint

  deploy:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: arc-runner-deploy
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to production
        run: make deploy
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

يطبّق هذا فصل المسؤوليات على مستوى الـ runner. فمهام التحقق من الـ PR تعمل على runners لا تملك وصولاً إلى أسرار النشر أو أجزاء الشبكة المميزة. أما مهام النشر فتعمل على مجموعة منفصلة من الـ runners تملك بيانات الاعتماد والوصول الشبكي اللازمين، لكنها تُطلَق فقط عند الدفع (push) إلى الفرع main.

التمرين 6: التوسّع التلقائي

يدعم ARC التوسّع التلقائي بشكل أصيل. فبودات الـ runner تُنشأ عند الطلب وتُدمَّر عند الخمول. ويمكنك تهيئة الحدّ الأدنى والأقصى من النسخ (replicas) للتحكم في التكلفة وسرعة الاستجابة.

الخطوة 1: تهيئة معاملات التوسّع التلقائي

حدّث ملف قيم مجموعة توسّع الـ runner لتضمين معاملات التوسّع:

githubConfigUrl: "https://github.com/<org>/<repo>"
githubConfigSecret:
  github_token: "<PAT>"

minRunners: 0
maxRunners: 10

template:
  spec:
    containers:
      - name: runner
        image: ghcr.io/actions/actions-runner:latest
        command: ["/home/runner/run.sh"]
helm upgrade arc-runner-set \
  actions-runner-controller/gha-runner-scale-set \
  --namespace arc-runners \
  -f autoscale-values.yaml

الخطوة 2: توليد حمل

أنشئ workflow يُطلق عدة مهام متوازية:

name: Autoscale Test
on: workflow_dispatch

jobs:
  parallel-job:
    runs-on: arc-runner-set
    strategy:
      matrix:
        id: [1, 2, 3, 4, 5]
    steps:
      - name: Simulate work
        run: |
          echo "Job ${{ matrix.id }} running on $(hostname)"
          sleep 60

أطلق هذا الـ workflow وراقب توسّع البودات:

kubectl get pods -n arc-runners -w

سترى خمسة بودات تُنشأ — واحداً لكل مهمة في المصفوفة (matrix):

NAME                              READY   STATUS    RESTARTS   AGE
arc-runner-set-abcde-runner       1/1     Running   0          5s
arc-runner-set-fghij-runner       1/1     Running   0          5s
arc-runner-set-klmno-runner       1/1     Running   0          5s
arc-runner-set-pqrst-runner       1/1     Running   0          5s
arc-runner-set-uvwxy-runner       1/1     Running   0          5s

بعد اكتمال المهام (60 ثانية)، تُنهى جميع البودات. وتعود مساحة الأسماء إلى صفر بودات.

الخطوة 3: تهيئة تأخير التقليص

لتحسين التكلفة، قد ترغب في أن تبقى البودات دافئة (warm) لفترة قصيرة بعد اكتمال المهمة. وهذا يتجنّب زمن بدء التشغيل البارد (cold-start) للأحمال المتقطعة. وسلوك التوسّع إلى الصفر في ARC هو الخيار الافتراضي والأكثر أماناً. وإذا احتجت إلى runners دافئة، فأبقِ النافذة قصيرة (أقل من 5 دقائق) وتأكّد من أن الوضع المؤقت لا يزال مفروضاً.

التمرين 7: السياسات الشبكية للـ runners

تتيح لك NetworkPolicies في Kubernetes تقييد الوصول الشبكي لبودات الـ runner. وهذا دفاع بالغ الأهمية ضد تسريب البيانات من عمليات build مخترَقة.

الخطوة 1: إنشاء NetworkPolicy

طبّق NetworkPolicy التالية على مساحة الأسماء arc-runners:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: runner-egress-policy
  namespace: arc-runners
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    # Allow DNS resolution
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    # Allow GitHub API and Actions services
    - to:
        - ipBlock:
            cidr: 140.82.112.0/20
        - ipBlock:
            cidr: 143.55.64.0/20
        - ipBlock:
            cidr: 185.199.108.0/22
        - ipBlock:
            cidr: 4.0.0.0/8
      ports:
        - protocol: TCP
          port: 443
    # Allow your container registry (example: ghcr.io)
    - to:
        - ipBlock:
            cidr: 140.82.112.0/20
      ports:
        - protocol: TCP
          port: 443
    # Allow your artifact storage (replace with your CIDR)
    # - to:
    #     - ipBlock:
    #         cidr: 10.0.0.0/8
    #   ports:
    #     - protocol: TCP
    #       port: 443
kubectl apply -f runner-network-policy.yaml

ملاحظة: تنشر GitHub نطاقات IP الخاصة بها على https://api.github.com/meta. استخدم نطاقات actions وapi. والقيم أعلاه أمثلة — تحقّق من النطاقات الحالية وحدّثها وفقاً لذلك.

الخطوة 2: اختبار NetworkPolicy

أنشئ workflow يحاول الوصول إلى رابط خارجي:

name: Network Policy Test
on: workflow_dispatch

jobs:
  test-network:
    runs-on: arc-runner-set
    steps:
      - name: Test GitHub API (should work)
        run: curl -s -o /dev/null -w "%{http_code}" https://api.github.com

      - name: Test external URL (should be blocked)
        run: |
          if curl -s --connect-timeout 5 https://evil-exfiltration-server.example.com; then
            echo "FAIL: External access was allowed"
            exit 1
          else
            echo "PASS: External access was blocked by NetworkPolicy"
          fi

عند تشغيل هذا الـ workflow:

  • ينجح طلب واجهة API الخاصة بـ GitHub (HTTP 200) لأن NetworkPolicy تسمح بالحركة إلى نطاقات IP الخاصة بـ GitHub.
  • ينتهي طلب الرابط الخارجي بانتهاء المهلة ويفشل لأنه ليس في قائمة الحركة الصادرة المسموح بها.

يمنع هذا build مخترَقاً من تسريب الشيفرة المصدرية أو الأسرار أو artifacts الـ build إلى خادم يتحكم فيه المهاجم. وحتى لو نفّذت تبعية خبيثة شيفرة عشوائية أثناء الـ build، فلن تتمكن من الاتصال بالخارج.

التنظيف

أزل جميع الموارد التي أُنشئت أثناء هذا المختبر:

# Delete Helm releases
helm uninstall arc-runner-set -n arc-runners
helm uninstall arc-runner-pr -n arc-runners
helm uninstall arc-runner-deploy -n arc-runners
helm uninstall arc -n arc-systems

# Delete namespaces
kubectl delete namespace arc-runners
kubectl delete namespace arc-systems

# Delete the kind cluster
kind delete cluster --name arc-lab

إذا أنشأت GitHub App لهذا المختبر، فيمكنك حذفه من Settings → Developer settings → GitHub Apps. وألغِ أي رموز PAT أنشأتها.

أبرز الخلاصات

  • الـ runners المؤقتة تقضي على التلوث بين المهام. فكل مهمة تحصل على حاوية جديدة — وتُدمَّر الأسرار والرموز وartifacts الـ build عند اكتمال المهمة.
  • يوفّر ARC مزايا الـ runner ذاتي الاستضافة دون المخاطر الأمنية. فتحصل على أدوات مخصّصة، ووصول شبكي خاص، وتحكم في التكلفة، مع الحفاظ على نموذج الأمان المؤقت.
  • صور الـ runner المخصّصة تمنحك تحكماً كاملاً في بيئة الـ build. ثبّت إصدارات الأدوات، وافحص بحثاً عن الثغرات، واقضِ على خطر سلسلة التوريد الناجم عن البرمجيات المثبّتة مسبقاً.
  • عزل مجموعات الـ runner يطبّق فصل المسؤوليات. فworkflows التحقق من الـ PR والنشر تعمل على مجموعات runner منفصلة بامتيازات ووصول شبكي مختلفين.
  • السياسات الشبكية طبقة دفاع بالغة الأهمية. فتقييد الحركة الصادرة للـ runner يمنع تسريب البيانات حتى لو اختُرقت خطوة build.
  • التوسّع التلقائي إلى الصفر يقلّل التكلفة وسطح الهجوم. فبودات الـ runner لا توجد إلا طوال مدة المهمة — فلا توجد بنية تحتية دائمة يجب صيانتها أو تأمينها.

الخطوات التالية

واصل تعزيز وضعيتك الأمنية في CI/CD مع هذه الأدلة ذات الصلة: