GitHub Actions:AWS 短期身份与权限验收#
验证:未记录
- 适用版本
- 未记录
- 验证范围
- 尚未登记验证记录。
场景:发布流水线从现有制品桶读取 orders/releases/,不得访问其他应用目录。建立独立的制品读取角色,用真实资源检验信任边界;应用部署权限另按目标服务授予,不能直接追加管理员权限。
OIDC(OpenID Connect,联合身份认证)交换 STS(Security Token Service,安全令牌服务)短期凭据,仓库不保存 AWS Access Key。以下针对 AWS 商业分区,使用 AWS CLI v2 和 Python 3。
1. 准备身份与制品#
平台管理员在隔离账号准备:已有私有制品桶、orders/releases/ 下真实验收对象,以及不含敏感内容的 other/ 前缀。无公共访问或额外桶策略授权;流水线不负责建桶、写制品或创建自己的角色。
export AWS_ACCOUNT_ID='REPLACE_ACCOUNT_ID'
export AWS_REGION='REPLACE_REGION'
export ARTIFACT_BUCKET='REPLACE_EXISTING_PRIVATE_BUCKET'
export GH_REPOSITORY='REPLACE_OWNER/REPLACE_REPO'
export ROLE_NAME='orders-artifact-reader'
aws sts get-caller-identity
aws iam list-open-id-connect-providers核对管理账号。若已有 token.actions.githubusercontent.com provider,复用并核对 audience 包含 sts.amazonaws.com;仅在不存在时创建:
aws iam create-open-id-connect-provider --url https://token.actions.githubusercontent.com --client-id-list sts.amazonaws.com仓库建立 production Environment,限制部署分支为 main,启用可用的审核保护;仓库套餐不支持所需保护时,不能宣称人工门禁已经生效。保护分支、工作流及配置文件的修改权限。默认 OIDC subject 使用环境时不包含分支,main 限制由 Environment 规则执行;自定义 subject 的仓库需按实际声明调整信任。GitHub AWS 配置说明。
2. 创建限定角色#
用结构化方式生成策略,变量只来自管理员核对的配置:
umask 077
python3 - <<'PYJSON'
import json, os
account, repo, bucket = (os.environ[k] for k in ("AWS_ACCOUNT_ID", "GH_REPOSITORY", "ARTIFACT_BUCKET"))
trust = {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Federated":f"arn:aws:iam::{account}:oidc-provider/token.actions.githubusercontent.com"},"Action":"sts:AssumeRoleWithWebIdentity","Condition":{"StringEquals":{"token.actions.githubusercontent.com:aud":"sts.amazonaws.com","token.actions.githubusercontent.com:sub":f"repo:{repo}:environment:production"}}}]}
access = {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:ListBucket","Resource":f"arn:aws:s3:::{bucket}","Condition":{"StringLike":{"s3:prefix":"orders/releases/*"}}},{"Effect":"Allow","Action":"s3:GetObject","Resource":f"arn:aws:s3:::{bucket}/orders/releases/*"}]}
for name, policy in (("trust.json", trust), ("artifact-read.json", access)):
with open(name, "w") as f: json.dump(policy, f, indent=2)
PYJSON
aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document file://trust.json --max-session-duration 3600
aws iam put-role-policy --role-name "$ROLE_NAME" --policy-name ArtifactRead --policy-document file://artifact-read.json角色存在时先 review 现有授权,不自动覆盖。读取加密对象正文若涉及客户管理密钥,需在指定密钥上另授解密并核对 key policy;下文仅验证对象元数据,不证明正文解密链路。权限边界和组织策略按所在账号要求配置,见 IAM 边界。
3. 工作流实际验收#
在 Environment Variables 配置 AWS_ROLE_ARN、AWS_ACCOUNT_ID、AWS_REGION、ARTIFACT_BUCKET、ARTIFACT_KEY;对象键固定为已验证存在的 orders/releases/...。把以下保存为 .github/workflows/aws-identity.yml,使用临时托管 runner,不运行外部拉取请求代码:
name: AWS identity acceptance
on:
workflow_dispatch:
permissions: {}
jobs:
verify:
environment: production
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
id-token: write
steps:
- uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1
with:
role-to-assume: ${{ vars.AWS_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
allowed-account-ids: ${{ vars.AWS_ACCOUNT_ID }}
role-duration-seconds: 900
role-session-name: gha-${{ github.run_id }}-${{ github.run_attempt }}
unset-current-credentials: true
- name: Verify allowed and denied access
shell: bash
env:
ARTIFACT_BUCKET: ${{ vars.ARTIFACT_BUCKET }}
ARTIFACT_KEY: ${{ vars.ARTIFACT_KEY }}
run: |
set -euo pipefail
umask 077
trap 'rm -f denied.err' EXIT
aws sts get-caller-identity
[[ "$ARTIFACT_KEY" == orders/releases/* ]]
aws s3api head-object --bucket "$ARTIFACT_BUCKET" --key "$ARTIFACT_KEY" --query ContentLength
aws s3api list-objects-v2 --bucket "$ARTIFACT_BUCKET" --prefix orders/releases/ --max-keys 1 --query KeyCount
if aws s3api list-objects-v2 --bucket "$ARTIFACT_BUCKET" --prefix other/ --max-keys 1 --query KeyCount >/dev/null 2>denied.err; then
echo 'FAIL: out-of-scope prefix was allowed'; exit 1
fi
if ! grep -q '(AccessDenied)' denied.err; then
echo 'FAIL: unexpected error; verify network, credentials and audit events'; exit 1
fi
if aws iam list-roles --max-items 1 --query 'length(Roles)' >/dev/null 2>denied.err; then
echo 'FAIL: IAM enumeration was allowed'; exit 1
fi
if ! grep -q '(AccessDenied)' denied.err; then
echo 'FAIL: unexpected IAM test error'; exit 1
fi
echo 'PASS: expected resource and action denials'id-token: write 只允许申请身份令牌,不授予 AWS 资源写权限。Action 固定提交应通过依赖更新流程维护。结果不一致时查实际角色、信任条件、桶策略与组织限制,不追加 s3:*。
4. 拒绝、撤销与清理#
| 验收 | 预期 |
|---|---|
| main → production → 本应用对象 | 会话账号/角色正确,元数据与限定列表成功 |
| 同会话访问 other/ | 必须是 AccessDenied;超时或令牌过期不算通过 |
| 同会话枚举 IAM 角色 | 必须是 AccessDenied,不获得身份管理权限 |
| 非 main 分支请求 production | Environment 阻止,不能进入云认证步骤 |
| 独立测试工作流不声明 Environment | 默认 subject 不匹配,在 AssumeRoleWithWebIdentity 处被拒绝 |
| 管理员移除本角色 ArtifactRead 策略 | 传播完成后,原会话和新会话的对象读取均应失败 |
最后一项仅在本次独立验收角色上执行:aws iam delete-role-policy --role-name "$ROLE_NAME" --policy-name ArtifactRead。重建该策略即可恢复;生产撤权需另评估受影响流水线,并按 已有会话撤销 处理。不要为了测试改变共享 provider 或放宽 Environment 规则。
结束后删除测试工作流和本次角色,保留审计结果;共享 provider、制品桶和业务对象不随实验删除。该验收验证联合身份及制品访问边界,不代替应用发布和运行时权限验收。