cloud-devops

GitHub Actions OIDC إلى Google Cloud: عمليات نشر بدون مفاتيح (2026)

٥ يونيو ٢٠٢٦

GitHub Actions OIDC to Google Cloud: Keyless Deploys (2026)

للمصادقة على إجراءات GitHub Actions مع Google Cloud بدون مفتاح حساب خدمة، قم بإنشاء Workload Identity Pool ومزود OIDC يثق في https://token.actions.githubusercontent.com، وقم بتقييد الدخول بشرط سمة (attribute condition)، وتبادل توكن OIDC الخاص بمسار العمل عبر google-GitHub-actions/auth@v3. يربط هذا البرنامج التعليمي العملية من البداية إلى النهاية في نشر Cloud Run.

ملخص

ستقوم بتهيئة Workload Identity Federation (WIF) في Google Cloud بحيث يمكن لمسار عمل GitHub Actions النشر في Cloud Run بدون بيانات اعتماد مخزنة على الإطلاق — لا يوجد مفتاح JSON في أسرار المستودع الخاص بك، ولا شيء يتطلب التدوير، ولا شيء للتسريب. الإعداد بالكامل يتكون من عشرة أوامر gcloud بالإضافة إلى ملف مسار عمل واحد يستخدم google-GitHub-actions/auth@v31 و google-GitHub-actions/deploy-cloudrun@v32، ويستغرق حوالي 20 دقيقة. ستقوم أيضًا بتأمين تكوين الثقة الخاص بك للمستقبل للتعامل مع مطالبات الموضوع غير القابلة للتغيير (immutable subject claims) من GitHub، والتي ستصبح الافتراضية للمستودعات الجديدة في 18 يونيو 20263.

ما ستتعلمه

  • كيف يعمل تبادل توكن OIDC الخاص بـ GitHub Actions مع Google Cloud (وكيف يختلف عن تدفق AWS)
  • كيفية إنشاء Workload Identity Pool ومزود مع شروط سمات آمنة
  • الفرق بين Workload Identity Federation المباشر وانتحال شخصية حساب الخدمة — وأيهما يحتاجه نشر Cloud Run
  • أدوار IAM المحددة التي يحتاجها GitHub Actions للنشر في Cloud Run
  • ملف YAML لمسار عمل النشر بدون مفاتيح، خطوة بخطوة
  • كيفية التحقق من النشر وتشديد سياسة الثقة لتغيير المطالبة غير القابلة للتغيير في 18 يونيو 2026
  • استكشاف أخطاء خمسة أوضاع فشل موثقة وإصلاحها

كيف يعمل تبادل التوكن

عندما يتم تشغيل وظيفة مسار عمل بإذن id-token: write، يقوم GitHub بإصدار توكن OIDC قصير العمر تحدد مطالباته مسار العمل: repository، و repository_owner، و ref، ومطالبة sub (الموضوع) مركبة تكون افتراضيًا repo:OWNER/REPO:ref:refs/heads/BRANCH4. تتحقق خدمة Security Token Service في Google Cloud من هذا التوكن مقابل جهة إصدار GitHub (https://token.actions.githubusercontent.com)، وتتحقق منه مقابل شرط السمة الخاص بالمجمع (pool) الخاص بك، وتبادله ببيانات اعتماد Google قصيرة العمر. توكن OIDC الخاص بـ GitHub نفسه قصير العمر — يشير ملف README الخاص بـ auth إلى أنه ينتهي في غضون 5 دقائق تقريبًا1.

إذا اتبعت البرنامج التعليمي الخاص بنا AWS OIDC keyless deploy tutorial، فسيكون الشكل مألوفًا — لكن GCP تختلف في طريقتين: تعيش الثقة في زوج موارد مخصص pool + provider بدلاً من سياسة ثقة دور IAM، وتقدم GCP وضعين متميزين (Direct WIF وانتحال شخصية حساب الخدمة) بينما تمتلك AWS افتراض الدور فقط.

sequenceDiagram
    participant GH as GitHub Actions job
    participant IdP as token.actions.githubusercontent.com
    participant STS as Google Cloud STS
    participant IAMC as IAM Credentials API
    participant CR as Cloud Run Admin API
    GH->>IdP: Request OIDC token (id-token: write)
    IdP-->>GH: Signed JWT (sub, repository, ref claims)
    GH->>STS: Exchange JWT (pool/provider validates + attribute condition)
    STS-->>GH: Federated token
    GH->>IAMC: Impersonate deployer SA (roles/iam.workloadIdentityUser)
    IAMC-->>GH: OAuth 2.0 access token
    GH->>CR: gcloud run deploy (roles/run.admin)

Direct WIF مقابل انتحال شخصية حساب الخدمة

يدعم إجراء auth وضعين لـ WIF، ويصنف ملف README الرسمي Direct WIF كـ "(مفضل)"1:

Direct WIFWIF عبر حساب خدمة
مورد وسيطلا يوجد — يحصل المجمع (pool) على منح IAM مباشرةيتم انتحال شخصية حساب خدمة
كيفية اختيارهحذف مدخل service_accountتوفير مدخل service_account
عمر التوكنتوكن موحد (Federated)، بحد أقصى 10 دقائقتوكن وصول OAuth 2.0؛ توكنات الوصول التي يتم إنشاؤها تكون افتراضيًا ساعة واحدة (3600 ثانية)
التغطيةلا تقبل جميع موارد GCP هويات principalSetيعمل مع أي API يمكن لحساب الخدمة استدعاؤه
إصدار توكن OAuth/IDغير مدعوممدعوم

يعد Direct WIF هو الخيار الافتراضي الأنظف للموارد التي تدعم الهويات الموحدة (يوضح ملف README استخدام Secret Manager). في هذا البرنامج التعليمي، نستخدم WIF عبر حساب خدمة، لأن هذا هو نموذج التفويض الذي يوثقه إجراء deploy-cloudrun: حساب خدمة مع roles/run.admin بالإضافة إلى Service Account User على حساب خدمة Compute Engine الافتراضي2. ستحصل على مصادقة بدون مفاتيح في كلتا الحالتين — حساب الخدمة هنا ليس له مفتاح مصدر؛ لا يمكن انتحال شخصيته إلا من خلال مسارات العمل التي تجتاز شرط السمة الخاص بك.

المتطلبات الأساسية

  • مشروع Google Cloud مع تمكين الفواتير، وتثبيت gcloud CLI والمصادقة عليه (gcloud auth login) كمالك مشروع أو محرر
  • مستودع GitHub (على GitHub.com — يستخدم GitHub Enterprise Server عنوان URL مختلف لجهة الإصدار، راجع استكشاف الأخطاء وإصلاحها)
  • إصدارات الإجراءات المثبتة المستخدمة أدناه: actions/checkout@v6.0.2 (2026-01-09)، google-GitHub-actions/auth@v3.0.0 (2025-08-28)، google-GitHub-actions/deploy-cloudrun@v3.0.1 (2025-09-03)5

قم بتعيين متغيرات الصدفة (shell variables) التي تستخدمها كل خطوة:

export PROJECT_ID="your-project-id"
export GITHUB_ORG="your-GitHub-org-or-username"
export REPO="your-GitHub-org-or-username/your-repo"

الخطوة 1 — تمكين واجهات برمجة التطبيقات المطلوبة

يحتاج Workload Identity Federation إلى واجهات برمجة تطبيقات IAM و Security Token Service و IAM Credentials؛ ويحتاج هدف النشر إلى Cloud Run6:

gcloud services enable \
  iam.googleapis.com \
  sts.googleapis.com \
  iamcredentials.googleapis.com \
  run.googleapis.com \
  --project="${PROJECT_ID}"

المخرجات المتوقعة: Operation "operations/..." finished successfully.

الخطوة 2 — إنشاء Workload Identity Pool

gcloud iam workload-identity-pools create "GitHub" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --display-name="GitHub Actions Pool"

التقط اسم المورد الكامل للمجمع — ستحتاج إليه لربط IAM:

export WORKLOAD_IDENTITY_POOL_ID=$(gcloud iam workload-identity-pools describe "GitHub" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --format="value(name)")
echo "${WORKLOAD_IDENTITY_POOL_ID}"

المخرجات المتوقعة (لاحظ رقم المشروع، وليس معرف المشروع):

projects/123456789/locations/global/workloadIdentityPools/GitHub

الخطوة 3 — إنشاء مزود OIDC مع شرط سمة

هذا هو مرساة الثقة. هناك قاعدتان من ملف README الرسمي لـ auth غير قابلة للتفاوض: أضف دائمًا شرط سمة يقيد الدخول إلى المجمع، و قم بتعيين كل مطالبة تريد التأكيد عليها — لا يمكنك الرجوع إلى assertion.repository في شرط أو ربط IAM ما لم يتم تعيينه أولاً1.

gcloud iam workload-identity-pools providers create-oidc "GitHub-provider" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --workload-identity-pool="GitHub" \
  --display-name="GitHub repo provider" \
  --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
  --attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
  --issuer-uri="https://token.actions.githubusercontent.com"

شرط السمة هو تعبير CEL يتم تقييمه مقابل التوكن الوارد. التقييد على repository_owner يمنع أي مستودع خارج مؤسستك من دخول المجمع — ثم تضيق روابط IAM الوصول بشكل أكبر لكل مستودع. هناك مشكلتان: الشروط حساسة لحالة الأحرف في جانب Google حتى لو كان GitHub يعامل الأسماء بغض النظر عن حالة الأحرف، ويتم رفض google.subject المعين الذي يزيد طوله عن 127 بايت (أسماء المستودعات + الفروع الطويلة) بواسطة IAM7.

استخرج اسم مورد المزود لمسار العمل:

gcloud iam workload-identity-pools providers describe "GitHub-provider" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --workload-identity-pool="GitHub" \
  --format="value(name)"

المخرجات المتوقعة:

projects/123456789/locations/global/workloadIdentityPools/GitHub/providers/GitHub-provider

هذا المسار الكامل — مع رقم المشروع — هو قيمة workload_identity_provider الخاصة بك. تمرير معرف المشروع (ID) بدلاً من الرقم هو وضع فشل يشير إليه دليل استكشاف الأخطاء وإصلاحها الرسمي صراحةً7.

الخطوة 4 — إنشاء حساب خدمة النشر ومنح الأدوار

أنشئ حساب خدمة مخصص للنشر (لن يتم إنشاء مفتاح له أبداً):

gcloud iam service-accounts create "GitHub-deployer" \
  --project="${PROJECT_ID}" \
  --display-name="GitHub Actions Cloud Run deployer"

امنحه ما توثقه أداة deploy-cloudrun: دور Cloud Run Admin على المشروع، ودور Service Account User على حساب خدمة Compute Engine الافتراضي (الهوية التي تعمل بها إصدارات Cloud Run الجديدة في وقت التشغيل)2:

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
  --member="serviceAccount:GitHub-deployer@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role="roles/run.admin"

export PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")

gcloud iam service-accounts add-iam-policy-binding \
  "${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --project="${PROJECT_ID}" \
  --member="serviceAccount:GitHub-deployer@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role="roles/iam.serviceAccountUser"

الخطوة 5 — السماح للمستودع الخاص بك بانتحال شخصية الناشر

اربط الهوية الموحدة للمجمع (pool) لـ مستودعك فقط بحساب الخدمة باستخدام roles/iam.workloadIdentityUser1:

gcloud iam service-accounts add-iam-policy-binding \
  "GitHub-deployer@${PROJECT_ID}.iam.gserviceaccount.com" \
  --project="${PROJECT_ID}" \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"

العضو principalSet://...attribute.repository/... يطابق كل تشغيل لسير العمل (workflow) الذي تساوي مطالبة repository في رمزه المميز (token) القيمة your-org/your-repo — وهذا هو بالضبط السبب في أن الخطوة 3 كان عليها تعيين attribute.repository=assertion.repository أولاً.

الدفاع الآن يعتمد على طبقتين: شرط السمة (attribute condition) الذي يحدد أي الرموز المميزة تدخل المجمع (مؤسستك)، وهذا الربط في IAM الذي يحدد أي من تلك الهويات يمكنها انتحال شخصية الناشر (مستودع واحد).

الخطوة 6 — سير عمل النشر بدون مفتاح

أنشئ .GitHub/workflows/deploy.yml. استبدل قيمة workload_identity_provider باسم مورد المزود من الخطوة 3:

name: deploy-cloud-run

on:
  push:
    branches: ['main']

jobs:
  deploy:
    runs-on: 'ubuntu-latest'

    permissions:
      contents: 'read'
      id-token: 'write'

    steps:
      - uses: 'actions/checkout@v6.0.2'

      - id: 'auth'
        uses: 'google-GitHub-actions/auth@v3.0.0'
        with:
          project_id: 'your-project-id'
          workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/GitHub/providers/GitHub-provider'
          service_account: 'GitHub-deployer@your-project-id.iam.gserviceaccount.com'

      - id: 'deploy'
        uses: 'google-GitHub-actions/deploy-cloudrun@v3.0.1'
        with:
          service: 'hello-keyless'
          region: 'us-central1'
          image: 'us-Docker.pkg.dev/cloudrun/container/hello:latest'

      - name: 'Show service URL'
        run: 'echo "Deployed to ${{ steps.deploy.outputs.url }}"'

ثلاثة تفاصيل يؤدي إهمالها إلى فشل عمليات النشر:

  1. id-token: write إلزامي. بدونه، لن يقوم GitHub أبداً بحقن رمز OIDC المميز وستفشل خطوة المصادقة. لاحظ أن إضافة كتلة permissions تزيل الإعدادات الافتراضية، لذا يجب إعادة ذكر contents: read لخطوة checkout1.
  2. يجب أن تأتي actions/checkout قبل auth. تقوم الأداة بكتابة ملف بيانات اعتماد في $GITHUB_WORKSPACE للخطوات اللاحقة؛ ملف README صريح في أن حذف checkout أو وضعه بعد auth يترك الخطوات المستقبلية غير قادرة على المصادقة1.
  3. أضف gha-creds-*.json إلى .gitignore و .dockerignore. يوجد ملف بيانات الاعتماد المصدر في مساحة العمل أثناء الوظيفة (يتم حذفه تلقائياً عند انتهاء الوظيفة)، لذا فإن أي شيء يحزم مساحة العمل — مثل بناء Docker أو أصل إصدار (release artifact) — يمكن أن يدرجه بالخطأ1.

صورة العرض us-Docker.pkg.dev/cloudrun/container/hello:latest هي حاوية عينة من Google، تمت الإشارة إليها تماماً كما يفعل ملف README الرسمي لـ deploy-cloudrun2؛ في خط أنابيب حقيقي، ستستبدلها بوسم الصورة المثبت الخاص بك.

قم بعمل commit، و push إلى main، وشاهد التشغيل. ملاحظة تشغيلية واحدة: تغييرات المجمع والمزود و IAM تتسم بالاتساق النهائي (eventually consistent) ويمكن أن تستغرق ما يصل إلى 5 دقائق للانتشار — إذا فشل التشغيل الأول بخطأ في الأذونات، انتظر خمس دقائق وأعد التشغيل قبل تغيير أي شيء1.

التحقق

خدمة Cloud Run الجديدة خاصة بشكل افتراضي — عمليات النشر الجديدة تكون خاصة تلقائياً، وتوصية منتج Cloud Run هي ألا تقوم أنظمة CI/CD بضبط أو تغيير إعداد unauthenticated-invocations2. تحقق من ذلك من جهازك الخاص باستخدام رمز هوية مميز (يحتاج حساب gcloud الخاص بك إلى run.routes.invoke، والذي يتضمنه دور Cloud Run Admin)8:

curl -s -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  "$(gcloud run services describe hello-keyless \
      --project="${PROJECT_ID}" \
      --region='us-central1' \
      --format='value(status.url)')"

المتوقع: الصفحة الرئيسية للتطبيق العينة (HTTP 200). والدليل على نجاح التمرين بأكمله — تحقق من Settings ← Secrets في المستودع: لا توجد بيانات اعتماد Google Cloud مخزنة في أي مكان.

التحصين لتاريخ 18 يونيو 2026: مطالبات الموضوع غير القابلة للتغيير

أعلنت GitHub عن مطالبات موضوع (subject claims) غير قابلة للتغيير لرموز OIDC المميزة لـ Actions في 23 أبريل 2026: بدلاً من repo:octocat/my-repo:ref:refs/heads/main، تكتسب مطالبة sub معرفات رقمية غير قابلة للتغيير، مثل repo:octocat-123456/my-repo-456789:ref:refs/heads/main. المستودعات التي تم إنشاؤها بعد 18 يونيو 2026 تستخدم التنسيق الجديد تلقائياً، وكذلك المستودعات الحالية بعد إعادة تسميتها أو نقلها؛ كل شيء آخر يحتفظ بالتنسيق القديم ما لم تختر التغيير عبر إعدادات OIDC للمستودع أو المؤسسة3.

هذا يغلق ثغرة هجوم حقيقية: إذا تم حذف اسم مؤسسة أو مستودع وإعادة تسجيله، يمكن للمالك الجديد سابقاً إصدار رموز مميزة بنفس الـ sub ووراثة ثقتك السحابية.

إعدادنا محمي إلى حد كبير لأن ربط IAM يطابق attribute.repository (الذي يظل owner/repo) وشرط السمة يطابق repository_owner — ولا يدمج أي منهما تنسيق sub. ولكن إذا قمت بتشديد الروابط القائمة على google.subject (على سبيل المثال، principal://...subject/repo:org/repo:ref:refs/heads/main لتثبيت الفرع)، فإن المستودع الذي تم إنشاؤه أو إعادة تسميته بعد 18 يونيو سيحمل المالك/المستودع الملحق بالمعرف في sub وسيتوقف عن مطابقة النمط القديم. بالنسبة للقيود على مستوى الفرع، يفضل التعيين والتحقق من assertion.ref بالإضافة إلى assertion.repository في شرط السمة بدلاً من مطابقة السلسلة النصية لـ sub بالكامل.

الأخطاء الشائعة

كل هذه حالات فشل موثقة من دليل استكشاف أخطاء auth الرسمي7:

  1. Failed to generate Google Cloud federated token — فشل الدخول إلى المجمع. رفض شرط السمة الخاص بك الرمز المميز: تحقق من هجاء المؤسسة/المستودع وتذكر أن الشروط حساسة لحالة الأحرف في جانب Google.
  2. Failed to generate OAuth 2.0 Access Token — فشل انتحال الشخصية. ربط roles/iam.workloadIdentityUser مفقود أو أن principalSet الخاص به لا يطابق: تحقق من أن معرف المجمع في سلسلة العضو يستخدم رقم المشروع وأن المستودع هو owner/name.
  3. Permission denied مع مسار مجمع في workload_identity_provider — لقد مررت اسم المجمع بدلاً من اسم المزود الكامل، أو استخدمت معرف المشروع حيث يكون رقم المشروع مطلوباً.
  4. The size of mapped attribute exceeds the 127 bytes limit — الـ google.subject الخاص بك (المستودع + الفرع) طويل جداً. هذا حد من حدود Google Cloud IAM؛ قم بتقصير اسم الفرع أو المستودع.
أنت تستخدم GitHub Enterprise Server أو رابط توكن فريد لـ GHEC؛ جهة الإصدار ليست https://token.actions.githubusercontent.com. استخدم رابط التوكن الخاص بتثبيتك كـ --issuer-uri.

لاحظ أيضاً مشكلة محتملة في سياسة المؤسسة: إذا كانت مؤسستك في Google Cloud تقيد مزودي الهوية الخارجيين، فيجب إدراج https://token.actions.githubusercontent.com في القائمة المسموح بها في قيد constraints/iam.workloadIdentityPoolProviders قبل أن تنجح الخطوة 37. وإذا كانت خطوة لاحقة تستخدم gsutil، فلن تظهر لها بيانات الاعتماد هذه — استخدم gcloud storage بدلاً من ذلك1.

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

Footnotes

  1. google-GitHub-actions/auth README and inputs reference — https://GitHub.com/google-GitHub-actions/auth 2 3 4 5 6 7 8 9 10

  2. google-GitHub-actions/deploy-cloudrun README (usage, inputs, authorization) — https://GitHub.com/google-GitHub-actions/deploy-cloudrun 2 3 4 5

  3. GitHub Changelog (2026-04-23): Immutable subject claims for GitHub Actions OIDC tokens — https://GitHub.blog/changelog/2026-04-23-immutable-subject-claims-for-GitHub-actions-oidc-tokens/ 2

  4. GitHub Docs: OpenID Connect reference — https://docs.GitHub.com/actions/reference/openid-connect-reference

  5. Release dates from the GitHub Releases API: auth v3.0.0 (2025-08-28), deploy-cloudrun v3.0.1 (2025-09-03), checkout v6.0.2 (2026-01-09).

  6. Google Cloud IAM docs: Configure Workload Identity Federation with deployment pipelines — https://docs.cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines

  7. google-GitHub-actions/auth troubleshooting guide — https://GitHub.com/google-GitHub-actions/auth/blob/main/docs/TROUBLESHOOTING.md 2 3 4

  8. Google Cloud Run docs: Authenticate developers — https://docs.cloud.google.com/run/docs/authenticating/developers