無駄と文化

実用的ブログ

OpenID Connect で GitHub Actions から AWS のリソースにアクセスする

GitHub Actions から AWS などのリソースに安全にアクセスするためには OIDC (OpenID Connect) という仕組みを利用します。

...ということを見聞きしてさらっと知ってはいたのですが、自分で一通りの設定をなぞってやったことはなかったのでやってみます。

なぜ OIDC か

OIDC を使うのが主流 (メジャー) だから。

というのは冗談で、
OICD で使う ID トークンは短命で、万が一漏洩しても被害を最小限に抑えられるとのことです。

また AssumeRole の仕組みを使うことでアクセス元を ユーザー/リポジトリ/ブランチ まで細かく絞れるのがいい感じだなと思いました。

やっていく

ID プロバイダの準備

まずは AWS コンソール側であれこれ準備をします。
練習なので何もリソースを作っていない真っさらなアカウントを用意しました。

アカウントを新規作成したばかりの AWS コンソール

IAM > ID プロバイダ から ID プロバイダを追加します。

次に IAM > ロール からロールを作成。

先ほど作成したプロバイダが選択肢に表示されている

先ほど作成したプロバイダ token.actions.githubusercontent.com が選択肢に表示されているので選択します。これにて ID プロバイダと IAM ロールが紐付きます。

リソースへのアクセスを許したい「GitHub 組織」「GitHub リポジトリ」「GitHub ブランチ」をそれぞれ設定します。

リポジトリとブランチの設定は任意なので空欄のままでも可ですが、アクセス元の制御が緩くなるので GitHub 側の設定を誤ったときにより脆弱になります。
組織設定を誤ると他のユーザーからのアクセスを許すことになるのでセキュリティホールになります。

実験用なので `IAMReadOnlyAccess` を設定しておく

ポリシーをアタッチしてこのロールで可能な操作を決めます。ロール作成後に変更可能なのでいったん IAMReadOnlyAccess だけつけておきます。

分かりやすいロール名を設定して完了です。

 

作成したロールを見てみましょう。

「信頼関係」というタブにアクセス元の制御が JSON で書かれている

「信頼関係」というタブにアクセス元の制御が JSON で書かれています。
{ "Statement" { "Condition" { "StringLike" {"token.actions.githubusercontent.com:sub": "..." } } } } の内容によってどの組織のどのリポジトリのどのブランチからのアクセスを許可するか決まります。
ここを細かく書き換えることで特定のブランチからのみアクセスを許可するなどの細かな制御が可能です。

 

ここまでで AWS コンソール側での一通りの準備が完了しました。

  • ID プロバイダを作成
  • IAM ロールを作成
  • ID プロバイダと IAM ロールを紐付け
  • IAM ロールにポリシーをアタッチ

以上をやりました。

GitHub Actions でワークフローを定義する

todays-mitsui/gh-actions-oidc-drill というリポジトリを作って GitHub Actions のワークフローを書いていきます。
.github/workflows/open-id-connect.yaml というファイルを作成します。

name: OpenID Connect
on: push
env:
  ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ID }}:role/${{ secrets.ROLE_NAME }}
  SESSION_NAME: gh-oidc-${{ github.run_id }}-${{ github.run_attempt }}
jobs:
  connect:
    timeout-minutes: 5
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ env.ROLE_ARN }}
          role-session-name: ${{ env.SESSION_NAME }}
          aws-region: ap-northeast-1
      - run: aws iam list-users
      - run: aws iam create-user --user-name invalid || true

aws-actions/configure-aws-credentials というアクションを使って OIDC の認証を行います。
認証に成功すれば AWS_SESSION_TOKEN などの環境変数がセットされます。以降は aws コマンドで環境変数を読んで適切な権限でリソースにアクセスすることが可能です。

 

さて、さっそく実行してみたいところですが、 ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ID }}:role/${{ secrets.ROLE_NAME }} の部分がシークレットで一部隠されています。
ワークフローの実行前に GitHub リポジトリの Settings > Secrets and variables > Actions の画面から適切に値を設定しておく必要があります。

AWS_ID は AWS コンソールの右上で確認できる「アカウント ID」のハイフンを抜いた数字列を、ROLE_NAME は先ほど作成した IAM ロールの名前 (今回の例では github-actions) を設定します。

AWS_ID (アカウント ID) は AWS コンソールの右上から確認できる

ワークフローを実行してみる

実行してみましょう。実際のログが ここ から見られます。

ステップ `aws-actions/configure-aws-credentials@v4 のログ

aws-actions/configure-aws-credentials@v4 のステップで正常に認証が行われています。

ステップ `aws iam list-users` のログ

その後 aws iam list-users のステップでリソースにアクセスして IAM ユーザーの一覧情報が取得できました。
実行確認のために作成したテストユーザーの情報が見えています。

ステップ `aws iam create-user --user-name invalid || true` のログ

さらに次のステップでは aws iam create-user しようとして失敗しています。
使用している IAM ロールにアタッチしたポリシーは IAMReadOnlyAccess で、ユーザーを作成する権限が無いためです。
アタッチした以上の強すぎる権限は発揮できないことが確認できました。

別のリポジトリからアクセスできないことを確認する

今回作成した ID プロバイダと IAM ロールでは todays-mitsui/gh-actions-oidc-drill リポジトリからのみリソースにアクセスできるように設定しました。
ということは別のリポジトリ (例えば todays-mitsui/gh-actions-oidc-drill-another リポジトリ) からはアクセスできないはずです。

todays-mitsui/gh-actions-oidc-drill-another というリポジトリを作り、同じ内容の .github/workflows/open-id-connect.yaml ファイルを置いてワークフローを実行してみましょう。

ステップ `aws-actions/configure-aws-credentials@v4` のログ

はい、何度かリトライした後に認証に失敗してエラーになっています。
リポジトリのオーナーはどちらも私 (todays-mitsui) ですが、リポジトリ単位でアクセス許可の制御ができました。

まとめ

最初は AWS コンソールでの設定が面倒かなと思いましたが、意外と最低限の必要事項だけで設定できるので内容の理解は容易です。
GitHub Actions のワークフローファイルに一切の機密情報を置くことなくアクセス制御ができるのが安心・便利ですね。

 

さて次回もまた GitHub Actions の便利な機能を試します。GitHub Apps トークンを使ってリポジトリを跨いだアクセス制御に挑戦しようと思います。

 

 

私からは以上です。