github-actions-oidc-aws
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGitHub Actions OIDC Authentication for AWS
面向AWS的GitHub Actions OIDC身份认证
This is a reference pattern. Learn from the approach, adapt to your context — don't copy verbatim.
Status: 🔴 CRITICAL PATTERN
Category: CI/CD / Infrastructure
Applies To: Any project using GitHub Actions to deploy to AWS
Category: CI/CD / Infrastructure
Applies To: Any project using GitHub Actions to deploy to AWS
这是一个参考模式。 请学习其实现思路,适配你的业务场景,不要直接原样复制。
状态: 🔴 关键模式
分类: CI/CD / 基础设施
适用场景: 所有使用GitHub Actions部署到AWS的项目
分类: CI/CD / 基础设施
适用场景: 所有使用GitHub Actions部署到AWS的项目
Overview
概述
Secure authentication pattern for GitHub Actions workflows to access AWS resources using OpenID Connect (OIDC) instead of long-lived IAM credentials. Eliminates the need to store AWS access keys in GitHub secrets.
Key Benefits:
- No long-lived credentials to rotate or leak
- Temporary credentials with automatic expiration
- Repository-scoped access control
- Audit trail via AWS CloudTrail
- Industry best practice (AWS + GitHub recommended)
这是一种GitHub Actions工作流访问AWS资源的安全认证模式,使用OpenID Connect(OIDC)替代长期有效的IAM凭证,无需在GitHub secrets中存储AWS访问密钥。
核心优势:
- 无需轮换或担心泄露长期有效凭证
- 使用自动过期的临时凭证
- 仓库级别的访问权限控制
- 可通过AWS CloudTrail生成审计日志
- 行业最佳实践(AWS和GitHub官方推荐)
Problem
存在的问题
GitHub Actions workflows need to authenticate to AWS to deploy infrastructure, trigger pipelines, or manage resources. Traditional approaches have security issues:
Anti-Pattern: Static IAM Credentials
yaml
undefinedGitHub Actions工作流在部署基础设施、触发流水线或管理资源时需要向AWS进行身份认证,传统实现方式存在安全隐患:
反模式:静态IAM凭证
yaml
undefined❌ Security risk: long-lived credentials in secrets
❌ Security risk: long-lived credentials in secrets
- 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 }}
**Problems**:
- Credentials never expire (must be manually rotated)
- If leaked, attacker has persistent access
- No way to scope to specific repositories
- Difficult to audit which workflow used credentials
- Violates principle of least privilege
---- 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 }}
**存在的问题**:
- 凭证永不过期(必须手动轮换)
- 一旦泄露,攻击者将获得持久访问权限
- 无法限定仅特定仓库可使用凭证
- 难以审计哪个工作流使用了凭证
- 违反最小权限原则
---Solution
解决方案
Use GitHub's OIDC provider to issue temporary credentials via AWS IAM role assumption.
Flow:
GitHub Actions → OIDC Token → AWS STS → Temporary Credentials → AWS ResourcesHow it works:
- GitHub Actions requests OIDC token from GitHub
- Workflow presents token to AWS STS
- AWS validates token against IAM OIDC provider
- AWS issues temporary credentials (valid 1 hour)
- Workflow uses temporary credentials to access AWS
使用GitHub的OIDC提供商,通过AWS IAM角色Assume机制下发临时凭证。
流程:
GitHub Actions → OIDC Token → AWS STS → Temporary Credentials → AWS Resources工作原理:
- GitHub Actions向GitHub请求OIDC令牌
- 工作流向AWS STS提交令牌
- AWS通过IAM OIDC提供商验证令牌有效性
- AWS下发临时凭证(有效期1小时)
- 工作流使用临时凭证访问AWS资源
Components
组件说明
1. AWS IAM OIDC Provider
1. AWS IAM OIDC提供商
Establishes trust between AWS and GitHub's OIDC issuer.
Configuration:
- URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com - Thumbprint: (GitHub root CA)
1c58a3a8518e8759bf075b76b750d4f2df264fcd
Why root CA thumbprint?
- More stable than intermediate certificate
- GitHub can rotate intermediate certs without breaking trust
- Recommended by AWS documentation
用于建立AWS和GitHub OIDC签发方之间的信任关系。
配置:
- URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com - Thumbprint: (GitHub根CA证书)
1c58a3a8518e8759bf075b76b750d4f2df264fcd
为什么使用根CA指纹?
- 比中间证书更稳定
- GitHub轮换中间证书时不会破坏信任关系
- AWS官方文档推荐方案
2. IAM Role with Trust Policy
2. 配置信任策略的IAM角色
Role that GitHub Actions can assume, with trust policy restricting access.
Trust Policy Structure:
json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::{account}: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:{owner}/{repo}:*"
}
}
}]
}Trust Policy Scoping Options:
undefinedGitHub Actions可Assume的角色,通过信任策略限制访问权限。
信任策略结构:
json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::{account}: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:{owner}/{repo}:*"
}
}
}]
}信任策略范围配置选项:
undefinedAll branches and tags
All branches and tags
"repo:owner/repo:*"
"repo:owner/repo:*"
Specific branch only
Specific branch only
"repo:owner/repo:ref:refs/heads/main"
"repo:owner/repo:ref:refs/heads/main"
Multiple branches
Multiple branches
["repo:owner/repo:ref:refs/heads/main", "repo:owner/repo:ref:refs/heads/dev"]
["repo:owner/repo:ref:refs/heads/main", "repo:owner/repo:ref:refs/heads/dev"]
Pull requests
Pull requests
"repo:owner/repo:pull_request"
"repo:owner/repo:pull_request"
Environment-specific
Environment-specific
"repo:owner/repo:environment:production"
undefined"repo:owner/repo:environment:production"
undefined3. IAM Permissions Policy
3. IAM权限策略
Defines what the role can do in AWS (principle of least privilege).
Examples by use case:
Pipeline Trigger Only:
json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "codepipeline:StartPipelineExecution",
"Resource": "arn:aws:codepipeline:*:{account}:pipeline-name-*"
}]
}Direct S3 Deployment:
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::bucket-name", "arn:aws:s3:::bucket-name/*"]
},
{
"Effect": "Allow",
"Action": "cloudfront:CreateInvalidation",
"Resource": "*"
}
]
}Infrastructure Deployment (use with caution):
json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]
}定义角色在AWS中的可执行操作(遵循最小权限原则)。
不同场景的示例:
仅用于触发流水线:
json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "codepipeline:StartPipelineExecution",
"Resource": "arn:aws:codepipeline:*:{account}:pipeline-name-*"
}]
}直接部署到S3:
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::bucket-name", "arn:aws:s3:::bucket-name/*"]
},
{
"Effect": "Allow",
"Action": "cloudfront:CreateInvalidation",
"Resource": "*"
}
]
}基础设施部署(谨慎使用):
json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]
}4. GitHub Actions Workflow Configuration
4. GitHub Actions工作流配置
Workflow must request OIDC token and assume role.
Required Permissions:
yaml
permissions:
id-token: write # Required for OIDC token
contents: read # Required to checkout codeAuthentication Step:
yaml
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::{account}:role/{role-name}
aws-region: {region}工作流必须请求OIDC令牌并Assume对应角色。
所需权限:
yaml
permissions:
id-token: write # Required for OIDC token
contents: read # Required to checkout code认证步骤:
yaml
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::{account}:role/{role-name}
aws-region: {region}Implementation
实现步骤
Step 1: Create OIDC Provider (One-Time Setup)
步骤1:创建OIDC提供商(仅需配置一次)
Choose your infrastructure tool:
AWS CDK (TypeScript):
typescript
import * as iam from 'aws-cdk-lib/aws-iam';
const provider = new iam.OpenIdConnectProvider(this, 'GitHubProvider', {
url: 'https://token.actions.githubusercontent.com',
clientIds: ['sts.amazonaws.com'],
thumbprints: ['1c58a3a8518e8759bf075b76b750d4f2df264fcd']
});Terraform:
hcl
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["1c58a3a8518e8759bf075b76b750d4f2df264fcd"]
}AWS CLI:
bash
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 1c58a3a8518e8759bf075b76b750d4f2df264fcdCloudFormation:
yaml
GitHubOIDCProvider:
Type: AWS::IAM::OIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- sts.amazonaws.com
ThumbprintList:
- 1c58a3a8518e8759bf075b76b750d4f2df264fcd选择你使用的基础设施工具:
AWS CDK (TypeScript):
typescript
import * as iam from 'aws-cdk-lib/aws-iam';
const provider = new iam.OpenIdConnectProvider(this, 'GitHubProvider', {
url: 'https://token.actions.githubusercontent.com',
clientIds: ['sts.amazonaws.com'],
thumbprints: ['1c58a3a8518e8759bf075b76b750d4f2df264fcd']
});Terraform:
hcl
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["1c58a3a8518e8759bf075b76b750d4f2df264fcd"]
}AWS CLI:
bash
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 1c58a3a8518e8759bf075b76b750d4f2df264fcdCloudFormation:
yaml
GitHubOIDCProvider:
Type: AWS::IAM::OIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- sts.amazonaws.com
ThumbprintList:
- 1c58a3a8518e8759bf075b76b750d4f2df264fcdStep 2: Create IAM Role
步骤2:创建IAM角色
AWS CDK (TypeScript):
typescript
const role = new iam.Role(this, 'GitHubActionsRole', {
roleName: 'GitHubActionsRole',
assumedBy: new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, {
StringEquals: {
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com'
},
StringLike: {
'token.actions.githubusercontent.com:sub': `repo:${owner}/${repo}:*`
}
}),
inlinePolicies: {
DeploymentPermissions: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['codepipeline:StartPipelineExecution'],
resources: [`arn:aws:codepipeline:*:${this.account}:pipeline-*`]
})
]
})
}
});Terraform:
hcl
resource "aws_iam_role" "github_actions" {
name = "GitHubActionsRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:${var.github_owner}/${var.github_repo}:*"
}
}
}]
})
}
resource "aws_iam_role_policy" "github_actions" {
name = "deployment-permissions"
role = aws_iam_role.github_actions.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "codepipeline:StartPipelineExecution"
Resource = "arn:aws:codepipeline:*:${data.aws_caller_identity.current.account_id}:pipeline-*"
}]
})
}AWS CDK (TypeScript):
typescript
const role = new iam.Role(this, 'GitHubActionsRole', {
roleName: 'GitHubActionsRole',
assumedBy: new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, {
StringEquals: {
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com'
},
StringLike: {
'token.actions.githubusercontent.com:sub': `repo:${owner}/${repo}:*`
}
}),
inlinePolicies: {
DeploymentPermissions: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['codepipeline:StartPipelineExecution'],
resources: [`arn:aws:codepipeline:*:${this.account}:pipeline-*`]
})
]
})
}
});Terraform:
hcl
resource "aws_iam_role" "github_actions" {
name = "GitHubActionsRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:${var.github_owner}/${var.github_repo}:*"
}
}
}]
})
}
resource "aws_iam_role_policy" "github_actions" {
name = "deployment-permissions"
role = aws_iam_role.github_actions.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "codepipeline:StartPipelineExecution"
Resource = "arn:aws:codepipeline:*:${data.aws_caller_identity.current.account_id}:pipeline-*"
}]
})
}Step 3: Update GitHub Actions Workflow
步骤3:更新GitHub Actions工作流
Before (Static Credentials):
yaml
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- 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
- run: aws s3 sync ./dist s3://my-bucketAfter (OIDC):
yaml
name: Deploy
on: [push]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
- run: aws s3 sync ./dist s3://my-bucket改造前(使用静态凭证):
yaml
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- 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
- run: aws s3 sync ./dist s3://my-bucket改造后(使用OIDC):
yaml
name: Deploy
on: [push]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
- run: aws s3 sync ./dist s3://my-bucketStep 4: Remove Old Secrets (Cleanup)
步骤4:删除旧的Secrets(清理)
bash
undefinedbash
undefinedList current secrets
List current secrets
gh secret list
gh secret list
Remove old credentials
Remove old credentials
gh secret remove AWS_ACCESS_KEY_ID
gh secret remove AWS_SECRET_ACCESS_KEY
---gh secret remove AWS_ACCESS_KEY_ID
gh secret remove AWS_SECRET_ACCESS_KEY
---Configuration Management
配置管理
Avoid Hardcoding Account IDs
避免硬编码账号ID
Anti-Pattern:
yaml
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRoleBetter: Use Repository Variables:
yaml
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRoleBest: Read from Configuration File:
yaml
- name: Load config
id: config
run: |
echo "account=$(grep '^AWS_ACCOUNT_ID=' .env | cut -d'=' -f2)" >> $GITHUB_OUTPUT
echo "region=$(grep '^AWS_REGION=' .env | cut -d'=' -f2)" >> $GITHUB_OUTPUT
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ steps.config.outputs.account }}:role/GitHubActionsRole
aws-region: ${{ steps.config.outputs.region }}反模式:
yaml
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole更优方案:使用仓库变量:
yaml
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole最佳方案:从配置文件读取:
yaml
- name: Load config
id: config
run: |
echo "account=$(grep '^AWS_ACCOUNT_ID=' .env | cut -d'=' -f2)" >> $GITHUB_OUTPUT
echo "region=$(grep '^AWS_REGION=' .env | cut -d'=' -f2)" >> $GITHUB_OUTPUT
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ steps.config.outputs.account }}:role/GitHubActionsRole
aws-region: ${{ steps.config.outputs.region }}Deployment Patterns
部署模式
Pattern A: Pipeline Trigger
模式A:触发流水线
GitHub Actions triggers AWS CodePipeline, which handles actual deployment.
Use When:
- Complex multi-stage deployments
- Need AWS-native deployment history
- Want to trigger from multiple sources
- Build requires significant compute resources
Workflow:
yaml
permissions:
id-token: write
contents: read
jobs:
trigger:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole
aws-region: ${{ vars.AWS_REGION }}
- run: aws codepipeline start-pipeline-execution --name my-pipelineIAM Permissions:
json
{
"Effect": "Allow",
"Action": "codepipeline:StartPipelineExecution",
"Resource": "arn:aws:codepipeline:*:*:pipeline-name"
}GitHub Actions触发AWS CodePipeline,由CodePipeline执行实际部署操作。
适用场景:
- 复杂的多阶段部署
- 需要AWS原生的部署历史记录
- 需要支持多来源触发
- 构建过程需要大量计算资源
工作流:
yaml
permissions:
id-token: write
contents: read
jobs:
trigger:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole
aws-region: ${{ vars.AWS_REGION }}
- run: aws codepipeline start-pipeline-execution --name my-pipelineIAM权限:
json
{
"Effect": "Allow",
"Action": "codepipeline:StartPipelineExecution",
"Resource": "arn:aws:codepipeline:*:*:pipeline-name"
}Pattern B: Direct Deployment
模式B:直接部署
GitHub Actions performs full deployment (build + deploy).
Use When:
- Simple static site deployments
- Want fast feedback loops
- Prefer GitHub Actions native features
- Cost-conscious (avoid CodePipeline/CodeBuild costs)
Workflow:
yaml
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci && npm run build
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole
aws-region: ${{ vars.AWS_REGION }}
- run: |
aws s3 sync dist/ s3://my-bucket/ --delete
aws cloudfront create-invalidation --distribution-id ${{ vars.CF_DIST_ID }} --paths "/*"IAM Permissions:
json
{
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::bucket/*", "arn:aws:s3:::bucket"]
},
{
"Effect": "Allow",
"Action": "cloudfront:CreateInvalidation",
"Resource": "*"
}
]
}GitHub Actions执行完整部署流程(构建+部署)。
适用场景:
- 简单的静态站点部署
- 需要快速的反馈链路
- 偏好使用GitHub Actions原生功能
- 对成本敏感(避免CodePipeline/CodeBuild的费用)
工作流:
yaml
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci && npm run build
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole
aws-region: ${{ vars.AWS_REGION }}
- run: |
aws s3 sync dist/ s3://my-bucket/ --delete
aws cloudfront create-invalidation --distribution-id ${{ vars.CF_DIST_ID }} --paths "/*"IAM权限:
json
{
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::bucket/*", "arn:aws:s3:::bucket"]
},
{
"Effect": "Allow",
"Action": "cloudfront:CreateInvalidation",
"Resource": "*"
}
]
}Pattern C: Infrastructure Deployment
模式C:基础设施部署
GitHub Actions deploys infrastructure changes (CDK, Terraform, CloudFormation).
Use When:
- Infrastructure as Code workflows
- Want PR-based infrastructure reviews
- Need to validate changes before merge
Workflow:
yaml
permissions:
id-token: write
contents: read
pull-requests: write # For PR comments
jobs:
plan:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsTerraformRole
aws-region: ${{ vars.AWS_REGION }}
- run: terraform plan -out=plan.tfplan
- uses: actions/github-script@v7
with:
script: |
// Post plan to PR comment
apply:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsTerraformRole
aws-region: ${{ vars.AWS_REGION }}
- run: terraform apply -auto-approveIAM Permissions: Typically requires broad permissions (AdministratorAccess or PowerUserAccess). Consider using separate roles for plan (read-only) vs apply (write).
GitHub Actions部署基础设施变更(CDK、Terraform、CloudFormation)。
适用场景:
- 基础设施即代码工作流
- 需要基于PR的基础设施变更审核
- 需要在合并前验证变更的正确性
工作流:
yaml
permissions:
id-token: write
contents: read
pull-requests: write # For PR comments
jobs:
plan:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsTerraformRole
aws-region: ${{ vars.AWS_REGION }}
- run: terraform plan -out=plan.tfplan
- uses: actions/github-script@v7
with:
script: |
// Post plan to PR comment
apply:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsTerraformRole
aws-region: ${{ vars.AWS_REGION }}
- run: terraform apply -auto-approveIAM权限: 通常需要较宽泛的权限(AdministratorAccess或PowerUserAccess)。建议为plan(只读)和apply(写入)操作使用独立的角色。
Multi-Environment Strategy
多环境策略
Separate Roles per Environment
每个环境使用独立角色
Recommended: Create separate roles for dev/staging/prod with different permissions.
CDK Example:
typescript
['dev', 'prod'].forEach(env => {
new iam.Role(this, `GitHubActionsRole-${env}`, {
roleName: `GitHubActionsRole-${env}`,
assumedBy: new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, {
StringEquals: {
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com'
},
StringLike: {
'token.actions.githubusercontent.com:sub':
env === 'prod'
? `repo:${owner}/${repo}:ref:refs/heads/main`
: `repo:${owner}/${repo}:ref:refs/heads/dev`
}
})
});
});Workflow:
yaml
- name: Determine environment
id: env
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "name=prod" >> $GITHUB_OUTPUT
else
echo "name=dev" >> $GITHUB_OUTPUT
fi
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole-${{ steps.env.outputs.name }}
aws-region: ${{ vars.AWS_REGION }}推荐方案: 为开发/测试/生产环境创建独立角色,配置不同的权限。
CDK示例:
typescript
['dev', 'prod'].forEach(env => {
new iam.Role(this, `GitHubActionsRole-${env}`, {
roleName: `GitHubActionsRole-${env}`,
assumedBy: new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, {
StringEquals: {
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com'
},
StringLike: {
'token.actions.githubusercontent.com:sub':
env === 'prod'
? `repo:${owner}/${repo}:ref:refs/heads/main`
: `repo:${owner}/${repo}:ref:refs/heads/dev`
}
})
});
});工作流:
yaml
- name: Determine environment
id: env
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "name=prod" >> $GITHUB_OUTPUT
else
echo "name=dev" >> $GITHUB_OUTPUT
fi
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole-${{ steps.env.outputs.name }}
aws-region: ${{ vars.AWS_REGION }}Security Considerations
安全注意事项
1. Principle of Least Privilege
1. 最小权限原则
Always grant minimum permissions required:
json
// ❌ Too broad
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
// ✅ Specific
{
"Effect": "Allow",
"Action": "codepipeline:StartPipelineExecution",
"Resource": "arn:aws:codepipeline:us-east-1:123456789012:my-pipeline"
}始终授予所需的最小权限:
json
// ❌ Too broad
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
// ✅ Specific
{
"Effect": "Allow",
"Action": "codepipeline:StartPipelineExecution",
"Resource": "arn:aws:codepipeline:us-east-1:123456789012:my-pipeline"
}2. Repository Scoping
2. 仓库范围限定
Always restrict to specific repository:
json
// ❌ Any repository in organization
"token.actions.githubusercontent.com:sub": "repo:my-org/*"
// ✅ Specific repository
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
// ✅ Even more specific (main branch only)
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"始终限定仅特定仓库可访问:
json
// ❌ Any repository in organization
"token.actions.githubusercontent.com:sub": "repo:my-org/*"
// ✅ Specific repository
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
// ✅ Even more specific (main branch only)
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"3. Audience Validation
3. 受众验证
Always validate audience:
json
{
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}始终验证受众:
json
{
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}4. Session Duration
4. 会话时长
Default: 1 hour (sufficient for most workflows)
Custom duration (if needed):
yaml
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
role-duration-seconds: 3600 # 1 hour (default)
aws-region: us-east-1默认值: 1小时(满足绝大多数工作流需求)
自定义时长(如有需要):
yaml
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
role-duration-seconds: 3600 # 1 hour (default)
aws-region: us-east-15. CloudTrail Auditing
5. CloudTrail审计
Monitor role assumptions:
bash
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
--max-results 10Key fields to monitor:
- : GitHub repository and workflow
userIdentity.principalId - : Which role was assumed
requestParameters.roleArn - : GitHub Actions IP range
sourceIPAddress - : GitHub Actions user agent
userAgent
监控角色Assume操作:
bash
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
--max-results 10需要监控的关键字段:
- : GitHub仓库和工作流信息
userIdentity.principalId - : 被Assume的角色ARN
requestParameters.roleArn - : GitHub Actions IP段
sourceIPAddress - : GitHub Actions user agent
userAgent
Troubleshooting
问题排查
Error: "Not authorized to perform sts:AssumeRoleWithWebIdentity"
错误:"Not authorized to perform sts:AssumeRoleWithWebIdentity"
Cause: Trust policy doesn't match workflow context.
Check:
- Repository name matches trust policy
- Branch/tag matches trust policy condition
- Workflow has permission
id-token: write
Debug:
yaml
- name: Debug OIDC token
run: |
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq原因: 信任策略和工作流上下文不匹配。
检查项:
- 仓库名称和信任策略配置匹配
- 分支/标签符合信任策略的条件
- 工作流配置了权限
id-token: write
调试方法:
yaml
- name: Debug OIDC token
run: |
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jqError: "OpenIDConnect provider not found"
错误:"OpenIDConnect provider not found"
Cause: OIDC provider not created or wrong ARN.
Fix:
bash
undefined原因: OIDC提供商未创建或ARN配置错误。
修复方案:
bash
undefinedList providers
List providers
aws iam list-open-id-connect-providers
aws iam list-open-id-connect-providers
Check provider details
Check provider details
aws iam get-open-id-connect-provider
--open-id-connect-provider-arn arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com
--open-id-connect-provider-arn arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com
undefinedaws iam get-open-id-connect-provider
--open-id-connect-provider-arn arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com
--open-id-connect-provider-arn arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com
undefinedError: "Access Denied" after successful authentication
错误:认证成功后提示"Access Denied"
Cause: Role lacks required permissions.
Fix: Update role's permissions policy to include required actions.
原因: 角色缺少所需的权限。
修复方案: 更新角色的权限策略,添加所需的操作权限。
Workflow doesn't request OIDC token
工作流未请求OIDC令牌
Cause: Missing permission.
id-token: writeFix:
yaml
permissions:
id-token: write # Add this
contents: read原因: 缺少权限配置。
id-token: write修复方案:
yaml
permissions:
id-token: write # Add this
contents: readMigration Checklist
迁移检查清单
Migrating from static credentials to OIDC:
- Create OIDC provider in AWS account
- Create IAM role with trust policy
- Attach permissions policy to role
- Test role assumption manually (optional)
- Update workflow to use OIDC
- Add block to workflow
permissions - Replace credential secrets with role ARN
- Test workflow in non-production environment
- Verify CloudTrail logs show role assumption
- Deploy to production
- Remove old AWS credential secrets from GitHub
- Revoke/delete old IAM user (if applicable)
- Document role ARN and permissions
从静态凭证迁移到OIDC的检查项:
- 在AWS账号中创建OIDC提供商
- 创建配置了信任策略的IAM角色
- 为角色绑定权限策略
- 手动测试角色Assume操作(可选)
- 更新工作流使用OIDC认证
- 为工作流添加配置块
permissions - 用角色ARN替换凭证secrets
- 在非生产环境测试工作流
- 验证CloudTrail日志中有角色Assume记录
- 部署到生产环境
- 从GitHub中删除旧的AWS凭证secrets
- 吊销/删除旧的IAM用户(如有)
- 记录角色ARN和权限配置
References
参考资料
Progressive Improvement
持续优化
If the developer corrects a behavior that this skill should have prevented, suggest a specific amendment to this skill to prevent the same correction in the future.
如果开发者修正了本指南本应避免的错误行为,请提出具体的修订建议,避免未来再次出现同类问题。