GitHub ActionsからOIDCでCredential情報を使わずにAWSと連携

# はじめに
今更ながら、GitHubとAWSをOIDC（OpenID Connect）で連携して、GitHub ActionsからS3へ連携したので、やり方について記載

# OIDC
そもそもOIDCってなんだっていう人は、ChatGPTさんに聞いたら以下をおすすめしてくれたので、記載しておきます。

https://auth0.com/docs/authenticate/protocols/openid-connect-protocol

https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe

その上で、GitHubの[該当ページ](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)には、以下のように説明がありました。

> - No cloud secrets: You won't need to duplicate your cloud credentials as long-lived GitHub secrets. Instead, you can configure the OIDC trust on your cloud provider, and then update your workflows to request a short-lived access token from the cloud provider through OIDC.
> - Authentication and authorization management: You have more granular control over how workflows can use credentials, using your cloud provider's authentication (authN) and authorization (authZ) tools to control access to cloud resources.
> - Rotating credentials: With OIDC, your cloud provider issues a short-lived access token that is only valid for a single job, and then automatically expires.

要は今まで`Secrets`に設定していたCredentialsな情報の設定無しに、短命なトークンを発行して、Provider(AWS)にアクセスが出来て、かつProvider側でロールによる制御が可能という理解をしました。

またドキュメントにあった、概要図も以下に転載しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457889/6167babc-14f6-170a-efcb-96a20fcc73e1.png)

# 設定

## CloudProviderの設定

最初にCloud Provider(AWS)側で設定を行います。
設定する項目は以下
- IAMのIDプロバイダ
    - プロバイダ：token.actions.githubusercontent.com
    - タイプ：OpenID Connect
    - 対象者：sts.amazonaws.com
- IAMRole
   - ポリシー
      - ```json
         {
             "Version": "2012-10-17",
             "Statement": [
                 {
                     "Action": [
                         "s3:PutObject",
                         "s3:GetObject",
                         "s3:ListBucket"
                     ],
                     "Resource": "*",
                     "Effect": "Allow"
                 }
             ]
         }
        ```  
   - 信頼エンティティ
       - ```json
         {
             "Version": "2008-10-17",
             "Statement": [
                 {
                     "Effect": "Allow",
                     "Principal": {
                         "Federated": "arn:aws:iam::<AWS_ACCOINT_ID>:oidc-provider/token.actions.githubusercontent.com"
                     },
                     "Action": "sts:AssumeRoleWithWebIdentity",
                     "Condition": {
                         "StringEquals": {
                             "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                         },
                         "StringLike": {
                             "token.actions.githubusercontent.com:sub": "repo:<GITHUB_USER_ORG>/<GITHUB_REPOSITORY>:*"
                         }
                     }
                 }
             ]
         }
         ```

基本は、IDプロバイダは１つ作成すれば良く、以降はIAMRoleに紐付ける形で使いまわしていく。
また、信頼エンティティにある`"token.actions.githubusercontent.com:sub": "repo:<GITHUB_USER_ORG>/<GITHUB_REPOSITORY>:*"`にある`*`を変更することで、Provider側からトークンを発行する制御が設定できると思われます。今回はすべての条件で利用出来るようにしたため、`*`を設定しました。

GitHubの[ドキュメント](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims)には例としていくつか上がっていました。

| 対象 | 設定 |
|-----------|------------|
| `Environment`の名前| repo:\<orgName/repoName>:environment:\<environmentName>| 
| PRイベント     | repo:\<orgName/repoName>:pull_request      | 
| ブランチ       | repo:\<orgName/repoName>:ref:refs/heads/branchName        | 
| タグ         | repo:\<orgName/repoName>:ref:refs/tags/\<tagName>          | 



### CloudFormation
おまけとして上記のCFnを貼り付けておきます。

```yaml
Parameters:
  GitHubOrg:
    Description: Name of GitHub organization/user (case sensitive)
    Default: ""
    Type: String
  RepositoryName:
    Description: Name of GitHub repository (case sensitive)
    Default: ""
    Type: String
  OIDCProviderArn:
    Description: Arn for the GitHub OIDC Provider.
    Default: ""
    Type: String
  RoleName:
    Description: Name of the Role.
    Default: ""
    Type: String
  OIDCAudience:
    Description: Audience supplied to configure-aws-credentials.
    Default: "sts.amazonaws.com"
    Type: String

Conditions:
  CreateOIDCProvider: !Equals 
    - !Ref OIDCProviderArn
    - ""

Resources:
  Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref RoleName
      Policies:
        # Githubから扱うPolicy定義
        - PolicyName: S3AccessPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 's3:PutObject'
                  - 's3:GetObject'
                  - 's3:ListBucket'
                Resource: '*'
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: !If 
                - CreateOIDCProvider
                - !Ref GithubOidc
                - !Ref OIDCProviderArn
            Condition:
              StringEquals:
                token.actions.githubusercontent.com:aud: !Ref OIDCAudience
              StringLike:
                token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/${RepositoryName}:*

  GithubOidc:
    Type: AWS::IAM::OIDCProvider
    DeletionPolicy: Retain
    Condition: CreateOIDCProvider
    Properties:
      Url: https://token.actions.githubusercontent.com
      ClientIdList: 
        - sts.amazonaws.com
      ThumbprintList:
        - 6938fd4d98bab03faadb97b34396831e3780aea1

Outputs:
  Role:
    Value: !GetAtt Role.Arn 
```

```sh
$ aws cloudformation create-stack --stack-name github-oidc-provider --template-body file://oidc.yaml --parameters ParameterKey=GitHubOrg,ParameterValue=<ORG> ParameterKey=RepositoryName,ParameterValue=<REPO> ParameterKey=RoleName,ParameterValue=oidc-role --capabilities CAPABILITY_NAMED_IAM 
```

## GitHubの設定
GitHubActionsの設定は以下のActionを利用して、簡単に連携できます。

https://github.com/aws-actions/configure-aws-credentials

```yaml
name: sync to S3
on:
  push:
    branches:
      - develop
    paths:
      - 'path/to/file.yaml'
      - 'path/to/file.json'

permissions:
  id-token: write
  contents: read

jobs:
  sync:
    name: Upload S3
    runs-on: ubuntu-latest
    environment: develop
    steps:
      - uses: actions/checkout@v3
      - uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: "arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/oidc-role"
          aws-region: ap-northeast-1
      - name: upload richmenu to S3
        run: |
          aws s3 sync . s3://backet_name/ --exclude "*" --include "file.*"
```

ポイントとしては、

```yaml
permissions:
  id-token: write
  contents: read
```
で`permissions`を指定しないと、発行されるトークンが利用できないため必須


# まとめ
以前↓の記事にて、GCP側にOIDCを設定していたことを途中から思い出しましたが、改めてAWS側にも設定する機会があったのでまとめてみました。

https://qiita.com/ppco/items/8abbbd283d507d365a63