これまでGitHub ActionsからAWSのリソース操作しようとすると、以下のような対応が必要でした。
- 権限を絞ったロールを付与したIAMユーザを作成する
- そのアクセスキーIDをGitHub Actionsに登録し、Jobから利用できるようにする
しかし、GitHub ActionsがOIDCに対応した結果、こういったケースにおいてもアクセスキーIDの設定が不要になりました。
周りでも盛り上がっていたのですが、なかなか試す機会がなく、ようやく試した次第です。
仕組みの概要
GitHub ActionsとAWSに代表されるクラウドサービスを組み合わせるときの仕組みの概要は下図で示されます。 (図はAbout security hardening with OpenID Connectから引用)
- クラウドプロバイダは、事前にGitHubのOIDC Providerと事前に信頼関係を結びます
- その後にGitHub Actionsのワークフローでジョブが起動すると、GitHubのOIDC ProviderがOIDC Tokenを生成します
- 開発者は、必要に応じてこれをクラウドプロバイダに提示するステップを定義します
- クラウドプロバイダは提示された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
指定でしょうか。
type
をFederated
とし、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にアクセスできています。