GitHub Actionsでもっとセキュアにコンテナイメージをプッシュする

2021/10/27にGithub ActionsでOpenID Connect（以下OIDC）サポートが[正式にアナウンス](https://github.blog/changelog/2021-10-27-github-actions-secure-cloud-deployments-with-openid-connect/)になりました。

元々IAM Userの機密情報（アクセスキー＆シークレットキー）を使ってGithub Actionsでコンテナイメージのビルド&プッシュを行っていましたが、IAM Userの機密情報を使っていることで、万が一誤って機密情報をGithubリポジトリにプッシュしてしまうこともありえ、AWSも推奨していないので、今回OICDの仕様に則り一時的な認証情報でAWSリソースへアクセス・操作できるようにしたいと思います。


## AWSでの設定
まずはGithub ActionsからOIDCでAWSにアクセスできるよう、いくらかIAMでロールなどを作成します。

AWSコンソールからポチポチして作成することもできるのですが、作成したリソースの管理のためにTerraformでAWSリソースを作成したいと思います。

以下のTerraformのtfファイルを適用すれば一気に必要なAWSリソースを作成できます。

```:Terraform
# ----- GitHub Action -----
##########################
# IAM identity providers （Githubが提供するOIDCプロバイダをAWSに登録）
##########################
# GitHub ActionsのOIDCのIDプロパイダのthumbprintを計算するため必要
data "http" "github_actions_openid_configuration" {
  url = "https://token.actions.githubusercontent.com/.well-known/openid-configuration"
}

# GitHub ActionsのOIDCのIDプロパイダのthumbprintを計算するため必要
data "tls_certificate" "github_actions" {
  url = jsondecode(data.http.github_actions_openid_configuration.body).jwks_uri
}

resource "aws_iam_openid_connect_provider" "github_oidc" {
  url = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
  thumbprint_list = [data.tls_certificate.github_actions.certificates[0].sha1_fingerprint]
}

##########################
# IAM Role （信頼関係を登録して一時的なAWSリソースへのアクセストークンを発行するIAMロールを作成）
##########################
data "aws_iam_policy_document" "github_actions_assume_role_policy" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRoleWithWebIdentity"]
    principals {
      type       = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github_oidc.arn]
    }

    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values   = ["repo:【SampleOrg/SampleRepo（※使用環境に応じて変更して下さい）】:*"]
    }
  }
}

resource "aws_iam_role" "github_actions" {
  name               = "iam_role_for_github_actions"
  assume_role_policy = data.aws_iam_policy_document.github_actions_assume_role_policy.json
}

##########################
# IAM Policy (AWS ECRへコンテナイメージがプッシュできるよう必要な権限をポリシーとして定義)
##########################
data "aws_iam_policy_document" "github_actions_resource_role_policy" {
  statement {
    actions = [
      "ecr:BatchCheckLayerAvailability",
      "ecr:PutImage",
      "ecr:InitiateLayerUpload",
      "ecr:UploadLayerPart",
      "ecr:CompleteLayerUpload",
      "ecr:GetAuthorizationToken"
    ]
    resources = ["*"]
    effect    = "Allow"
  }
}

resource "aws_iam_policy" "github_actions" {
  name        = "iam_policy_for_github_actions"
  path        = "/"
  description = "IAM Policy for GitHub Actions"
  policy      = data.aws_iam_policy_document.github_actions_resource_role_policy.json
}

# 作成したIAMロールとIAMポリシーを関連付け
resource "aws_iam_role_policy_attachment" "github_actions" {
  role       = aws_iam_role.github_actions.name
  policy_arn = aws_iam_policy.github_actions.arn
}
# ----- GitHub Action -----
```

## Github Actionsの設定

Github Actionsでタグをプッシュするとコンテナイメージをビルド&プッシュするワークフローを作成しました。

前提としてGithub ActionsのSecretsには

- AWS_ACCOUNT_ID：AWSのアカウントID
- AWS_ECR_REPOSITORY：コンテナイメージをプッシュするリポジトリ

を設定して下さい。

```:.github/build.yml
name: Build Backend

on:
  push:
    tags:
      - v*

jobs:
  backend-build:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
      actions: read

    steps:
      - uses: actions/checkout@v3

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

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1.4.0

      - name: Build app and push image to AWS ECR
        env:
          AWS_ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          AWS_ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPOSITORY }}
        run: |
          IMAGE_TAG=$(echo ${{ github.ref }} | sed -e "s#refs/tags/##g")
          docker build -t $AWS_ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG .
          docker push $AWS_ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG
```

## 最後に
上記のGithub Actionsが正常に動作したら、

- Github ActionsのSecretsからAWSの認証情報を削除
- 削除した認証情報を持つIAMユーザーを削除

を実施しましょう。

以上でよりセキュアにGitHub Actionsを利用できるようになりました。


### 参考
[GitHub Actions が OpenID Connect をサポートし、デプロイがよりセキュアに出来るようになりました！](https://blog.serverworks.co.jp/github-actions-oidc)
[GitHub Actions の OpenID Connect サポートについて](https://zenn.dev/miyajan/articles/github-actions-support-openid-connect)
[GitHub ActionsからAWS利用時に永続的クレデンシャル情報を渡さないようにする](https://masakimisawa.com/remove-credentials-from-actions/)
[Terraform だけを使って GitHub Actions OIDC ID プロパイダの thumbprint を計算する方法](https://zenn.dev/yukin01/articles/github-actions-oidc-provider-terraform)