如何在GitHub Actions中通过OIDC/IAM角色从AWS ECR拉取自定义容器?
问题描述
我需要在GitHub Actions作业中使用自定义容器执行任务,目前能通过提供用户名密码凭证从AWS ECR拉取镜像,但希望改用OIDC和IAM角色实现。当前的方案是拆分两个作业,先获取短期凭证再传递给容器作业,代码如下:
--- name: Manual test "on": workflow_dispatch: jobs: get_creds: permissions: id-token: write # 用于请求JWT contents: read # 用于actions/checkout runs-on: [self-hosted] steps: - name: configure aws credentials uses: aws-actions/configure-aws-credentials@v3 with: role-to-assume: arn:aws:iam::123456:role/GithubWorkflow aws-region: us-east-1 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 with: mask-password: 'false' outputs: docker_username: ${{ steps.login-ecr.outputs.docker_username_123456_dkr_ecr_us_east_1_amazonaws_com }} docker_password: ${{ steps.login-ecr.outputs.docker_password_123456_dkr_ecr_us_east_1_amazonaws_com }} job1: needs: [get_creds] permissions: id-token: write contents: read runs-on: [self-hosted] container: image: 123456.dkr.ecr.us-east-1.amazonaws.com/3rdparty/library/alpine:3.18.4 credentials: username: ${{ needs.get_creds.outputs.docker_username }} password: ${{ needs.get_creds.outputs.docker_password }} steps: - name: Step1 run: | cat /etc/os-release
(上述代码中的alpine仅为调试示例)
想问有没有更简便的实现方案?另外我考虑过把aws-actions/configure-aws-credentials和aws-actions/amazon-ecr-login放到Runner文档提到的prepare_job阶段,但不确定Runner是否具备承担对应角色所需的OIDC令牌,也看不懂这些Action的TypeScript代码逻辑。
简化方案一:利用自托管Runner的prepare_job钩子(推荐)
这种方案不需要拆分作业,直接在Runner拉取容器镜像前完成OIDC认证和ECR登录,彻底省去凭证传递的步骤:
配置自托管Runner的
prepare_job钩子
在自托管Runner的安装目录下,找到_work/_hooks文件夹,创建(或修改)prepare_job.sh(Linux/macOS)脚本,添加以下逻辑:#!/bin/bash # 确保Runner已安装aws-cli和jq # 获取GitHub OIDC令牌 ID_TOKEN=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq -r '.value') # 通过OIDC Assume角色获取临时AWS凭证 STS_RESPONSE=$(aws sts assume-role-with-web-identity \ --role-arn arn:aws:iam::123456:role/GithubWorkflow \ --role-session-name "github-runner-$(date +%s)" \ --web-identity-token "$ID_TOKEN" \ --duration-seconds 3600) # 导出临时凭证到环境变量 export AWS_ACCESS_KEY_ID=$(echo "$STS_RESPONSE" | jq -r '.Credentials.AccessKeyId') export AWS_SECRET_ACCESS_KEY=$(echo "$STS_RESPONSE" | jq -r '.Credentials.SecretAccessKey') export AWS_SESSION_TOKEN=$(echo "$STS_RESPONSE" | jq -r '.Credentials.SessionToken') # 登录AWS ECR aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456.dkr.ecr.us-east-1.amazonaws.com注意:需要确保Runner的IAM身份(或实例角色)允许调用
sts:AssumeRoleWithWebIdentity,且目标IAM角色的信任策略已配置为信任GitHub OIDC提供商。简化作业配置
此时作业无需处理凭证传递,直接指定镜像即可:name: Manual test on: workflow_dispatch: jobs: job1: permissions: id-token: write contents: read runs-on: [self-hosted] container: image: 123456.dkr.ecr.us-east-1.amazonaws.com/3rdparty/library/alpine:3.18.4 steps: - name: Step1 run: | cat /etc/os-release
简化方案二:优化多作业凭证传递
如果不想修改Runner钩子,可以优化现有多作业方案,利用amazon-ecr-login的通用输出变量,避免硬编码仓库名后缀:
name: Manual test on: workflow_dispatch: jobs: get_creds: permissions: id-token: write contents: read runs-on: [self-hosted] steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v3 with: role-to-assume: arn:aws:iam::123456:role/GithubWorkflow aws-region: us-east-1 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 outputs: registry: ${{ steps.login-ecr.outputs.registry }} docker_username: ${{ steps.login-ecr.outputs.docker_username }} docker_password: ${{ steps.login-ecr.outputs.docker_password }} job1: needs: [get_creds] permissions: id-token: write contents: read runs-on: [self-hosted] container: image: ${{ needs.get_creds.outputs.registry }}/3rdparty/library/alpine:3.18.4 credentials: username: ${{ needs.get_creds.outputs.docker_username }} password: ${{ needs.get_creds.outputs.docker_password }} steps: - name: Step1 run: | cat /etc/os-release
关于prepare_job阶段的OIDC令牌问题
自托管Runner在执行prepare_job钩子时,会自动获取GitHub提供的OIDC令牌,相关环境变量ACTIONS_ID_TOKEN_REQUEST_URL和ACTIONS_ID_TOKEN_REQUEST_TOKEN会被注入到钩子的执行环境中,完全可以用来调用STS服务完成角色假设,无需额外配置。
内容的提问来源于stack exchange,提问作者Tomas Kouba

