前々から言われていた GitHub Actions で OpenID Connect 経由で、各種Cloud Provider の認証を得るのが GA しました。 めでたい。
これにより、aws-actions/configure-aws-credentials のみで認証が組めるようになったので見てみましょう。
https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actionsgithub.com
tl;dr;
- GitHub Actions でAWS操作をするために、IAM User が不要になるので神
- セルフホストな GitHub Actions を EC2 で動かしているなら、素直に Instance Role を使うので十分というのもある。(何か事情があれば OpenId Connector でもいい)
- AWS アカウントで OpenId Connect Provider は 一意です。1 AWSアカウントで複数環境を持っている & 環境ごとに IAM Role を持っている場合は、環境ごとに OpenId Conect Provider を作ろうとして失敗しないように
- 並列度高く認証を取得しようとすると失敗することが多いので注意
動作例
設定がただしければ、必要な IAM Role Arn を role-to-assume
に指定するだけで、その Role 権限で 操作ができるようになります。
これで
- name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@master with: aws-region: ap-northeast-1 role-to-assume: "作ったIAM Role の Arn を入れる" role-session-name: "適当な名前"
こうなる。
基本
GitHubから公式にドキュメントが公開されたのでこれに従いましょう。先日までuses: aws-actions/configure-aws-credentials@v1
になってたせいでドキュメントが嘘つきでしたが @master
に修正されています。
なお、https://github.com/aws-actions/configure-aws-credentials は、uses: aws-actions/configure-aws-credentials@v1
になっているので動きません。uses: aws-actions/configure-aws-credentials@master
に読み替えましょう。
Terraform で AWSを用意する。
用意する必要があるのは、OIDC Provider と IAM Role です。
- OIDC Provider は、GitHub OIDC と信頼関係を結ぶのに必要です
- IAM Role は OIDC Provider 経由で GitHub OIDC でリクエストされたときに、
リクエスト元のリポジトリオーナー/リポジトリ名:ブランチ名
を検証し条件にマッチしたらそのRoleを Assume して利用できるようにします。つまり、このRoleに、リポジトリの制約と必要な IAM Policyを振ればok
全体を示します。
OIDC Provider を用意する
値は固定なので単純です。terraform ならこうなります。
// oidc provider resource "aws_iam_openid_connect_provider" "main" { url = "https://token.actions.githubusercontent.com" client_id_list = ["sts.amazonaws.com"] thumbprint_list = ["a031c46782e6e6c662c2c87c76da9aa62ccabd8e", "6938fd4d98bab03faadb97b34396831e3780aea1"] // 2022/1/11 に 中間証明書更新された }
thumbprint に関しては 中間証明書の寿命が1年なので、毎年1/11 に代わっていくはずです。 thumbprint はコマンドでとれるので、更新直後などドキュメントが間に合ってないときは適当に対応しましょう。
Get Thumbprint of GitHub OIDC, updated on 2022/01/13. · GitHub
client_id_list がGA前と後で変わっています。古いバージョンではここで、リクエスト元のリポジトリURL を指定していましたが、今は sts.amazonaws.com
といつものになりました。標準に沿ってくれてよかった。
GitHub の Endpoint が確認できますね。
https://token.actions.githubusercontent.com/.well-known/openid-configuration
{"issuer":"https://token.actions.githubusercontent.com","jwks_uri":"https://token.actions.githubusercontent.com/.well-known/jwks","subject_types_supported":["public","pairwise"],"response_types_supported":["id_token"],"claims_supported":["sub","aud","exp","iat","iss","jti","nbf","ref","repository","repository_owner","run_id","run_number","run_attempt","actor","workflow","head_ref","base_ref","event_name","ref_type","environment","job_workflow_ref"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid"]}
IAM Role を用意する
順番に解説します。
重要なのは、Assume Role です。 Assume Role で OIDC Provider からのリクエストを検証しています。
- principal は、認証を委譲されて受けるので
type: "Federated"
+ 先ほど作った OIDC Provider の arn を指定 します - condition で、リクエスト元が
repo:<GitHubOwner>/<Repositry>:Branch
の条件とマッチするか検証します
OIDC 経由で、許可した「リポジトリ、かつブランチ」だった場合に IAM Role をsts:AssumeRoleWithWebIdentity
としてAssume できるようにします。
さて、複数のリポジトリで同じロールを使いたいケースはどうすればいいでしょうか?
IAM Policy の condition は、ワイルドカード一致するかを StringLike
で検証できますが、これは value が1要素 (=単一リポジトリ) のときにしか機能しません。
value が複数要素(=複数リポジトリ)でもワイルドカードで一致するかを見る場合は、ForAnyValue:
+ StringLike
を用います。
今回は 分岐させましたが、別に ForAnyValue で初めから書いてもいいでしょう。
data "aws_iam_policy_document" "github_oid_assume_role_policy" { statement { effect = "Allow" actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = [aws_iam_openid_connect_provider.main.arn] } # aud があるとはじかれてるので、aud の値がおかしいっぽい。 aws-actions/configure-aws-credentials の仕組みからすると、sts.amazonaws.com が来るはず。 <- github.com/aws-actions/configure-aws-credentials ではなしになってる :( # condition { # test = "StringEquals" # variable = "token.actions.githubusercontent.com:aud" # values = ["https://github.com/${var.github_owner}"] # } condition { test = length(var.github_oidc_repo_names) == 1 ? "StringLike" : "ForAnyValue:StringLike" variable = "token.actions.githubusercontent.com:sub" values = [for item in var.github_oidc_repo_names : "repo:${var.github_owner}/${item}:*"] } } }
AssumeRole ができてしまえば IAM Roleを作るだけです。今回は、aws sts get-caller-identity
を実行できるようにしてみましょう。
特にいうことはないですね。終わり。
data "aws_iam_policy_document" "github_actions" { // allow running `aws sts get-caller-identity` statement { effect = "Allow" actions = ["sts:GetCallerIdentity"] resources = ["*"] } } resource "aws_iam_policy" "github_actions" { name = "githubactions_policy" path = "/" description = "Policy for GitHubActions" policy = data.aws_iam_policy_document.github_actions.json } resource "aws_iam_role" "test_role" { name = "githubactions-oidc-role" path = "/" assume_role_policy = data.aws_iam_policy_document.github_oid_assume_role_policy.json policy_arns = [ aws_iam_policy.github_actions.arn ] }
リソースを作成
さて、これで AWS にリソースを作るとこんな感じになります。
今回は私は、guitarrapc/githubactions-lab と guitarrapc/infrastructure の 2リポジトリから GitHub Actions 経由で認証を受けられるようにしました。
IAM Role の Trust relationships > Conditions で2リポジトリが ForAnyValue:StringLike
で指定されているのがわかりますね。
ここまでのどれかにミスがあると Assume Role されません。 うまくいかない場合は何度も見直すことになるでしょう。
GitHub Actions の構成
Workflow を用意します。 キーポイントは3つです。
permissions
で、id-token: write
で書き込み権限が必要です。permissions: write-all
でもいいのですが、現状では permissions を指定しないとうごかないので注意ですrole-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
のsecrets.AWS_ROLE_TO_ASSUME
は、GitHub Secret に AWS_ROLE_TO_ASSUME という名前で先ほど作った IAM Role の Arn を仕込んであります。Arnのフォーマットは、今回の例ならarn:aws:iam::xxxxxxxxxxxxx:role/githubactions-oidc-role
というフォーマットになります- role-session-name で、CloudTrail イベントにユーザー名が出るのでいい感じの名前にしましょう
uses: aws-actions/configure-aws-credentials@master
を指定します。uses: aws-actions/configure-aws-credentials@v1
でないので注意してください
name: aws oidc credential on: workflow_dispatch: push: branches: ["main"] # allow use id-token permissions: id-token: write # required! contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Configure AWS Credentials # must use "master", not "v1". v1 is not yet released to use latest role-to-assume. # Error: Credentials could not be loaded, please check your action inputs: Could not load credentials from any providers uses: aws-actions/configure-aws-credentials@master with: aws-region: ap-northeast-1 role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} role-session-name: GitHubActions-${{ github.run_id }} role-duration-seconds: 900 # minimum: 900sec, maximum: iam role session duration - name: get-caller-identity is allowed to run on role. run: aws sts get-caller-identity
実行してみると、冒頭のスクショのように成功するはずです。
$ aws sts get-caller-identity { "UserId": "AROASJXUOK5UM7XZKRYTB:GitHubActions-1420393244", "Account": "***", "Arn": "arn:aws:sts::***:assumed-role/githubactions-oidc-role/GitHubActions-1420393244" }
うまくできましたか? おめでとうございます!
ダメでした? 私も失敗を繰り返したので、経験したエラーと対処をFAQに載せておきます。
FAQ
GitHub Actionsで実行してみたら動作しない。
エラーメッセージ
Credentials could not be loaded, please check your action inputs: Could not load credentials from any providers
この場合、AWS OIDC Provider の設定がおかしい or GitHub Actions の permissions が抜けています。
- (AWSの設定ミス) OIDC Provider の client_id_list に
"sts.amazonaws.com"
ではなく、リポジトリのURL を指定している。(古いブログが記事がそうなっている) - (GitHub Actions Workflowの設定ミス) Workflow で、permissions がない。デフォルトの GitHub Actionsは
write-all
のはずですが、まだid-tokens
は含まれていないようです。明示的にpermissions: write-all
を指定するか、id-token: write
を指定しましょう
GitHub Actionsで実行してみたら動作しない。
エラーメッセージ
Not authorized to perform sts:AssumeRoleWithWebIdentity
このケースは、OIDC Provider は問題なく、AssumeRole の設定にミスがあります。
- (AWSの設定ミス) AssumeRole の Action が
"Action": "sts:AssumeRoleWithWebIdentity"
になっていない - (AWSの設定ミス) 複数リポジトリなのに
ForAnyValue:StringLike
ではなくStringLike
で判定している - (AWSの設定ミス) AWS の IAM Role の Assume Policy が設定しようとしている GitHub Owner/Repository:ブランチ と内容と一致していない
過去にある類似記事との差分
GA前の内容で、結構ずれています。GA前の記事の内容でやると失敗するので注意です。(aws-actions/configure-aws-credentials が v1 で OIDC 変更を反映すればいける)