🪁

GitHub ActionsのOIDCを使ってAWS公式のActionsを動かす方法

2021/11/06に公開

はじめに

GitHub ActionsからOIDC経由でAWSのAssume Roleをすることで、色々と便利になりました。[1]
公式にアナウンスされたこともあり導入に向けて動いていました。

ところで、以下のようなGitHub Actionsの例はよく見ると思います。[2]

- name: Configure AWS
  env:
    ACCOUNT_ID: ${{ secrets.ACCOUNT_ID }}
    ROLE_NAME: ${{ secrets.ROLE_NAME }}
  run: |
    export AWS_ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}"
    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 

上記のGitHub Actionsを動かすためには、IDプロバイダとそのIDプロバイダからAssumeできるIAM Roleが必要となります。
ActionsAWSのIDプロバイダ側の対象者(ClientID)はhttps://github.com/${GitHubOrg名}としています。
この状態で、AWSの公式のActions[3]を利用したときに色々つまづいたので、ここにまとめます。

動かしたい人向けの結論

  1. AWS公式クレデンシャル設定のActionsを使用しましょう
    role-to-assumeのパラメタのみを設定することで、AWS公式のActionsが利用するクレデンシャル情報を払い出してくれています。
  2. AWS OIDCプロバイダの設定の対象者(ClientID, audience)にsts.amazonaws.comを許可するように設定する必要があります。

調査の流れ

以下発生したエラーとそれに付随する情報等を記載しています。

最初のエラー(No credentials) と AWS_WEB_IDENTITY_TOKEN_FILE の話

最初に以下のような状態で動かすとNo credentials. Try adding @aws-actions/configure-aws-credentials earlier in your job to set up AWS credentials.というエラーが出ました

- name: Configure AWS
  env:
    ACCOUNT_ID: ${{ secrets.ACCOUNT_ID }}
    ROLE_NAME: ${{ secrets.ROLE_NAME }}
  run: |
    export AWS_ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}"
    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

- name: Run CodeBuild
  uses: aws-actions/aws-codebuild-run-build@v1
  with:
    project-name: CodeBuildProjectName

なぜaws sts get-caller-identityだと動作するのに、公式のActionsだと失敗するのかは、それぞれのクレデンシャル情報取得処理に鍵があります。

AWS CLIとAWS_WEB_IDENTITY_TOKEN_FILEの話

awsコマンドで実行されるAWS CLIでは設定を行うことで、自動的にAssumeRoleWithWebIdentityの呼び出しが行われています。
その設定方法の1つとして、環境変数AWS_WEB_IDENTITY_TOKEN_FILEの設定があります。
そのため、上記でaws sts get-caller-identityを動作させたときには、内部的にAssumeRoleWithWebIdentityが実行され、その実行結果から得られる一時的なクレデンシャル情報が使われています。

aws-codebuild-run-buildとaws-sdkのクレデンシャル取得の処理

詳細はところまで終えていませんが、結局aws-sdk実行しているだけのため、aws-sdkのドキュメントに従うと考えられます。
ドキュメントによると、AWS_WEB_IDENTITY_TOKEN_FILEはサポートされておらず、AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN(optional)がサポートされているようです。

自前で書く

最初のエラーはAssumeRoleWithWebIdentityを、AWS CLIは勝手にやってくれているが、AWS SDKはやってくれていないことに起因しています。
そのため、勝手にやってくれていることを、自分で書くことで、動作することが確認できました。

- name: Configure AWS
  env:
    ACCOUNT_ID: ${{ secrets.ACCOUNT_ID }}
    ROLE_NAME: ${{ secrets.ROLE_NAME }}
  run: |
    export AWS_ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}"
    export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
    export AWS_DEFAULT_REGION=ap-northeast-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
- name: Configure Manually
  run: |
    AWS_WEB_IDENTITY_TOKEN=$(cat $AWS_WEB_IDENTITY_TOKEN_FILE)
    CREDENTIALS=$(aws sts assume-role-with-web-identity --role-arn ${AWS_ROLE_ARN} --role-session-name MySessionName --web-identity-token ${AWS_WEB_IDENTITY_TOKEN})
    export AWS_ACCESS_KEY_ID=$(echo $CREDENTIALS | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo $CREDENTIALS | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo $CREDENTIALS | jq -r '.Credentials.SessionToken')
    echo AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID >> $GITHUB_ENV
    echo AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY >>$GITHUB_ENV
    echo AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN >> $GITHUB_ENV
- name: Run CodeBuild
  uses: aws-actions/aws-codebuild-run-build@v1
  with:
    project-name: CodeBuildProjectName

@aws-actions/configure-aws-credentialsを使用時のエラー(Incorrect token audience)

自前で書くのは流石に辛いなぁと思っていたところ、AWS公式のクレデンシャル設定Actionsに入っていることを知りました。
ドキュメントによるとrole-to-assumeのパラメタだけ指定すると動作するようです。[4]

- name: Configure AWS Credentials
  uses: aws-actions/configure-aws-credentials@v1
  with:
    aws-region: ap-northeast-1
    role-to-assume: arn:aws:iam::${{ secrets.ACCOUNT_ID }}:role/${{ secrets.ROLE_NAME }}
    role-session-name: MySessionName

これで記述が楽になったと思いきや、実行すると以下のようなエラーが出ました。
An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdentity operation: Incorrect token audience

Audience(ClientID)について

Actionsのサンプルに書いてあるのですが、AWS側のOIDC Providerの設定において、許可するClientIdにsts.amazonaws.comを指定する必要があります。
しかし、最初の例だとhttps://github.com/${GitHubOrg名}というClientIdを許可しないと動作しなかったので、気になって調査しました。
調査したところ、OIDCについて詳しくないのですが、少なくともGitHub OIDCにおいては、任意のaudフィールドを持つトークンを発行することが可能なようです。
以下のようにURLのクエリパラメタとして、audienceを指定することで実現できます。
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=hogehoge"

AWSの公式Actions上ではここのコードで設定されています。
最終的に呼び出されるコードは上記で説明したようなaudienceクエリを使ったトークン取得が行われています。

本記事の最初の例のように指定しなかった場合、https://github.com/${GitHubOrg名}が入ってくるようです。

本設定を行うことで、GitHub OIDCを使って、AWS公式のActionsの利用が可能となりました。

セキュリティの話

上記で書いたように、GitHub OIDCのaudフィールドが任意のものを発行可能です。
そのため、Assume対象のRoleの条件に気をつけなければなりません。
ClientIDとして、sts.amazonaws.comを許可するIDプロバイダを作成し、そのIDプロバイダを無条件に信頼している場合、AWSアカウントIDとIAMロール名のみで、そのロールへのAssumeRoleが可能となってしまいます。
StringLike token.actions.githubusercontent.com:sub repo:${GitHubOrg名}/${リポジトリ名}:* のような条件を追加し、必ず実行を許可するリポジトリを制限しましょう。[5]

またクレデンシャル情報の扱いについて、AWS公式Actionsでは、Cleanup処理で環境変数の消去を行なってくれています。
こういったものは忘れがちですので、できるだけ自分では書かずに利用するようにしましょう。

リポジトリ

本記事で扱ったAWSの設定を行うTerraformファイルとGitHub Acionsの例(AWS公式アクションを使う or 自分で設定)は以下のリポジトリにあります。
https://github.com/daku10/github-oidc-terraform-sample

参考

https://dev.classmethod.jp/articles/github-actions-configure-aws-credentials-oidc/
https://awsteele.com/blog/2021/09/15/aws-federation-comes-to-github-actions.html
https://zenn.dev/mryhryki/articles/2021-09-19-access-aws-by-github-actions

脚注
  1. 特に大きいのはIAM Userが不要になることですね ↩︎

  2. 一番早くアナウンスしていたhttps://awsteele.com/blog/2021/09/15/aws-federation-comes-to-github-actions.htmlより引用(ARNの箇所は改変) ↩︎

  3. 自分はaws-codebuild-run-buildを利用 ↩︎

  4. 結構無理矢理感ある実装ですがv2で解消されそうですhttps://github.com/aws-actions/configure-aws-credentials/blob/8eba69932f928f2bc1b9837d16a095e7e1cb0845/index.js#L263-L269 ↩︎

  5. mainブランチのみの制限などは今回の話では扱いません ↩︎

Discussion