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、制品桶和业务对象不随实验删除。该验收验证联合身份及制品访问边界,不代替应用发布和运行时权限验收。

搜索文档

输入关键词,搜索所有文档。

↑ ↓ 选择 · Enter 打开 · Esc 关闭