CDKでOIDCプロパイダーを作成し、GitHubActionsでOIDCを使用してAWS認証を行う

# 完成物
![Pipeline drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/436166c2-9d5a-454c-b68a-67ee3f05ce14.png)

今回の記事ではこれの左側の部分の説明をします。
今後似たような認証機構を構築する時のために備忘録として残しておくための記事です。
間違いや、もっとこうしたら良いなどがあったらコメントしていただけると幸いです。

# OIDC認証とは
会社の同期がめっちゃわかりやすい記事を書いてくれていたのでそれを参考しました。

https://zenn.dev/mai_mizz/articles/cce2d2c764e9fc#oauth%E3%81%A8oidc


上記の記事より引用。OIDCの流れの簡略化した図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/8b8d2364-2700-2487-e917-5491332c45fc.png)

ものすごくざっくり言うと、
**OIDC = 「トークンを使用した短期認証のプロトコル」**
と言えそうです

AWS Credentialsのような長期的な認証ではないのでセキュリティ的にも推奨されます。

今回は、
**AWS側にOIDCプロバイダーを設置して、GitHubActionsから送られてくるトークンを検証、識別して必要なロールを渡す**
という機構を構築していきます。

# 1. CDKでOIDCプロバイダーと必要なロールをデプロイ
パイプライン用に先にデプロイしていたpipeline-stack.tsにOIDCに関する部分を追加します

最終的にはこのようなコードになります
lib/stacks/pipeline-stack.ts
```typescript
~~~~

    // 元々定義していたバケットのARNを変数に格納
    const bucketArn = bucket.bucketArn;

    // GitHubのOAuthトークンを取得するためのOpenID Connectプロバイダーを作成
    const idProvider = new iam.OpenIdConnectProvider(this, 'IdProvider', {
      url: 'https://token.actions.githubusercontent.com',
      clientIds: ['sts.amazonaws.com'],
    });

    // GitHub Actionsの特定のワークフローに対してS3への権限を与えるためのロール
    const role = new iam.Role(this, 'Role', {
      roleName: 'GitHubActionRole',
      maxSessionDuration: Duration.hours(1),
      assumedBy: new iam.WebIdentityPrincipal(idProvider.openIdConnectProviderArn, {
        StringLike: {
          'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
        },
        StringEquals: {
          'token.actions.githubusercontent.com:sub': 'repo:orgnize/project:ref:refs/heads/main',
        },
      }),
    });

    // 作成したロールにS3への権限を与える
    role.addToPolicy(new iam.PolicyStatement({
      actions: ['s3:PutObject'],
      resources: [bucketArn + '/*'],
    }));

```

ひとつずつ解説していきます。

<br>

## AWS側にOIDCプロバイダーを設置する
lib/stacks/pipeline-stack.ts
```typescript
    // GitHubのOAuthトークンを取得するためのOpenID Connectプロバイダーを作成
    const idProvider = new iam.OpenIdConnectProvider(this, 'IdProvider', {
      url: 'https://token.actions.githubusercontent.com',
      clientIds: ['sts.amazonaws.com'],
    });

```
この'token.actions.githubusercontent.com'は
**GitHub Actions が OIDC トークンを取得する際に内部的に使用するエンドポイント**
です。
GitHub Actions のジョブが実行されると、GitHub はこのエンドポイントを介して AWS に送信される OIDC トークンを発行します。
このトークンには、リポジトリ、ブランチ、その他の関連情報が含まれています。

AWS の Security Token Service (STS) をclient(受信者）として定義

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/10160190-f116-acef-3a0a-73cac14c7144.png)

つまり、このようなフローでトークンが届くので、
「発行元がURL通りで受信者がSTSならOKを出すプロバイダー」
を定義したことになります。この段階だとゆるゆるです。


OpenIdConnectProviderの公式APIリファレンスドキュメントはこちら

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.OpenIdConnectProvider.html


<br>

# GitHubActionsで使用するためのロールを定義

lib/stacks/pipeline-stack.ts

```typescript
    // GitHub Actionsの特定のワークフローに対してS3への権限を与えるためのロール
    const role = new iam.Role(this, 'Role', {
      roleName: 'GitHubActionRole',
      maxSessionDuration: Duration.hours(1), // 有効な時間
      assumedBy: new iam.WebIdentityPrincipal(idProvider.openIdConnectProviderArn, {
        StringLike: {
          'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
        },
        StringEquals: {
          'token.actions.githubusercontent.com:sub': 'repo:orgnize/project:ref:refs/heads/main',
        },
      }),
    });

    // 作成したロールにS3への権限を与える
    role.addToPolicy(new iam.PolicyStatement({
      actions: ['s3:PutObject'],
      resources: [bucketArn + '/*'],
    }));

```
Web Identity (サードパーティをPrincipalとすることができる)にassumeするIAMロールを定義しています。

assumedByの条件でプロバイダーの基本条件に加え、特定のリポジトリやブランチに限定した条件を追加することで、ロールを引き受けることができるトークンをさらに絞り込んでいます。

先ほどと重複しますが、発行されたトークンは、リポジトリ、ブランチ、その他の関連情報が含まれているので、その情報を使用している感じです。

このような流れで、GitHubActionsに対して、同じスタックに定義したS3へPUTする権限を与える準備ができました。


# GitHubActionsのワークフローを実装

.github/workflows/infra-deploy.yml
```yaml
name: infra-deploy

on: #mainへのプッシュをトリガーに起動
  push:
    branches:
      - main

permissions: #ワークフローがリポジトリにアクセス、OIDCトークンを利用しての認証とロールの引き受けを許可
  id-token: write
  contents: read

jobs:
  upload-source:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source
        uses: actions/checkout@v4

      - name: Configure AWS credentials # IAMロールの引き受けと、CLIを実行できるように設定
        uses: aws-actions/configure-aws-credentials@v4 #ここが一番重要
        with:
          aws-region: ${{ secrets.AWS_REGION }}
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }} # 先に定義した'GitHubActionRole'のARNをシークレットに入れておく

      - name: Install AWS CLI
        run: |
          sudo apt-get update
          sudo apt-get install -y awscli

      - name: Zip source code
        run: zip -r source-code.zip .

      - name: Upload source code
        run: aws s3 cp source-code.zip s3://bucket-name-${{ secrets.ENV_NAME }}

```

**uses: aws-actions/configure-aws-credentials@v4**
このアクションが、GitHub Actions ジョブが AWS の OIDC プロバイダーとやりとりし、トークンを取得し、そのトークンを使って指定された IAM ロールを引き受けるためのプロセスを担当してます。

GitHubのシークレットの使い方

https://qiita.com/mkin/items/75a4928a1fafe5eacd17

以上の工程でOIDCを使用した短期認証によってGitHubActionsからS3へのソースコードのプッシュが実装できました

# 参考にした記事まとめ

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_create_oidc.html

https://docs.github.com/ja/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#getting-started-with-oidc


https://zenn.dev/rescuenow/articles/1976c01e28b3cc

https://zenn.dev/fusic/articles/48620c1c798ece

https://www.cloudbuilders.jp/articles/4268/

https://zenn.dev/kou_pg_0131/articles/gh-actions-oidc-aws

https://zenn.dev/miyajan/articles/github-actions-support-openid-connect