GitHub Actionsのトークンを使ってAWS公式のconfigure-aws-credentialsを使ってみた
20211127追記
下記のブログ記事にあるように、configure-aws-credentials
がOIDCでAssumeRoleできる機能を正式に取り込んだため、本記事に書いてあるような面倒なことをしなくても簡潔にAssumeRoleできるようになりました。
ついにaws-actions/configure-aws-credentials@v1にOIDCでAssumeRoleできる機能が取り込まれました!
そのおかげで、GitHub Actionsのワークフロー上では単に下記のようにすればAssumeRoleできます。
name: Example
on:
push:
env:
AWS_ROLE_ARN: <使いたいIAMロールのARN>
permissions:
id-token: write
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: "${{ env.AWS_ROLE_ARN }}"
aws-region: ap-northeast-1
role-duration-seconds: 900
role-session-name: GitHubActionsTestSession
もちろん、事前にAWSアカウント側でIAMのOIDCプロバイダを準備しておく必要があります(GitHub Actions OIDCでconfigure-aws-credentialsでAssumeRoleする)
以下はリリース前に試したことですので、参考までに。
始めに
先日、以下のツイートが話題になりました。
これにより、GitHub ActionsからAWSのリソースに触る必要のある時[1]にGitHub Actions実行用のアクセスキーが不要になります。
なぜそんなことができるのかについてざっくりいうと、IAMでIdプロバイダを作り、GitHub側のOIDCのIssuerを許可することによりGitHub側からの権限の呼び出しを行えるようにしています。
また、IAMロールの信頼関係の設定で呼び出し元リポジトリやOrganizationなどの名称で呼び出し元リポジトリを絞り込むことができます(後ろで補足します)。
IAMはもともとOIDC(OpenID Connect)に対応していて、WebID Federationによって外部のIdプロバイダと連携できます。
また、クラスメソッドさんでもさっそくブログが書かれています。
IAM User / Access Keyがこの世に存在してよい理由の一つがなくなりました。
個人的には上記の文言に激しく同意したいです。
なぜアクセスキーはなるべく作らないほうが良いかについては、過去記事にしました。
本記事で試したこと
大元のブログでやっていたこと
元ブログや同じことをやってみた記事ではGitHub Actionsで下記のように権限を呼び出して、テストとしてaws sts get-caller-identity
を実行しています。
# .github/workflows/example.yml
name: Example
on:
push:
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- run: sleep 5 # there's still a race condition for now
- name: Configure AWS
run: |
export AWS_ROLE_ARN=arn:aws:iam::0123456789012:role/ExampleGithubRole
export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
export AWS_DEFAULT_REGION=us-east-1
echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV
echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV
echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE
- run: aws sts get-caller-identity # just an example. why not deploy something?
(元ブログより抜粋)
id-token
はこの記事を書いた時点ではまだドキュメントには記載がないオプションです
Configure AWSのところで何をしているかと言うと、まずGitHub Actionsの環境変数に
AWS_WEB_IDENTITY_TOKEN_FILE
AWS_ROLE_ARN
AWS_DEFAULT_REGION
という3つの環境変数をセットする設定を入れています。
awscliやSDKなどではどうやらAWS_WEB_IDENTITY_TOKEN_FILE
にトークンファイルのファイルパス、AWS_ROLE_ARN
に使いたいIAMロールのarnを設定しておくと、AWS_WEB_IDENTITY_TOKEN_FILE
にあるトークンファイルを読み込んで、AWS_ROLE_ARN
で設定したIAMロールをAssumeRoleWithWebIdentity[2]で呼び出して使用してくれるようです。
このことはAWS CLIのドキュメントにも記載されています(今回初めて知りました…)。
最後に
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL"
でGitHubに対してトークンを発行するためのリクエストを投げています。
$ACTIONS_ID_TOKEN_REQUEST_TOKEN
や$ACTIONS_ID_TOKEN_REQUEST_URL
はGitHub Actionsの環境変数のようですが、執筆時点でドキュメントに記載はありませんが、GitHub Actionsが持っている$ACTIONS_ID_TOKEN_REQUEST_TOKEN
の値を使って$ACTIONS_ID_TOKEN_REQUEST_URL
にリクエストを送り一時トークンを発行しているんだと思います。
※この辺はドキュメントに記載がなく、詳細がよくわからないので何かわかる方いたらぜひ補足をお願いします。もしドキュメントに何か追記されたらこの記事でも補足をします。
課題
さて、これでもAWSのロールを引き受けることができるのですが、いくつか問題があります。
- 一時的な権限の有効期限を設定する項目がない
- もともとGitHub Actionsで使われる
configure-aws-credentials
の利用を前提にしていたActionが、正しく動作しない可能性がある
https://github.com/aws-actions/configure-aws-credentials
など。
GitHub Actionsではconfigure-aws-credentials
を使ってクレデンシャルの設定を行ってからAWSのリソース周りをいじる、という流れで行っているものが多いので可能であればconfigure-aws-credentials
に乗りたいなあと思います。
クレデンシャルの設定についてはaws sts assume-role-with-web-identity
をGitHub Actions上で実行させて、そこで発行されたAWSの一時キーとトークンを使用して以後の操作を行うようにしているものが多かったですが、試してみたらconfigure-aws-credentials
を利用することがどうやら可能なようでした。
やったこと
まずは通常通りにGitHubのトークンを取得します。
先に貼った定義の中ではConfigure AWS
の部分でやっていたことですね。
まだきちんとは試せていないですが、トークンを発行するだけなのでここでは最低限
export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" \
| jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE
だけ行えば十分だと思います。
ただ、環境変数にロール名を入れていて次に使うのであれば上で貼ったものと同様にここで定義しておきましょう(上記のような説明を書いたものの、この記事では環境変数をそのまま使用する例を以後記載します)。
その次に、configure-aws-credentials
を呼び出してAssumeRole[2:1]を行います。
ReadMeにはすべては書いてないのですが、action.yml
を見ると設定可能なオプションがすべて確認できます。
- uses: aws-actions/configure-aws-credentials@master
with:
role-to-assume: "${{ env.AWS_ROLE_ARN }}"
web-identity-token-file: "${{ env.AWS_WEB_IDENTITY_TOKEN_FILE }}"
aws-region: "${{ env.AWS_DEFAULT_REGION }}"
role-duration-seconds: 900
role-session-name: GitHubActionsTestSession
web-identity-token-file
に、取得したトークンのパスを渡します。
そうすることでトークンファイルを使ってくれます。
WebID Federationの設定が済んでいれば、あとはrole-to-assume
に使用したいロールのarnを渡してあげればAssumeRoleWithWebIdentity[2:2]でロールを引き受けることができます。
role-duration-seconds
では、AssumeRole[2:3]したロールのクレデンシャルの有効期限を設定できます。
role-duration-seconds
を設定しないとIAMロール側のMaxSessionDuration
の設定時間(1時間=3600秒)をオーバーする値を使うらしくエラーになってしまうので、適切な期限を設定しましょう。
MaxSessionDuration
を長くすることも可能ですが、GitHub Actionsをそんなに長時間実行し続けることはないと思うので必要と思う期限を設定すべきでしょう。
ちなみにAPIの仕様で900秒以上の時間を設定する必要があります。
role-session-name
は必須ではないですが、用途ごとにセッション名を分けておくとCloudTrailで追うときにどこでAssumeRole[2:4]されたかがトレースしやすくなるのでわかりやすい名前をつけておくことをお勧めします。
role-duration-seconds
やrole-session-name
のオプション設定の内容が有効であることはCloudTrailで確認できますので試したときに確認してみるとよいでしょう。
やったことの補足
- ここでは設定した環境変数を使用した例を書いていますが、ここでしか設定しないと思われるので、前述のように環境変数を
AWS_WEB_IDENTITY_TOKEN_FILE
しか設定していないのであれば直書きでもよいでしょう。そのへんはお好みでどうぞ。 -
aws-region
には一応AWS_DEFAULT_REGION
の値を明示的に設定していますが、AWS_DEFAULT_REGION
が設定されていればほかにリージョンの指定がない場合AWS_DEFAULT_REGION
のリージョンを使用してくれるはずなので不要かもしれません。
その他
Bitbucketでは公式ドキュメントにほぼ同様のことをしている例が書いてあります。
しかしながらこちらにもoidc: true
という見慣れないオプションが設定されておりドキュメントには何も記載がされていません。
ドキュメントの整備が待たれますが、ソースのここを見ればわかる等がもしあったら補足をお願いします。
なぜこの方法がセキュアか
前述の通り、Idプロバイダを作成するときにGitHub Actions側のIssuerを設定しています。
また、使用したいIAMロールを作成する際に信頼関係の設定の部分のIAMポリシーで呼び出し元を制限しています。
これはAssumeRole[2:5]をする際に必ず行う設定です。
今回は下記のように設定しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::xxxxxxxxxxxx:oidc-provider/token.actions.githubusercontent.com"
},
"Action": [
"sts:TagSession",
"sts:AssumeRoleWithWebIdentity"
],
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:yutaro1985/github-actions-oidc-test:*"
}
}
}
]
}
※sts:TagSession
をつけてますが、不要だと思いますね…。
Principalのところは、たとえばあるIAMロールからAssumeRole[2:6]できるようにしたいのであれば
"Principal": {"AWS": "arn:aws:iam::AWS-account-ID:role/role-name"},
のように書きます。
ほかにも設定可能なものは下記ドキュメントに載っています。
また、Condition
のところで文字列一致によりリポジトリ名を使って呼び出し元を制限しています。
今回の記載の場合、yutaro1985/github-actions-oidc-test
でしか使用ができません。
ここにたとえば
yutaro1985/*
と設定すると、yutaro1985
が管理するすべてのリポジトリで使えます。
また、Organizationでも絞ることができ、hogehoge
というOrganizationのリポジトリすべてで利用できるようにするためには
"repo:hogehoge/*"
とすれば可能です。
CfnやTerraformで作るならここの部分は変数化しておくとよいかもしれませんね。
ということで、該当するリポジトリのGitHub Actionsで発行したトークンだけがロールを引き受けることができるような設定をしているから安全に使うことができるということです。
万が一GitHub Actionsで発行したトークンが漏洩した場合でもロールのarnがバレなければ該当のロールを使うことはできません。
とはいえarnをソースに埋め込んでいたり、Actionsのログに残っていたりするケースもあるのでGitHub Actionsが発行したトークンが万が一漏洩するようなことがあった場合は注意が必要でしょう。
本記事を書くにあたり実験したこと
下記のスクラップに垂れ流しています。
記事を書くにあたりほかに参考にさせてもらった記事など
以下の記事とスクラップは自分が試したり確認したいなと思っていたことをほぼ全部先にやってくれていたのでとても助かりました。
最初のブログではCfnで書いてあるだけでしたが、手動で作成する場合についても試してくれています。
OIDCのIDトークンについては以下を参考にさせていただきました。
作ったもの
今回はTerraformで必要なものを作りました。
なお、すでに先駆者の方がいらっしゃったので大いに参考にさせていただきました(最初はまったく見ないで作ったのですがまさかリポジトリ名までまったく同じとは…)
今回説明したconfigure-aws-credentials
を使っている部分が違うほか、
自分は各設定の元に使用したのがクラスメソッドさんの記事の方なのでAudienceが定義してあったりします。
感想
これによって、GitHub Actionsにアクセスキーを渡す必要がなくなり、アクセスキーの管理の手間と漏洩のリスクが軽減できます。
OIDCを使ったロールの引受については今までやったことがなかったので勉強になりました。
OIDC周りについては単純にまだ理解不足な部分が多いのでもうちょっとちゃんと勉強したいなと思います。
Discussion