GitHub ActionsでOIDC経由でAWSリソースにアクセスしてみた

はじめに

何番煎じ案件ですが、社内にて OIDC 経由で GitHub Actions に AWS アクセスしようぜが話題に上がっていたため、自分の方でも素振りをしてみたので備忘録を残しておきます。 社内の強い人たちの足跡をほぼほぼ辿っただけでしたが、永続的な Credentials を Actions に残したくない気持ちは同じだったので本当に助かります :man-bowing:

内容

OIDC 自体の解説はコチラが参考になるかと思います。emoji-pray

まず、AWS 側で GitHub 向けの OIDC と role を設定します。

CDK のサンプルソースが見当たらず、公式が提供する AWS 向けの OIDC カスタムアクション aws-actions/configure-aws-credentials に CFn テンプレートが記述されていたため、それを参考に CDK を起こしています。 CDK で私が記述した場合は以下になります emoji-sweat_drops

// bin/index.ts
const { principal: githubOIDCPrincipal } = new GitHubOIDC(app, "github-oidc", {
  env,
  subject: "repo:sample-org/sample-rep:*",
})

new IamRole(app, "build-role", {
  env,
  roleProps: {
    roleName: "build",
    assumedBy: githubOIDCPrincipal,
    // 操作するリソース向けの policy を設定する必要があります。
    // 以下の policy では、当然 Lambda のデプロイ等はできません。
    managedPolicies: [
      iam.ManagedPolicy.fromAwsManagedPolicyName("AWSCloudFormationFullAccess"),
      iam.ManagedPolicy.fromAwsManagedPolicyName("IAMFullAccess"),
    ],
  },
})
// lib/open-id-connect.ts
import * as cdk from "@aws-cdk/core"
import * as iam from "@aws-cdk/aws-iam"

type GitHubOIDCProps = cdk.StackProps & {
  subject: string
}

export class GitHubOIDC extends cdk.Stack {
  public readonly principal: iam.FederatedPrincipal

  constructor(scope: cdk.App, id: string, props: GitHubOIDCProps) {
    super(scope, id, props)

    const { subject } = props

    // https://github.com/aws-actions/configure-aws-credentials#sample-iam-role-cloudformation-template
    const { openIdConnectProviderArn } = new iam.OpenIdConnectProvider(
      this,
      id,
      {
        url: "https://token.actions.githubusercontent.com",
        clientIds: ["sts.amazonaws.com"],
        thumbprints: ["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"],
      }
    )

    // https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#configuring-the-role-and-trust-policy
    this.principal = new iam.FederatedPrincipal(
      openIdConnectProviderArn,
      {
        StringLike: {
          "token.actions.githubusercontent.com:sub": subject,
        },
      },
      "sts:AssumeRoleWithWebIdentity"
    )
  }
}
// lib/iam.ts

import * as cdk from "@aws-cdk/core"
import * as iam from "@aws-cdk/aws-iam"

type IamRoleProps = cdk.StackProps & {
  roleProps: iam.RoleProps
}

export class IamRole extends cdk.Stack {
  public readonly role: iam.Role

  constructor(scope: cdk.App, id: string, props: IamRoleProps) {
    super(scope, id, props)

    const { roleProps } = props

    this.role = new iam.Role(this, id, {
      ...roleProps,
    })
  }
}

次に Actions になります。 以下は、認証を済ませた後に、Container Image を build、push する CI のサンプルになります。 コチラを参考にしています。

name: sample

on:
  pull_request:
    paths-ignore:
      - "**.md"

jobs:
  build:
    runs-on: ubuntu-20.04
    #
    permissions:
      id-token: write
      contents: read
    env:
      AWS_ROLE_ARN: arn:aws:iam::${お使いのaws-account-id}:role/build
      AWS_WEB_IDENTITY_TOKEN_FILE: /tmp/awscreds
      AWS_DEFAULT_REGION: ap-northeast-1
      AWS_REGION: ap-northeast-1 # cdk内部でsdkを利用する関係で AWS_REGION が必要
      AUDIENCE: sts.amazonaws.com

    steps:
      - uses: actions/checkout@v2

      # ここで認証する
      - name: Configure AWS
        run: |
          curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$AUDIENCE" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to Amazon ECR
        working-directory: packages/graphql
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: sample
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

おわりに

永続的な credentials を排除できたのですが、role の 細かい policy 管理が出てきたため面倒だなとも思っています emoji-sweat_drops と言いつつ、今までいい加減に管理していたフルフルな role を正すのに良い機会だとも思っています :man-bowing:


Canji

クラウド周りをちょこまかしたい注意散漫人間。個人開発を楽しんでいたあの頃。