مختبر: إعداد OIDC Workload Identity لـ GitHub Actions مع AWS

نظرة عامة

إذا كانت سير عمل GitHub Actions الخاصة بك تتصل بـ AWS باستخدام AWS_ACCESS_KEY_ID و AWS_SECRET_ACCESS_KEY المخزنة كأسرار في المستودع، فأنت تواجه مشكلة أمنية خطيرة. هذه البيانات طويلة الأمد لا تنتهي صلاحيتها من تلقاء نفسها، ويمكن لأي خطوة في سير العمل استخراجها (بما في ذلك الإجراءات من أطراف ثالثة)، وتمنح المهاجمين وصولاً دائماً إلى حساب AWS الخاص بك في حال اختراقها.

يقضي اتحاد OpenID Connect (OIDC) على هذا الخطر تماماً. بدلاً من تخزين بيانات اعتماد AWS الثابتة في GitHub، يطلب سير العمل الخاص بك رمز OIDC قصير الأمد من مزود هوية GitHub. تتحقق AWS من صحة هذا الرمز، وتتحقق من المطالبات (المستودع، الفرع، البيئة)، وتصدر بيانات اعتماد مؤقتة تنتهي صلاحيتها في دقائق. لا يتم تخزين أي أسرار في أي مكان — يتم إنشاء علاقة الثقة بين مزود OIDC الخاص بـ GitHub ودور IAM في AWS الخاص بك.

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

  • إعداد الأساس غير الآمن (مفاتيح AWS الثابتة) لفهم ما ستستبدله
  • إنشاء مزود هوية OIDC في AWS يثق بـ GitHub Actions
  • إنشاء دور IAM مع سياسة ثقة محددة النطاق لمستودعك وفرعك المحددين
  • تحديث سير عمل GitHub Actions لاستخدام مصادقة OIDC
  • تنفيذ ضوابط الوصول القائمة على الفروع والبيئات
  • تدقيق أحداث مصادقة OIDC في CloudTrail
  • حذف بيانات الاعتماد الثابتة القديمة لإتمام الترحيل

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

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

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

  • حساب AWS مع صلاحيات إدارية لـ IAM (القدرة على إنشاء مزودي الهوية والأدوار والسياسات)
  • حساب GitHub مع مستودع تتحكم فيه (الطبقة المجانية كافية)
  • AWS CLI v2 مثبت ومُعد محلياً (يجب أن يُرجع aws --version الإصدار 2.x)
  • Terraform v1.5+ (اختياري لكن يُنصح به — يوفر هذا المختبر تعليمات لكل من Console و Terraform)
  • فهم أساسي لأدوار IAM وسياسات الثقة وصيغة سير عمل GitHub Actions

الوقت المقدر: 60–90 دقيقة

إعداد البيئة: الأساس غير الآمن

قبل تنفيذ OIDC، لنقم بإنشاء النمط غير الآمن الذي ستستبدله. هذا يجعل التحسين الأمني ملموساً ويمنحك سير عمل عامل للترحيل منه.

الخطوة 1: إنشاء مستودع اختباري

أنشئ مستودع GitHub جديد يُسمى oidc-lab. قم بتهيئته مع README واستنسخه محلياً:

gh repo create oidc-lab --public --clone
cd oidc-lab

الخطوة 2: تخزين بيانات اعتماد AWS كأسرار GitHub (الطريقة غير الآمنة)

إذا كان لديك مستخدم IAM حالي مع وصول برمجي، قم بتخزين بيانات اعتماده كأسرار GitHub:

gh secret set AWS_ACCESS_KEY_ID --body "AKIAIOSFODNN7EXAMPLE"
gh secret set AWS_SECRET_ACCESS_KEY --body "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

لماذا هذا خطير:

  • لا انتهاء صلاحية: هذه البيانات صالحة حتى يتم تدويرها أو حذفها يدوياً. إذا تسربت، يحصل المهاجم على وصول غير محدد المدة.
  • تعرض واسع: كل تشغيل لسير العمل، وكل fork (إذا كان عاماً)، وكل إجراء من طرف ثالث في سير عملك يمكنه قراءة هذه الأسرار.
  • لا مسار تدقيق دقيق: يُظهر CloudTrail مستخدم IAM، لكن ليس أي مستودع أو فرع أو سير عمل أطلق استدعاء API.
  • عبء التدوير: يجب عليك تدوير هذه المفاتيح يدوياً وتحديث أسرار GitHub — وهي عملية غالباً ما يتم إهمالها.

الخطوة 3: إنشاء سير عمل الأساس غير الآمن

أنشئ .github/workflows/deploy.yml مع بيانات اعتماد ثابتة:

name: Deploy (Insecure - Static Keys)

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (INSECURE)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Verify Identity
        run: aws sts get-caller-identity

      - name: List S3 Buckets
        run: aws s3 ls

قم بعمل commit ودفع سير العمل هذا. تأكد من أنه يعمل بنجاح — هذا هو الأساس الذي ستستبدله بـ OIDC.

التمرين 1: إنشاء مزود هوية OIDC في AWS

الخطوة الأولى في اتحاد OIDC هي إخبار AWS بالثقة في مزود هوية GitHub. هذا إعداد يتم مرة واحدة لكل حساب AWS.

الخيار أ: AWS Console

  1. افتح IAM ConsoleIdentity providersAdd provider
  2. اختر OpenID Connect
  3. لـ Provider URL، أدخل: https://token.actions.githubusercontent.com
  4. انقر Get thumbprint (تقوم AWS بجلب شهادة TLS والتحقق منها)
  5. لـ Audience، أدخل: sts.amazonaws.com
  6. انقر Add provider

الخيار ب: AWS CLI

# Get the thumbprint for GitHub's OIDC provider
# As of 2024, GitHub's thumbprint is managed by AWS and auto-verified
# You can retrieve it with:
THUMBPRINT=$(openssl s_client -servername token.actions.githubusercontent.com \
  -showcerts -connect token.actions.githubusercontent.com:443 < /dev/null 2>/dev/null \
  | openssl x509 -fingerprint -noout \
  | cut -d'=' -f2 \
  | tr -d ':' \
  | tr '[:upper:]' '[:lower:]')

aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list "$THUMBPRINT"

الخيار ج: Terraform

resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]

  tags = {
    Name        = "GitHub Actions OIDC"
    Environment = "shared"
    ManagedBy   = "terraform"
  }
}

ملاحظة: تتحقق AWS الآن تلقائياً من بصمة مزود OIDC الخاص بـ GitHub. قيمة البصمة في مورد Terraform مطلوبة من قبل API لكن AWS ستتحقق من سلسلة الشهادات بغض النظر عن القيمة المقدمة.

التحقق

تأكد من إنشاء المزود:

aws iam list-open-id-connect-providers

يجب أن ترى ARN مثل:

arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com

التمرين 2: إنشاء دور IAM مع سياسة الثقة

الآن أنشئ دور IAM يمكن لـ GitHub Actions تولّيه عبر OIDC. سياسة الثقة هي الجزء الأهم — فهي تحدد بالضبط أي المستودعات والفروع والبيئات يمكنها تولّي هذا الدور.

الخطوة 1: إنشاء سياسة الثقة

احفظ ما يلي كـ trust-policy.json. استبدل 123456789012 بمعرف حساب AWS الخاص بك، و myorg/myrepo بمنظمة GitHub ومستودعك:

{
  "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:myorg/myrepo:ref:refs/heads/main"
        }
      }
    }
  ]
}

فهم حقول سياسة الثقة

  • Principal.Federated — معرف ARN لمزود GitHub OIDC الذي أنشأته في التمرين 1. هذا يُخبر AWS بأي مزود هوية يجب الوثوق به.
  • Action: sts:AssumeRoleWithWebIdentity — إجراء STS المحدد لاتحاد OIDC. هذا يختلف عن sts:AssumeRole (المستخدم للأدوار عبر الحسابات) أو sts:AssumeRoleWithSAML (المستخدم لاتحاد SAML).
  • Condition.StringEquals.aud — يتحقق من مطالبة الجمهور في رمز OIDC. يجب أن تكون sts.amazonaws.com لمطابقة ما يرسله إجراء configure-aws-credentials.
  • Condition.StringLike.sub — يتحقق من مطالبة الموضوع. هذا هو أهم عنصر تحكم أمني. يحتوي الموضوع على المستودع ونوع المرجع وقيمة المرجع. استخدام StringLike مع أحرف البدل يسمح بمطابقة مرنة، بينما StringEquals يتطلب مطابقة تامة.

الخطوة 2: إنشاء دور IAM

aws iam create-role \
  --role-name github-actions-deploy \
  --assume-role-policy-document file://trust-policy.json \
  --description "Role for GitHub Actions OIDC deployment" \
  --max-session-duration 3600

الخطوة 3: إرفاق سياسة IAM بالحد الأدنى من الصلاحيات

اتبع مبدأ الحد الأدنى من الامتيازات. لهذا المختبر، أرفق سياسة تمنح وصول قراءة فقط إلى حاوية S3 محددة:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-deployment-bucket",
        "arn:aws:s3:::my-deployment-bucket/*"
      ]
    }
  ]
}
# Save the above as permissions-policy.json, then:
aws iam put-role-policy \
  --role-name github-actions-deploy \
  --policy-name S3ReadAccess \
  --policy-document file://permissions-policy.json

معادل Terraform

data "aws_iam_policy_document" "github_actions_trust" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRoleWithWebIdentity"]

    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }

    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }

    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values   = ["repo:myorg/myrepo:ref:refs/heads/main"]
    }
  }
}

resource "aws_iam_role" "github_actions_deploy" {
  name                 = "github-actions-deploy"
  assume_role_policy   = data.aws_iam_policy_document.github_actions_trust.json
  max_session_duration = 3600

  tags = {
    Name      = "GitHub Actions Deploy"
    ManagedBy = "terraform"
  }
}

resource "aws_iam_role_policy" "s3_read" {
  name = "S3ReadAccess"
  role = aws_iam_role.github_actions_deploy.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:ListBucket"
        ]
        Resource = [
          "arn:aws:s3:::my-deployment-bucket",
          "arn:aws:s3:::my-deployment-bucket/*"
        ]
      }
    ]
  })
}

التحقق

# Confirm the role exists and has the correct trust policy
aws iam get-role --role-name github-actions-deploy \
  --query 'Role.AssumeRolePolicyDocument' --output json

التمرين 3: تحديث سير عمل GitHub Actions

الآن استبدل سير عمل بيانات الاعتماد الثابتة بمصادقة OIDC. هذه هي خطوة الترحيل الأساسية.

الخطوة 1: تحديث سير العمل

استبدل محتويات .github/workflows/deploy.yml بما يلي:

name: Deploy (Secure - OIDC)

on:
  push:
    branches: [main]

permissions:
  id-token: write   # Required for OIDC token request
  contents: read     # Required for actions/checkout

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
          role-session-name: github-actions-${{ github.run_id }}
          aws-region: us-east-1

      - name: Verify Identity
        run: |
          aws sts get-caller-identity
          echo "Successfully authenticated via OIDC!"

      - name: List S3 Bucket Contents
        run: aws s3 ls s3://my-deployment-bucket/

شرح التغييرات الرئيسية

  • permissions: id-token: write — هذا إلزامي. يمنح سير العمل إذن طلب رمز OIDC من مزود هوية GitHub. بدون هذا، لا يستطيع إجراء configure-aws-credentials الحصول على رمز.
  • permissions: contents: read — عند تعيين أي إذن صريح، تكون جميع الأذونات الأخرى افتراضياً none. يجب منح contents: read صراحة ليعمل actions/checkout.
  • role-to-assume — معرف ARN لدور IAM الذي تم إنشاؤه في التمرين 2. سيستدعي الإجراء sts:AssumeRoleWithWebIdentity باستخدام رمز OIDC.
  • role-session-name — اسم جلسة وصفي يظهر في CloudTrail. تضمين github.run_id يجعل كل جلسة قابلة للتتبع إلى تشغيل سير عمل محدد.
  • لا aws-access-key-id أو aws-secret-access-key — تم إزالة هذه الحقول بالكامل. يكتشف الإجراء أن role-to-assume مُعيّن بدون بيانات اعتماد ثابتة ويستخدم OIDC تلقائياً.

الخطوة 2: الدفع والتحقق

git add .github/workflows/deploy.yml
git commit -m "Migrate to OIDC authentication"
git push origin main

انتقل إلى تبويب Actions في مستودع GitHub الخاص بك. يجب أن ترى سير العمل قيد التشغيل. في خطوة “Verify Identity”، سيبدو الإخراج كالتالي:

{
    "UserId": "AROA3XFRBF23ZCEXAMPLE:github-actions-9876543210",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/github-actions-deploy/github-actions-9876543210"
}

لاحظ أن ARN يُظهر assumed-role/github-actions-deploy — هذا يؤكد أن سير العمل يتصل عبر دور OIDC، وليس مستخدم IAM.

التمرين 4: التقييد حسب الفرع والبيئة والعلامة

مطالبة sub في سياسة الثقة هي آلية التحكم في الوصول الأساسية الخاصة بك. تتبع مطالبة الموضوع من GitHub Actions تنسيقاً متوقعاً يشفّر المستودع ونوع المحفز والمرجع.

أنماط مطالبة الموضوع الشائعة

المحفز تنسيق مطالبة الموضوع
دفع إلى فرع repo:OWNER/REPO:ref:refs/heads/BRANCH
طلب سحب repo:OWNER/REPO:pull_request
بيئة repo:OWNER/REPO:environment:ENV_NAME
دفع علامة repo:OWNER/REPO:ref:refs/tags/TAG

التقييد للفرع الرئيسي فقط

هذا هو الإعداد من التمرين 2. فقط سير العمل المُطلق بدفع إلى main يمكنه تولّي الدور:

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"
}

التقييد لبيئة محددة

توفر GitHub Environments طبقة إضافية من التحكم في الوصول. عندما تحدد وظيفة ما environment، تتغير مطالبة الموضوع لتتضمن اسم البيئة:

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:environment:production"
}

التقييد للإصدارات المُعلّمة

السماح فقط للإصدارات المُعلّمة (مثل v1.0.0، v2.3.1) بتولّي الدور:

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/tags/v*"
}

اختبار القيود

أنشئ فرع ميزة وادفع تشغيل سير عمل:

git checkout -b feature/test-oidc-restriction
git commit --allow-empty -m "Test OIDC restriction"
git push origin feature/test-oidc-restriction

إذا كانت سياسة الثقة الخاصة بك تقيّد إلى refs/heads/main، فسيفشل سير العمل على فرع الميزة مع:

Error: Could not assume role with OIDC: Not authorized to perform
sts:AssumeRoleWithWebIdentity

AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity

الآن ادفع نفس التغيير إلى main:

git checkout main
git merge feature/test-oidc-restriction
git push origin main

سير العمل على main ينجح. هذا يوضح التحكم في الوصول القائم على المطالبات — تقيّم سياسة الثقة في AWS المطالبات المضمنة في رمز OIDC لاتخاذ قرارات التفويض. لا توجد بيانات اعتماد متضمنة؛ الرمز نفسه يحمل سياق التفويض.

التمرين 5: أدوار لكل بيئة

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

الخطوة 1: إنشاء بيئات GitHub

في مستودعك، انتقل إلى SettingsEnvironments:

  1. أنشئ بيئة staging (بدون قواعد حماية)
  2. أنشئ بيئة production مع:
    • مراجعون مطلوبون: أضف عضو فريق واحد على الأقل
    • فروع النشر: قيّد إلى main فقط

الخطوة 2: إنشاء دور IAM لبيئة التجهيز

دور التجهيز يثق بجميع الفروع في المستودع:

{
  "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:myorg/myrepo:*"
        }
      }
    }
  ]
}
aws iam create-role \
  --role-name github-actions-staging \
  --assume-role-policy-document file://staging-trust-policy.json \
  --description "Staging deployment role - all branches"

الخطوة 3: إنشاء دور IAM للإنتاج

دور الإنتاج يثق فقط ببيئة production على الفرع main:

{
  "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",
          "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:environment:production"
        }
      }
    }
  ]
}
aws iam create-role \
  --role-name github-actions-production \
  --assume-role-policy-document file://production-trust-policy.json \
  --description "Production deployment role - main branch + production environment only"

الخطوة 4: سير عمل متعدد البيئات

أنشئ سير عمل ينشر إلى التجهيز تلقائياً وإلى الإنتاج بموافقة يدوية:

name: Deploy Multi-Environment

on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (Staging)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-staging
          role-session-name: staging-${{ github.run_id }}
          aws-region: us-east-1

      - name: Deploy to Staging
        run: |
          aws sts get-caller-identity
          echo "Deploying to staging environment..."
          # Your staging deployment commands here

  deploy-production:
    runs-on: ubuntu-latest
    needs: deploy-staging
    environment: production
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (Production)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-production
          role-session-name: production-${{ github.run_id }}
          aws-region: us-east-1

      - name: Deploy to Production
        run: |
          aws sts get-caller-identity
          echo "Deploying to production environment..."
          # Your production deployment commands here

عند تشغيل سير العمل هذا:

  1. تعمل وظيفة staging فوراً، وتتولّى github-actions-staging، وتنشر
  2. تنتظر وظيفة production اكتمال التجهيز ثم تتوقف للحصول على موافقة يدوية
  3. يوافق المراجع المطلوب على النشر في واجهة GitHub
  4. تعمل وظيفة الإنتاج، وتتولّى github-actions-production، وتنشر

تستخدم سياسة ثقة دور الإنتاج StringEquals (وليس StringLike) مع الموضوع الدقيق repo:myorg/myrepo:environment:production. هذا يعني أن الوظائف التي تُعلن environment: production فقط يمكنها تولّي دور الإنتاج — وتفرض GitHub Environments أن عمليات نشر الفرع main فقط مع موافقة المراجع يمكنها استخدام تلك البيئة.

Terraform لكلا الدورين

locals {
  github_oidc_arn = aws_iam_openid_connect_provider.github.arn
  github_repo     = "myorg/myrepo"
}

# Staging role - trusts all branches
resource "aws_iam_role" "github_actions_staging" {
  name = "github-actions-staging"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = local.github_oidc_arn
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
          }
          StringLike = {
            "token.actions.githubusercontent.com:sub" = "repo:${local.github_repo}:*"
          }
        }
      }
    ]
  })
}

# Production role - trusts only the production environment
resource "aws_iam_role" "github_actions_production" {
  name = "github-actions-production"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = local.github_oidc_arn
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
            "token.actions.githubusercontent.com:sub" = "repo:${local.github_repo}:environment:production"
          }
        }
      }
    ]
  })
}

التمرين 6: التحقق والتدقيق

تنشئ مصادقة OIDC مسارات تدقيق مفصلة في AWS CloudTrail. يتم تسجيل كل استدعاء AssumeRoleWithWebIdentity مع المجموعة الكاملة من مطالبات GitHub OIDC.

الخطوة 1: استعلام CloudTrail عن أحداث OIDC

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --max-results 10 \
  --query 'Events[].{Time:EventTime,Username:Username,Resources:Resources}' \
  --output table

الخطوة 2: فحص حدث CloudTrail

يحتوي حدث AssumeRoleWithWebIdentity النموذجي في CloudTrail على الحقول الرئيسية التالية:

{
  "eventName": "AssumeRoleWithWebIdentity",
  "eventSource": "sts.amazonaws.com",
  "requestParameters": {
    "roleArn": "arn:aws:iam::123456789012:role/github-actions-deploy",
    "roleSessionName": "github-actions-9876543210"
  },
  "requestID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "responseElements": {
    "credentials": {
      "accessKeyId": "ASIAEXAMPLE...",
      "expiration": "Mar 23, 2026 2:00:00 PM"
    },
    "subjectFromWebIdentityToken": "repo:myorg/myrepo:ref:refs/heads/main",
    "provider": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
  },
  "additionalEventData": {
    "WebIdFederationData": {
      "federatedProvider": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com",
      "attributes": {
        "sub": "repo:myorg/myrepo:ref:refs/heads/main",
        "aud": "sts.amazonaws.com",
        "iss": "https://token.actions.githubusercontent.com",
        "repository": "myorg/myrepo",
        "ref": "refs/heads/main",
        "sha": "abc123def456...",
        "actor": "github-username",
        "workflow": "Deploy (Secure - OIDC)",
        "run_id": "9876543210"
      }
    }
  }
}

لاحظ غنى مسار التدقيق هذا: يمكنك رؤية المستودع والفرع و commit SHA ومستخدم GitHub الذي أطلق سير العمل واسم سير العمل بالضبط. هذا أكثر تفصيلاً بكثير مما تحصل عليه مع بيانات اعتماد مستخدم IAM الثابتة.

الخطوة 3: إنشاء إنذار CloudWatch لمحاولات AssumeRole الفاشلة

قد تشير استدعاءات AssumeRoleWithWebIdentity الفاشلة إلى محاولات وصول غير مصرح بها أو سياسات ثقة خاطئة التكوين. قم بإعداد فلتر مقياس وإنذار:

# Create a CloudWatch Logs metric filter for failed AssumeRoleWithWebIdentity
aws logs put-metric-filter \
  --log-group-name CloudTrail/DefaultLogGroup \
  --filter-name FailedOIDCAssumeRole \
  --filter-pattern '{ ($.eventName = "AssumeRoleWithWebIdentity") && ($.errorCode = "AccessDenied") }' \
  --metric-transformations \
    metricName=FailedOIDCAssumeRoleCount,metricNamespace=SecurityMetrics,metricValue=1

# Create a CloudWatch alarm that triggers when failures exceed threshold
aws cloudwatch put-metric-alarm \
  --alarm-name FailedOIDCAssumeRole \
  --alarm-description "Alert on failed OIDC AssumeRoleWithWebIdentity attempts" \
  --metric-name FailedOIDCAssumeRoleCount \
  --namespace SecurityMetrics \
  --statistic Sum \
  --period 300 \
  --threshold 3 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts \
  --treat-missing-data notBreaching

الخطوة 4: تدقيق المستودعات والفروع التي وصلت إلى الدور

استخدم CloudTrail Lake أو Athena للاستعلام عن أنماط الوصول التاريخية:

# Using CloudTrail lookup to find all OIDC authentications in the last 24 hours
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --start-time $(date -u -d '24 hours ago' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u -v-24H '+%Y-%m-%dT%H:%M:%SZ') \
  --end-time $(date -u '+%Y-%m-%dT%H:%M:%SZ') \
  --query 'Events[].CloudTrailEvent' \
  --output text | jq -r '.responseElements.subjectFromWebIdentityToken // "N/A"' | sort | uniq -c | sort -rn

هذا يعطيك عدد تكرار المستودعات والفروع التي تمت مصادقتها عبر OIDC — ضروري لمراجعات الوصول وعمليات تدقيق الامتثال.

التمرين 7: حذف مفاتيح الوصول القديمة

هذا هو أهم تمرين في المختبر بأكمله. الترحيل من بيانات الاعتماد الثابتة إلى OIDC غير مكتمل — ووضعك الأمني لم يتحسن — حتى يتم التخلص من مفاتيح الوصول القديمة. طالما أن كلتا طريقتي المصادقة موجودتان، يمكن للمهاجم استخدام المفاتيح الثابتة.

الخطوة 1: إزالة أسرار GitHub

احذف الأسرار القديمة من مستودع GitHub الخاص بك:

gh secret delete AWS_ACCESS_KEY_ID --repo myorg/myrepo
gh secret delete AWS_SECRET_ACCESS_KEY --repo myorg/myrepo

تحقق من إزالتها:

gh secret list --repo myorg/myrepo

يجب ألا ترى AWS_ACCESS_KEY_ID أو AWS_SECRET_ACCESS_KEY في القائمة بعد الآن.

الخطوة 2: تعطيل مفاتيح وصول IAM

قبل الحذف، قم بتعطيل المفاتيح أولاً. هذا يمنحك خيار التراجع في حالة حدوث خطأ:

# List the access keys for the IAM user
aws iam list-access-keys --user-name github-deploy-user

# Deactivate (not delete) the access key
aws iam update-access-key \
  --user-name github-deploy-user \
  --access-key-id AKIAIOSFODNN7EXAMPLE \
  --status Inactive

الخطوة 3: التحقق من أن خط الأنابيب لا يزال يعمل

أطلق سير عمل OIDC وتأكد من اكتماله بنجاح:

git commit --allow-empty -m "Verify OIDC-only authentication"
git push origin main

تحقق من تبويب Actions — يجب أن ينجح سير العمل باستخدام OIDC فقط.

الخطوة 4: حذف مفاتيح الوصول نهائياً

بعد التأكد من أن خط الأنابيب يعمل بدون مفاتيح ثابتة (انتظر 24-48 ساعة على الأقل لتكون آمناً)، احذف المفاتيح نهائياً:

aws iam delete-access-key \
  --user-name github-deploy-user \
  --access-key-id AKIAIOSFODNN7EXAMPLE

إذا كان مستخدم IAM يُستخدم فقط لـ GitHub Actions، احذف المستخدم بالكامل:

aws iam delete-user-policy --user-name github-deploy-user --policy-name DeployPolicy
aws iam delete-user --user-name github-deploy-user

الترحيل الآن مكتمل. يتصل خط الأنابيب الخاص بك حصرياً عبر اتحاد OIDC بدون أي بيانات اعتماد مخزنة.

التنظيف

إذا كانت هذه بيئة مختبرية، قم بتنظيف جميع الموارد:

# Delete IAM roles
aws iam delete-role-policy --role-name github-actions-deploy --policy-name S3ReadAccess
aws iam delete-role --role-name github-actions-deploy

aws iam delete-role-policy --role-name github-actions-staging --policy-name StagingPolicy
aws iam delete-role --role-name github-actions-staging

aws iam delete-role-policy --role-name github-actions-production --policy-name ProductionPolicy
aws iam delete-role --role-name github-actions-production

# Delete the OIDC provider
OIDC_ARN=$(aws iam list-open-id-connect-providers \
  --query "OpenIDConnectProviderList[?ends_with(Arn, 'token.actions.githubusercontent.com')].Arn" \
  --output text)
aws iam delete-open-id-connect-provider --open-id-connect-provider-arn "$OIDC_ARN"

# Delete the test repository (optional)
gh repo delete myorg/oidc-lab --yes

إذا استخدمت Terraform، فالتنظيف أبسط:

terraform destroy -auto-approve

النقاط الرئيسية

  • يقضي اتحاد OIDC على بيانات الاعتماد المخزنة. لا AWS_ACCESS_KEY_ID أو AWS_SECRET_ACCESS_KEY في GitHub — علاقة الثقة بين مزود هوية GitHub ودور IAM في AWS الخاص بك.
  • بيانات الاعتماد قصيرة الأمد تقلل نطاق الضرر. تنتهي صلاحية رموز OIDC وبيانات اعتماد جلسة AWS الناتجة في دقائق، وليس أشهر. الرمز المخترق لا فائدة منه بعد انتهاء الصلاحية.
  • توفر سياسات الثقة تحكماً في الوصول قائماً على المطالبات. تشفّر مطالبة sub في رمز OIDC المستودع والفرع والبيئة — استخدم شروط StringEquals و StringLike لفرض وصول دقيق.
  • تفرض الأدوار لكل بيئة فصل المهام. يجب أن تستخدم بيئتا التجهيز والإنتاج أدوار IAM مختلفة مع سياسات ثقة مختلفة ومستويات صلاحيات مختلفة.
  • يوفر CloudTrail مسارات تدقيق كاملة. يسجل كل حدث مصادقة OIDC المستودع والفرع و commit والفاعل وسير العمل — أغنى بكثير من تدقيق بيانات الاعتماد الثابتة.
  • الترحيل غير مكتمل حتى يتم حذف المفاتيح القديمة. تشغيل OIDC بالتوازي مع المفاتيح الثابتة لا يمنحك أي تحسين أمني. احذف بيانات الاعتماد القديمة بعد التحقق من عمل OIDC.

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

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