理系学生日記

おまえはいつまで学生気分なのか

GitHub ActionsでAWSのクレデンシャル登録なしにAWS操作を行う

これまでGitHub ActionsからAWSのリソース操作しようとすると、以下のような対応が必要でした。

  • 権限を絞ったロールを付与したIAMユーザを作成する
  • そのアクセスキーIDをGitHub Actionsに登録し、Jobから利用できるようにする

しかし、GitHub ActionsがOIDCに対応した結果、こういったケースにおいてもアクセスキーIDの設定が不要になりました。

周りでも盛り上がっていたのですが、なかなか試す機会がなく、ようやく試した次第です。

仕組みの概要

GitHub ActionsとAWSに代表されるクラウドサービスを組み合わせるときの仕組みの概要は下図で示されます。 (図はAbout security hardening with OpenID Connectから引用)

  1. クラウドプロバイダは、事前にGitHubのOIDC Providerと事前に信頼関係を結びます
  2. その後にGitHub Actionsのワークフローでジョブが起動すると、GitHubのOIDC ProviderがOIDC Tokenを生成します
  3. 開発者は、必要に応じてこれをクラウドプロバイダに提示するステップを定義します
  4. クラウドプロバイダは提示されたOIDC Tokenを検証し、問題なければ短命なクレデンシャルを返却します

AWSで考える

IDプロバイダーの構築

AWSとGitHub OIDC Providerとの信頼関係の構築は、AWSではIAMのOIDC IDプロバイダーの作成によって行われます。

Terraformで作成しようとすると、以下のようなコードになります。

Provider URLはhttps://token.actions.githubusercontent.com、Client IDリストにはsts.amazonaws.comを指定します。これらについては、GitHub DocsのConfiguring OpenID Connect in Amazon Web Servicesに記載があります。

resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"
  client_id_list = [
    "sts.amazonaws.com",
  ]

  thumbprint_list = [
    "a031c46782e6e6c662c2c87c76da9aa62ccabd8e"
  ]
}

最後のthumbprint_listに指定するのはルートCAサムプリントです。これらの算出方法は以下を参照してください。

これを実際に行っているQiitaの記事もあります。

上記のようなTerraformの実行により、下記のようにIDプロバイダが構築できます。

Assumeするロールの定義

上述のIDプロバイダにによって、AWSからクレデンシャルを払い出す仕組みは整います。 しかしこれだけでは片手落ちで、「どのような権限を持ったクレデンシャルが必要なのか」が定義されていません。 AWSで言えばフェデレーション用のIAMロールが必要です。

私はTerraformで以下のように作成しました。

locals {
  claim_sub       = "token.actions.githubusercontent.com:sub"
  repository_name = "kiririmode/hobby"
}

resource "aws_iam_role" "github_actions" {
  name               = "GitHubActionsRole"
  description        = "GitHub Actions"
  assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
  managed_policy_arns = [
    aws_iam_policy.backend_access.arn
  ]
}

data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    actions = [
      "sts:AssumeRoleWithWebIdentity",
      "sts:TagSession"
    ]

    # GitHub ActionsのOIDC Provider
    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }

    condition {
      test     = "StringLike"
      variable = local.claim_sub
      values   = ["repo:${local.repository_name}:*"]
    }
  }
}

ポイントは、やはりIAMポリシードキュメントのPrincipals指定でしょうか。 typeFederatedとし、identifiersにはIDプロバイダーのARNを指定することで、払い出すクレデンシャルの権限を定義します。

セキュリティとして重要なのはcondition指定です。ここが緩いと、例えば他のリポジトリのGitHub Actionsで当該権限が持ててしまったりします。上記の定義では、私のリポジトリで実行されるGitHub Actionsでしか利用できないよう絞り込んでいます。

この実装はConfiguring the role and trust policyの通りのものですが、GitHub Actionsで定義するEnvironment等を用いて絞り込みも可能です。

詳細についてはGitHub Docsの以下のドキュメントを参照ください。

GitHub Actionsのワークフロー設定

冒頭で引用した概要図の通り、OIDC Tokenをクラウドプロバイダに提示するのはGitHub Actions側の仕事です。 これは自分で実装すると面倒なのですが、configure-aws-credentials actionを使うだけで済みます。

例えば具体的なYAMLを示すと以下のようになります。

name: Sample

on:
  pull_request:
    branches:
      - main
jobs:
  sample:
    name: Sample
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    env:
      AWS_REGION: ap-northeast-1
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ secrets.TERRAFORM_ROLE_TO_ASSUME }}
          role-duration-seconds: 900
          aws-region: ${{ env.AWS_REGION }}

忘れがちな点としては、OIDC Tokenの発行に対してid-tokenのパーミッションが必要になる点。これは Adding permissions settingsに記載があります。

configure-aws-credentialsに対してもドキュメントを読めばそれほど迷わないのですが、発行されるクレデンシャルの有効期間は短ければ短いほど安心です。ここでは、minimumの900秒で指定しています。

一時的なセキュリティ認証情報の期間を指定する期間。DurationSeconds パラメータを使用して、ロールセッションの期間を 900 秒 (15 分) からそのロールの最大セッション期間設定まで指定できます。

一時的なセキュリティ認証情報のリクエスト

実行結果

上記の仕組みを用いて、GitHub ActionsからS3のBackendに対するterraform initを行いました。 以下の通り正常に終了しており、無事にS3 BucketやDynamo DBにアクセスできています。