GitHub Actions تسلسل أدوار (Role Chaining) AWS OIDC عبر حسابات متعددة (2026)
٣٠ مايو ٢٠٢٦
ملخص
إن النشر في حسابات AWS الخاصة ببيئة الاختبار (staging) والإنتاج (production) من نفس سير عمل GitHub Actions هو مشكلة "مركز وأطراف" (hub-and-spoke): دور واحد موثوق به عبر OIDC في حساب "المركز" (hub)، ودور نشر واحد لكل حساب عمل (spoke)، واستخدام role-chaining: true في aws-actions/configure-aws-credentials@v6.1.3 للتنقل بينهما. يوضح هذا الدليل التعليمي لعام 2026 النمط الكامل، بما في ذلك إذن sts:TagSession الذي ينساه الجميع، والحد الأقصى لـ AWS البالغ ساعة واحدة للجلسات المتسلسلة، واستخدام بيئات GitHub كبوابة للنشر، والتوافق المستقبلي لمطالبة الموضوع غير القابلة للتغيير (immutable subject claim) التي سيتم تفعيلها للمستودعات الجديدة في 18 يونيو 2026.
موجز الإجابة: ضع دور مركز واحد موثوق به عبر OIDC في حساب AWS مركزي، وامنح كل حساب عمل دور طرف (spoke) يثق في المركز باستخدام sts:AssumeRole و sts:TagSession، ثم استدعِ aws-actions/configure-aws-credentials@v6.1.3 مرتين — مرة عبر OIDC، ومرة باستخدام role-chaining: true. الجلسات المتسلسلة محدودة بساعة واحدة كحد أقصى.
ما ستتعلمه
- إنشاء دور IAM للمركز (hub) في حساب AWS مركزي يثق في GitHub Actions OIDC لمستودعك.
- إنشاء دورين IAM للأطراف (spokes) —
deploy-stagingوdeploy-production— في حسابات عمل منفصلة يمكن لدور المركز تقمصها عبر حدود الحسابات. - سير عمل GitHub Actions يقوم بتشغيل
terraform applyضد إما بيئة الاختبار أو الإنتاج بناءً على بيئة GitHub المختارة للوظيفة. - سياسة ثقة للأطراف تمنح كلاً من
sts:AssumeRoleوsts:TagSessionلدور المركز — والحل البديلrole-skip-session-tagging: trueلأدوار الخدمات المشتركة حيث لا يمكنك منحsts:TagSession. - تحديد سقف
role-duration-seconds: 3600مدمج في سير العمل بحيث تفشل عمليات النشر بسرعة بدلاً من التوقف المفاجئ عند حدود الجلسة المتسلسلة. - التوافق المستقبلي مع مطالبة الموضوع غير القابلة للتغيير التي ستطبق على المستودعات الجديدة في 2026-06-18.
يستغرق الأمر حوالي 40-60 دقيقة من البداية للنهاية.
المتطلبات الأساسية
- ثلاثة حسابات AWS: واحد "للمركز" (مرساة الثقة لـ OIDC) وحسابا عمل "أطراف" لبيئة الاختبار والإنتاج. لا يشترط أن يكون المركز هو حساب إدارة AWS Organization الخاص بك — حساب
shared-servicesأوsecurity-toolingمخصص هو الخيار التقليدي. - إعداد نسخة الحساب الواحد من هذا الدليل التعليمي وتفعيلها: GitHub Actions OIDC إلى AWS: Terraform بدون مفاتيح (2026). دور المركز الذي ستبنيه هنا هو نفس شكل دور الحساب الواحد في ذلك المنشور — هذا الدليل يضيف طبقة التسلسل (chaining) فوقه.
- خلفية S3 لحالة Terraform مع قفل أصلي (native locking) — يوضح دليل Terraform S3 native state locking كيفية توفير واحدة بدون DynamoDB.
- يعمل
aws-actions/configure-aws-credentials@v6.1.3علىactions/runnerالإصدار v2.327.1 أو أحدث1؛ مشغلاتubuntu-latestالمستضافة على GitHub تجاوزت هذا الحد الأدنى في أوائل عام 2026، لذا لا حاجة لتجاوز يدوي هناك. - Terraform 1.15.52 و AWS CLI v2.34+3 على المشغل (يتم توفيرهما بواسطة
hashicorp/setup-terraform@v4.0.14). - مستودع GitHub مع توفر صلاحية
id-token: write(أي خطة — OIDC يعمل على Free و Pro و Team و Enterprise Cloud5).
الخطوة 1: لماذا نمط المركز والأطراف (وليس "دور OIDC واحد لكل حساب")
النهج البديهي هو تسجيل مزود OIDC الخاص بـ GitHub في كل حساب عمل وإنشاء دور واحد موثوق به عبر OIDC لكل حساب. هذا ينجح، لكنه يمنحك N من النسخ لنفس إعدادات OIDC التي يجب مزامنتها — كل تغيير في تنسيق مطالبة sub، كل ميزة جديدة في GitHub، كل عملية تدقيق. كما يجعل إضافة حساب رابع أو خامس عملية إعداد لكل حساب على حدة.
نمط المركز والأطراف يحول N إلى 1 + N: مزود OIDC واحد في حساب المركز، ودور واحد موثوق به عبر OIDC في حساب المركز، ودور بسيط يعتمد على sts:AssumeRole فقط في كل طرف. سياسة ثقة دور المركز هي المورد الوحيد المدرك لـ OIDC — أما أدوار الأطراف فتثق فقط في كيان AWS (ARN الخاص بدور المركز)، وهو نفس النمط التقليدي عبر الحسابات الذي توفره AWS منذ عام 2011.
كما يمنحك لوحة تدقيق واحدة: كل عملية نشر عبر كل حساب تبدأ بنفس دور المركز، وأدوار الأطراف تحتاج فقط لتسجيل "هل قام هذا الـ ARN بتقمص دوري، ولأي مدة" — وهو سؤال يجيب عليه CloudTrail بوضوح بالفعل.
المقايضة: الجلسات المتسلسلة محدودة بساعة واحدة (الخطوة 6 تغطي ما يعنيه ذلك عملياً). بالنسبة لعملية Terraform apply تستغرق 20 دقيقة ضد بضع مئات من الموارد، فهذه ليست مشكلة. أما بالنسبة لعملية ترحيل بنية تحتية تستغرق 90 دقيقة، فستحتاج إلى تصميم مختلف.
الخطوة 2: إعداد حساب المركز — مزود OIDC ودور المركز
يتم تشغيل هذا مرة واحدة في حساب المركز (سنسميه 123456789012 طوال الوقت). يخبر مزود IAM OpenID Connect شركة AWS بالوثوق في رموز JWT الموقعة من قبل token.actions.githubusercontent.com.
# Run against the hub account
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com
لاحظ عدم وجود --thumbprint-list. منذ يوليو 2023، تتحقق AWS من شهادة TLS لنقطة نهاية JWKS مقابل مراجع التصديق الجذرية المضمنة لديها وتتجاهل أي بصمة (thumbprint) تمررها لمزود GitHub؛ الأدلة التعليمية التي لا تزال تثبت البصمة القديمة 6938fd4d… تكرر طقوساً ما قبل عام 2023 لا تفعل شيئاً اليوم6.
الآن قم بإنشاء دور المركز. احفظ سياسة الثقة باسم hub-trust.json:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:*"
}
}
}]
}
تثبيت :aud=sts.amazonaws.com يطابق مدخل audience الافتراضي لـ aws-actions/configure-aws-credentials@v6.1.31. تثبيت :sub=repo:your-org/your-repo:* باستخدام StringLike يقيد الدور برموز JWT الصادرة لمستودعك المحدد (أي فرع، أي بيئة)؛ ستقوم الخطوة 5 بتشديد هذا القيد أكثر.
قم بإنشاء الدور بسياسة أذونات دنيا. يحتاج دور المركز إلى قدرة واحدة فقط: sts:AssumeRole على الـ ARNs الخاصة بالأطراف.
# Hub account
aws iam create-role \
--role-name GitHub-actions-hub \
--assume-role-policy-document file://hub-trust.json \
--max-session-duration 3600
ثم أرفق سياسة مضمنة تسرد الـ ARNs الخاصة بالأطراف التي يمكنه تقمصها:
cat > hub-permissions.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::222222222222:role/deploy-staging",
"arn:aws:iam::333333333333:role/deploy-production"
]
}]
}
EOF
aws iam put-role-policy \
--role-name GitHub-actions-hub \
--policy-name AssumeSpokes \
--policy-document file://hub-permissions.json
ملاحظتان حول التصميم. أولاً، تم ضبط --max-session-duration لدور المركز على 3600 ثانية. الافتراضي هو 3600 على أي حال، لكن تثبيته صراحةً يزيل الإغراء لأي شخص لزيادته لاحقاً وافتراض أن ذلك سينتقل عبر السلسلة — وهو ما لا يحدث (الخطوة 6). ثانياً، دور المركز لديه صفر من أذونات مستوى البيانات في AWS: لا يمكنه قراءة كائن S3، أو سرد مثيل EC2، أو التحدث إلى RDS. نصف قطر الانفجار الوحيد في حال تسربت بيانات اعتماده هو "يمكن للمهاجم محاولة تقمص أدوار الأطراف، والتي بدورها تقيد الوصول حسب الـ ARN المصدر".
الخطوة 3: توفير أدوار الأطراف باستخدام sts:TagSession
هذا هو المكان الذي تخطئ فيه معظم الشروحات التعليمية الخاصة بالحسابات المتعددة. يجب أن تمنح سياسة الثقة الخاصة بحساب الـ spoke صلاحية sts:AssumeRole وأيضاً sts:TagSession لدور الـ hub؛ فبدون sts:TagSession، ستفشل عملية AssumeRole المتسلسلة لأن aws-actions/configure-aws-credentials يمرر وسوم الجلسة (GitHub، Repository، Workflow، Action، Actor، Commit، Branch) بشكل افتراضي، وترفض AWS الطلب عندما يكون إذن تمرير الوسوم مفقوداً7.
احفظ هذا كـ spoke-trust.json وقم بتطبيقه على كل من حسابي staging و production:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/GitHub-actions-hub"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}]
}
الـ Principal هو الـ ARN الخاص بدور الـ hub، وليس جذر الحساب (root). إن تحديد الـ principal بـ ARN الدور بدقة يعني أنه حتى لو تم إنشاء دور آخر بطريقة ما في حساب الـ hub، فلن يتمكن من تقمص دور الـ spoke الخاص بك — فقط GitHub-actions-hub هو من يستطيع8. (لا تزال بعض الأنماط القديمة تستخدم "AWS": "arn:aws:iam::123456789012:root". وهذا أقل أماناً بشكل صارم ولا يوجد سبب للقيام بذلك.)
قم بتهيئة كلا الـ spokes. من حساب staging (222222222222):
# Run against the staging spoke account
aws iam create-role \
--role-name deploy-staging \
--assume-role-policy-document file://spoke-trust.json \
--max-session-duration 3600
aws iam attach-role-policy \
--role-name deploy-staging \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess
# Replace PowerUserAccess with your least-privilege policy in real life.
كرر العملية ضد 333333333333 من أجل deploy-production. ملف سياسة الثقة متطابق؛ ما يختلف لكل spoke هو سياسة الأذونات (عادةً ما يكون الإنتاج production أكثر تقييداً من staging).
إذا رفض فريق الأمان لديك منح sts:TagSession لـ principal عبر الحسابات، فلديك مخرج واحد — توضحه الخطوة 4.
الخطوة 4: كتابة سير عمل التسلسل باستخدام role-chaining: true
الآن سير العمل. الهدف الكامل من role-chaining: true يتلخص في وصفه الخاص من بيان الإجراء (action manifest): "استخدم أوراق الاعتماد الموجودة من البيئة لتقمص دور جديد، بدلاً من تقديم أوراق الاعتماد كمدخلات."9 بدون هذا العلم، ستحاول خطوة configure-aws-credentials الثانية تنفيذ sts:AssumeRoleWithWebIdentity ضد دور الـ spoke — لكن دور الـ spoke لا يثق بمزود OIDC؛ بل يثق بـ ARN دور الـ hub. ستحصل على هذا الخطأ المألوف:
Error: Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentity
هذا الخطأ هو العلامة رقم 1 على أنك نسيت علم التسلسل (chaining flag)10. مع role-chaining: true، تستخدم الخطوة الثانية أوراق الاعتماد التي صدرتها الخطوة الأولى إلى بيئة المشغل (runner) كـ principal يستدعي sts:AssumeRole — وهو التدفق العادي عبر الحسابات.
احفظه كـ .GitHub/workflows/deploy.yml:
name: Deploy
on:
workflow_dispatch:
inputs:
target:
description: 'Deploy target'
required: true
default: 'staging'
type: choice
options: [staging, production]
permissions:
id-token: write
contents: read
jobs:
apply:
runs-on: ubuntu-latest
environment: ${{ inputs.target }}
steps:
- uses: actions/checkout@v6.0.2
- name: Assume hub role via OIDC
uses: aws-actions/configure-aws-credentials@v6.1.3
with:
aws-region: us-east-1
role-to-assume: arn:aws:iam::123456789012:role/GitHub-actions-hub
role-session-name: gh-hub-${{ GitHub.run_id }}
role-duration-seconds: 3600
- name: Chain into the spoke deploy role
uses: aws-actions/configure-aws-credentials@v6.1.3
with:
aws-region: us-east-1
role-to-assume: ${{ inputs.target == 'production'
&& 'arn:aws:iam::333333333333:role/deploy-production'
|| 'arn:aws:iam::222222222222:role/deploy-staging' }}
role-session-name: gh-spoke-${{ inputs.target }}-${{ GitHub.run_id }}
role-chaining: true
role-duration-seconds: 3600
- uses: hashicorp/setup-terraform@v4.0.1
with:
terraform_version: 1.15.5
- run: terraform init
- run: terraform plan -out=tfplan
- run: terraform apply -auto-approve tfplan
هناك بعض الأشياء التي تستحق التنويه. يقوم الإجراء بتقمص دور الـ hub باستخدام رمز GitHub OIDC (رمز ACTIONS_ID_TOKEN_REQUEST_TOKEN الذي يوفره المشغل والمستبدل برمز STS)، ثم يصدر AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN إلى بيئة المشغل. الخطوة الثانية ليس لها مدخل role-to-assume يشير إلى OIDC — بل يخبره role-chaining: true أن "أوراق الاعتماد في بيئتي هي بالفعل principal صالح؛ استخدمها لاستدعاء sts:AssumeRole." يتم اختيار ARN الـ spoke بواسطة تعبير ثلاثي (ternary) بناءً على مدخل target، ويربط environment: ${{ inputs.target }} الوظيفة ببيئة GitHub (سنضيف قواعد حماية في الخطوة 5).
إذا لم تتمكن من منح sts:TagSession على الـ spoke (على سبيل المثال، لأن مسار الحوكمة لديك يشحن فقط قالب سياسة ثقة ثابتاً لا يتضمنه)، فقم بتعيين role-skip-session-tagging: true في خطوة التسلسل11:
- name: Chain into the shared-services role
uses: aws-actions/configure-aws-credentials@v6.1.3
with:
aws-region: us-east-1
role-to-assume: arn:aws:iam::444444444444:role/shared-services
role-session-name: gh-shared-${{ GitHub.run_id }}
role-chaining: true
role-skip-session-tagging: true
role-duration-seconds: 3600
المقايضة هنا هي أن الجلسة المتسلسلة تفقد وسوم GitHub/Repository/Workflow/Commit/Branch — لا يزال CloudTrail يسجل حدث assume-role، لكن تدقيق حساب عبء العمل لم يعد بإمكانه الإجابة على "أي تشغيل لسير العمل قام بهذا؟" من الجلسة نفسها. إذا سلكت هذا المسار، فقم بتعيين اسم الجلسة ليكون شيئاً قابلاً للبحث (يستخدم سير العمل أعلاه gh-shared-<run_id>) واعتمد على الربط بين أحداث CloudTrail في حساب الـ hub وحساب الـ spoke بدلاً من ذلك.
الخطوة 5: ربط أدوار الـ spoke ببيئات GitHub
تسمح سياسة ثقة الـ hub في الخطوة 2 بأي فرع وأي بيئة في مستودعك. هذا جيد بالنسبة للـ hub — فنطاق تأثيره هو مجرد "القدرة على استدعاء AssumeRole على الـ spokes" — ولكن يجب أن تكون أدوار الـ spoke أكثر إحكاماً. على وجه الخصوص، يجب ألا يقبل دور الـ production إلا تسلسلاً نشأ من تشغيل سير عمل مر عبر بيئة GitHub مع قواعد الحماية الصحيحة (المراجعون المطلوبون، مؤقت الانتظار، فروع المصدر المقيدة)12.
لا يمكنك القيام بذلك في سياسة ثقة الـ spoke نفسها — فالـ spoke يثق بـ ARN دور الـ hub، وليس بمصدر OIDC الخاص بـ GitHub، لذا فليس لديه وصول إلى مطالبة token.actions.githubusercontent.com:sub. يجب أن يتم الربط في طبقة الـ hub، حيث تكون مفاتيح شروط OIDC مرئية.
قم بتشديد hub-trust.json لاستخدام StringLike على مطالبة الموضوع (subject claim) التي تطابق كلاً من أنماط environment:staging و environment:production، ولا شيء غيرهما:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": [
"repo:your-org/your-repo:environment:staging",
"repo:your-org/your-repo:environment:production"
]
}
}
}]
}
مطالبة الموضوع في وظيفة سير عمل مع environment: production هي بالضبط repo:your-org/your-repo:environment:production (فهي تحل محل مطالبة الفرع/المرجع بالكامل عند تعيين بيئة)13. عمليات الدفع (pushes) إلى فروع الميزات التي لا تشير إلى بيئة لا يمكنها سك JWT يطابق أي من النمطين، لذا لا يمكنها تقمص دور الـ hub على الإطلاق — مما يعني أنها لا تستطيع الوصول إلى أي من الـ spokes، بغض النظر عما تحاول فعله.
الآن اذهب إلى Settings → Environments في المستودع، وقم بإنشاء staging و production، وفي بيئة production قم بتمكين Required reviewers (شخص أو شخصان موثوقان)، و Wait timer (على سبيل المثال 5 دقائق)، و Deployment branches مقيدة بـ main. الوظيفة في الخطوة 4 لديها بالفعل environment: ${{ inputs.target }}، لذا سيتوقف سير العمل عند تلك القواعد قبل أن يبدأ المشغل في سك OIDC JWT12.
قم بتطبيق سياسة الثقة الجديدة:
aws iam update-assume-role-policy \
--role-name GitHub-actions-hub \
--policy-document file://hub-trust.json
الخطوة 6: حد الساعة الواحدة (وماذا تفعل حيال ذلك)
الصفحة الأكثر تخطياً في كل شرح تعليمي لـ AWS متعدد الحسابات هي تلك التي تذكر هذا: المدة القصوى لجلسة دور متسلسلة هي ساعة واحدة. نقطة. مرجع AWS STS AssumeRole API لا لبس فيه — "إذا قمت بتقمص دور باستخدام تسلسل الأدوار وقدمت قيمة لمعلمة DurationSeconds أكبر من ساعة واحدة، فستفشل العملية."14 تعيين --max-session-duration إلى 12 ساعة في دور الـ spoke لا يغير هذا. كما أن رفع role-duration-seconds إلى 7200 في سير العمل لا يغير هذا أيضاً؛ سيعيد استدعاء AssumeRole خطأ DurationSeconds exceeds the MaxSessionDuration set for this role لأن الحد الأقصى الفعلي للاستدعاء المتسلسل هو 3600.
بالنسبة لمعظم عمليات النشر، لا يمثل هذا مشكلة: فعملية terraform apply نموذجية على بضع مئات من الموارد تستغرق من 5 إلى 25 دقيقة. ولكن هناك ثلاثة أشياء قد تسبب لك المتاعب:
- عمليات Terraform apply الطويلة. عملية ترحيل (migration) مدتها 90 دقيقة تقوم بإنشاء نسخ RDS للقراءة، وتشغيل تغييرات في المخطط (schema)، وتدوير مجموعات الأمان ستصطدم بالحد الأقصى في منتصف عملية
apply. تنتهي صلاحية بيانات الاعتماد، ويبدأ موفر AWS في تلقي أخطاء 401، ويتركك Terraform بحالة جزئية (القفل لا يزال محتجزاً، ونصف الموارد تم تغييرها). الحل هو تقسيم النشر إلى استدعاءاتterraform applyأصغر يتناسب كل منها مع الساعة الواحدة، أو إعادة التصميم بحيث لا تحتاج العملية الطويلة إلى بيانات اعتماد جديدة (يجب تشغيل معظم عمليات ترحيل المخطط داخل التطبيق، وليس داخل Terraform). - منتظرو العمليات غير المتزامنة (Async waiters). بعض استدعاءات
aws ec2 wait …أوaws cloudformation wait stack-update-completeتقوم بالاستعلام المتكرر (polling) حتى تنتهي عملية طويلة الأمد. إذا استغرقت العملية المنتظرة أكثر من ساعة، سيفشل المنتظر بخطأExpiredTokenوستخرج خطوة سير العمل برمز خطأ (non-zero) على الرغم من نجاح عملية AWS في النهاية. استخدم خطوة استعلام أصغر تخرج بسرعة وتتتبع الاكتمال خارج الجلسة المتسلسلة. - إعادة استخدام بيانات الاعتماد عبر الخطوات. إذا قمت باستدعاء
configure-aws-credentialsمرة واحدة في بداية المهمة ثم كان لديك عشر خطوات لاحقة تعتمد على متغيرات البيئة المصدرة، فإن جميع الخطوات العشر تشترك في توقيت الجلسة الواحدة. خطوة اختبار مدتها 50 دقيقة تليها خطوة نشر مدتها 15 دقيقة ستفشل عند النشر. إما أن تعيد تشغيلconfigure-aws-credentialsمع تفعيلrole-chaining: trueفي بداية كل مرحلة طويلة الأمد (وهي عملية غير مكلفة؛ حيث تعيد التسلسل من بيانات اعتماد المحور "hub" التي لا تزال في البيئة) أو قم بالتقسيم إلى مهام منفصلة.
إن تعيين role-duration-seconds: 3600 صراحةً في كلتا الخطوتين في سير عمل الخطوة 4 هو خيار "الفشل السريع": فهو يخبرك مسبقاً أن لديك ساعة واحدة، بدلاً من التظاهر بأن لديك اثنتي عشرة ساعة.
الخطوة 7: التوافق المستقبلي لمطالبة الموضوع غير القابلة للتغيير بتاريخ 2026-06-18
أعلنت GitHub عن طرح "مطالبة الموضوع غير القابلة للتغيير" (immutable-subject-claim) في 23 أبريل 2026. بالنسبة لجميع المستودعات التي تم إنشاؤها في أو بعد 18 يونيو 2026 (بالإضافة إلى أي إعادة تسمية للمستودع أو نقل يحدث في ذلك التاريخ أو بعده)، لن تبدو مطالبة موضوع OIDC كما يلي:
repo:your-org/your-repo:environment:production
بل ستبدو هكذا:
repo:your-org-1234567/your-repo-89012345:environment:production
يتم إلحاق المعرفات الرقمية للحماية من هجمات إعادة استخدام الأسماء حيث يمكن إعادة تسجيل اسم org/repo مُعاد تدويره واستخدامه لإصدار رموز مميزة تطابق سياسة ثقة موجودة15. المستودعات الحالية لا تتأثر إلا إذا اخترت ذلك، والتغيير خاص بـ GitHub.com فقط — وليس GitHub Enterprise Server.
إذا كان مستودعك أقدم من تاريخ الطرح، فسيستمر سير العمل المذكور أعلاه في العمل. ولكن يجب عليك تحديث سياسة ثقة المحور (hub) الآن لقبول كل من التنسيقات القديمة وغير القابلة للتغيير، بحيث لا ينكسر أي شيء في الساعة 3 صباحاً في اليوم الذي يطبق فيه التغيير على مستودعك (بسبب إعادة التسمية أو النقل أو لأنك اخترت ذلك):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": [
"repo:your-org/your-repo:environment:staging",
"repo:your-org/your-repo:environment:production",
"repo:your-org-*/your-repo-*:environment:staging",
"repo:your-org-*/your-repo-*:environment:production"
]
}
}
}]
}
تطابق علامة النجمة * في StringLike المعرف الرقمي اللاحق دون إضعاف بقية المسار: المهاجم الذي يسجل مساحة اسم your-org في المستقبل لا يمكنه مطابقة أي من النمطين لأنه لا يمكنه إصدار رمز مميز يبدأ sub الخاص به بـ repo:your-org-<your-actual-id>/.
التحقق
أسرع فحص للسلامة هو تشغيل سير العمل ضد بيئة staging من واجهة مستخدم GitHub (Actions → Deploy → Run workflow → target: staging) ومراقبة كلتا خطوتي configure-aws-credentials وهما تسجلان علامة اختيار خضراء ومعرف حساب AWS مخفي. ثم تحقق من CloudTrail في كل حساب فرعي (spoke):
# في حساب staging الفرعي
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole \
--max-results 5 \
--query 'Events[*].[EventTime,Username,Resources[?ResourceType==`AWS::IAM::Role`].ResourceName | [0]]' \
--output table
يجب أن ترى حدثاً في الحساب الفرعي حيث يكون Username (الأصيل) هو GitHub-actions-hub/gh-hub-<run_id> (اسم جلسة دور المحور من الخطوة 4)، والمورد هو deploy-staging. هذا هو التسلسل الذي يعمل من البداية إلى النهاية: قام OIDC JWT بإنشاء جلسة المحور في الحساب 123456789012، وقامت جلسة المحور بانتحال دور deploy-staging في الحساب 222222222222.
إذا قمت بتغيير مدخل target إلى production من فرع PR، فيجب أن يفشل التشغيل في مرحلة "انتظار المراجعة" — وليس في طبقة IAM — لأن قواعد حماية البيئة تمنعه قبل أن يحاول المشغل (runner) البدء.
الأخطاء الشائعة
خطأ Not authorized to perform sts:AssumeRoleWithWebIdentity في الخطوة الثانية. لقد نسيت role-chaining: true في خطوة configure-aws-credentials الثانية. عاد الإجراء افتراضياً إلى تدفق OIDC ضد الدور الفرعي، والذي لا يثق في موفر OIDC10.
خطأ User: arn:aws:sts::123456789012:assumed-role/GitHub-actions-hub/... is not authorized to perform: sts:TagSession on resource: arn:aws:iam::222222222222:role/deploy-staging. سياسة ثقة الدور الفرعي تفتقد إلى sts:TagSession. إما أن تضيفها إلى سياسة ثقة الدور الفرعي (المسار المفضل في الخطوة 3) أو قم بتعيين role-skip-session-tagging: true في خطوة التسلسل (خيار الطوارئ في الخطوة 4)7.
خطأ The requested DurationSeconds exceeds the MaxSessionDuration set for this role في خطوة التسلسل، على الرغم من أنك قمت بتعيين --max-session-duration 3600 لكلا الدورين. من المؤكد تقريباً أنك قمت بتعيين role-duration-seconds بقيمة أعلى من 3600 في خطوة التسلسل. الجلسات المتسلسلة محدودة بساعة واحدة بغض النظر عن MaxSessionDuration لأي من الدورين14. اضبطها على 3600 (أو احذفها — فـ 3600 هي القيمة الافتراضية).
الرموز التي تنتهي صلاحيتها بعد 55 دقيقة. الوقت الفعلي لمهمتك من "إصدار رمز OIDC" إلى "الخطوة التي تحتاج إلى بيانات الاعتماد" أطول مما تعتقد — مؤقتات انتظار البيئة، والمراجعون المطلوبون، والمشغلون في قائمة الانتظار، كلهم يستهلكون من نفس الساعة. أعد تشغيل configure-aws-credentials مع role-chaining: true في بداية أي مرحلة تبدأ بعد أكثر من 30 دقيقة من خطوة المحور.
فشل سير العمل فوراً بخطأ Error: Unable to retrieve OIDC ID token: NotFound. المهمة تفتقد إلى permissions: id-token: write. تمنحها الكتلة البرمجية في سير عمل الخطوة 4 على مستوى سير العمل؛ إذا كنت تحدد الأذونات لكل مهمة على حدة، فإن كل مهمة تستدعي configure-aws-credentials تحتاج إلى هذا السطر5.
الخطوات التالية
لديك الآن مرساة ثقة OIDC واحدة وعدد N من الأدوار الفرعية، محمية ببيئات GitHub ومتوافقة مستقبلاً مع طرح 18 يونيو. ثلاثة توسعات طبيعية:
- إضافة دور فرعي رابع لحساب تطوير منفصل. كل شيء في الخطوة 3 يعتمد على قالب
account_id؛ أضف بيئةdevفي GitHub، ونمط:environment:developmentفي سياسة ثقة المحور، ودورdeploy-developmentفي الحساب الجديد. لا حاجة لموارد جديدة في جانب المحور. - استبدال قائمة AssumeRole المضمنة في
hub-permissions.jsonبشرط يعتمد على العلامات (tags). بدلاً من إدراج كل ARN فرعي صراحةً، قم بإرفاق علامة مثلPurpose=GitHubDeployبكل دور فرعي واكتب سياسة المحور كـ"Resource": "*"مع"Condition": {"StringEquals": {"iam:ResourceTag/Purpose": "GitHubDeploy"}}. ستحتاج الأدوار الفرعية الجديدة فقط إلى العلامة. - اربط هذا بحاجز حماية "الفشل المفتوح" (fail-open) في عملية النشر نفسها. إذا كان الحساب الفرعي غير متاح لفترة وجيزة، فيجب أن يفشل النشر بوضوح بدلاً من التوقف — أنماط البرمجيات الوسيطة للفشل المفتوح مقابل الفشل المغلق يمكن تعميمها على حواجز حماية CI أيضاً.
نمط الـ hub-and-spoke يتوسع خطياً مع الحسابات ويظل قابلاً للمراجعة والتدقيق مع نموه. الأجزاء التي يخطئ فيها معظم الناس — sts:TagSession، و role-chaining: true، والحد الأقصى للساعة الواحدة — هي بالضبط الأجزاء التي حددها هذا الدليل بمصادر موثقة، لذا عندما يسألك المهندس التالي في فريقك "لماذا يوجد 3600 في سير عمل النشر الخاص بنا؟" سيكون لديك إجابة لا تبدأ بـ "أعتقد أن..."
Footnotes
-
aws-actions/configure-aws-credentialsv6.1.3 release notes (2026-05-27); v6.0.0 release notes (2026-02-04) document the node24 runtime bump and the GitHub Actions Runner v2.327.1 minimum. https://GitHub.com/aws-actions/configure-aws-credentials/releases ↩ ↩2 -
HashiCorp Terraform releases — 1.15.5 is the current stable per the authoritative release directory. https://releases.hashicorp.com/terraform/ ↩
-
AWS CLI v2 installation reference. https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html ↩
-
hashicorp/setup-terraformv4.0.1 release notes (2026-05-12). https://GitHub.com/hashicorp/setup-terraform/releases/tag/v4.0.1 ↩ -
GitHub OpenID Connect reference — minting JWTs, the
id-token: writepermission, audience and subject claim formats. https://docs.GitHub.com/en/actions/concepts/security/openid-connect ↩ ↩2 -
"Obtain the thumbprint for an OpenID Connect identity provider" — AWS IAM docs explicitly state the thumbprint is no longer required for the GitHub provider and is ignored if supplied. https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html ↩
-
"How to use trust policies with IAM roles" — AWS Security Blog on pinning the trust principal to the exact role ARN instead of the account root. https://aws.amazon.com/blogs/security/how-to-use-trust-policies-with-iam-roles/ ↩
-
aws-actions/configure-aws-credentialsaction manifest,role-chaininginput description: "Use existing credentials from the environment to assume a new role, rather than providing credentials as input." https://GitHub.com/aws-actions/configure-aws-credentials/blob/main/action.yml ↩ -
Issue #391 on
aws-actions/configure-aws-credentials— the canonical write-up of "I forgot role-chaining: true and got AssumeRoleWithWebIdentity not authorized." https://GitHub.com/aws-actions/configure-aws-credentials/issues/391 ↩ ↩2 -
Issue #1396 — the credentials-chaining README example does not work without either
sts:TagSessionon the spoke trust policy ORrole-skip-session-tagging: trueon the chaining step. https://GitHub.com/aws-actions/configure-aws-credentials/issues/1396 ↩ -
GitHub Environments and protection rules — required reviewers, wait timers, branch restrictions. https://docs.GitHub.com/en/actions/how-tos/manage-workflow-runs/manage-environments ↩ ↩2
-
GitHub OIDC reference — the subject claim is
repo:ORG-NAME/REPO-NAME:environment:ENVIRONMENT-NAMEwhen a job references an environment, replacing the branchref:form. https://docs.GitHub.com/actions/reference/openid-connect-reference ↩ -
AWS STS
AssumeRoleAPI reference — "When you use role chaining, your new credentials are limited to a maximum duration of one hour." https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html ↩ ↩2 -
"Immutable subject claims for GitHub Actions OIDC tokens" — GitHub Changelog, 2026-04-23. New repositories adopt the immutable format automatically on 2026-06-18; existing repositories must opt in. GitHub.com only. https://GitHub.blog/changelog/2026-04-23-immutable-subject-claims-for-GitHub-actions-oidc-tokens/ ↩