1. はじめに

AWSにECS環境を構築します。GitHubActionsで以下を実施します。

  • コンテナイメージのビルド
  • コンテナイメージをECRにPUSH
  • ECSをデプロイするために、タスク定義のリビジョンアップデート
  • ECSにタスクをデプロイ

これまではシークレットキーやシークレットアクセスキーをSECRETSに書いていました。

このやり方だと、権限が広すぎますし、有効期限を管理が面倒だし、今っぽくないと考えていました。

調べると、OIDC(OpenID Connect)使えば、一時的な認証情報だけで安全にAWSへアクセスできることがわかりました。

この記事では、GitHub ActionsからOIDCを使ってECSタスクをデプロイする方法を解説します。

2. なぜOIDCを使うのか?

GitHub ActionsとAWSを連携する方法として、以下の2つがよく使われます。改めて表にしてみます。

方法 セキュリティ 運用負荷
アクセスキーをSECRETSに登録 中(漏洩リスクあり) 高(ローテーション管理)
OIDCで一時クレデンシャル取得 高(キー不要) 低(管理自動化)

アクセスキーやシークレットアクセスキーを使うっていうのが、もう面倒なわけですよ。誰のものを使用するかとか考えたり、ローテーションはどうするかって考えたり、実際運用するのが手間なわけです。

で、この記事でフォーカスしているOIDCを使えば、アクセスキーの管理が不要になります。GitHubが発行するJWTトークンを使ってAWSにアクセスする仕組みで、最小権限の原則にも則った安全な構成が可能になるのです。

3. 仕組み

GitHub ActionsからOIDCを使ってAWSにアクセスする仕組みを図で表すと以下のようになります。

3.1. 用語の整理

まず、混同しやすい用語を整理します:

  • OIDC(OpenID Connect): 認証プロトコルの名前
  • JWTトークン(JSON Web Token): OIDCで使用される具体的なトークン形式
  • IDトークン: JWTトークンの一種で、ユーザーやサービスの身元情報を含む

この記事では、GitHub ActionsがOIDCプロトコルを使用してJWTトークンを取得し、AWSに送信する流れを解説します。

上図の流れに沿って、4つのステップで詳しく説明します。

3.2. ① GitHub ActionsでJWTトークンを取得

GitHub Actionsワークフローが実行されると、GitHub内蔵のOIDCプロバイダーからJWTトークン(JSON Web Token)を取得します。

3.3. ② AWSにJWTトークンを送信してロール引き受けを要求

取得したJWTトークンを使って、AWS STSのAssumeRoleWithWebIdentity APIを呼び出します。この時、事前に設定したIAMロールの引き受けを要求します。

# 内部的に実行されるAPI呼び出し(イメージ)
aws sts assume-role-with-web-identity \
  --role-arn arn:aws:iam::123456789012:role/GitHubActionsRole \
  --role-session-name github-actions-session \
  --web-identity-token <JWT-Token>
bash

3.4. ③ AWSがJWTトークンを検証してIAMロールを引き受け許可

AWSは受け取ったJWTトークンを以下の手順で検証します。

  1. 発行者の確認: JWTトークンのissクレームがhttps://token.actions.githubusercontent.comであることを確認
  2. 署名の検証: GitHubの公開鍵でJWTトークンの署名を検証
  3. 条件の確認: IAMロールの信頼ポリシーで設定した条件(リポジトリ名、ブランチなど)とJWTトークンのクレームが一致するかチェック

検証に成功すると、AWSはIAMロールの引き受けを許可し、一時的な認証情報を返します:

  • アクセスキーID: 一時的なアクセスキー
  • シークレットアクセスキー: 一時的なシークレットキー
  • セッショントークン: 一時的なセッショントークン
  • 有効期限: デフォルトで最大1時間

3.5. ④ 一時的な認証情報を使ってECSにデプロイ

GitHub Actionsは、取得した一時的な認証情報を使ってAWS操作を実行します。この認証情報には、事前にIAMロールに付与したポリシーの権限のみが含まれているため、最小権限の原則に従った安全な操作が可能です。

具体的には以下のような操作を実行できます:

  • ECRへのPUSH**: コンテナイメージをECRリポジトリにアップロード
  • タスク定義の更新: 新しいイメージを使用するタスク定義を登録
  • ECSサービスの更新: update-service --force-new-deploymentでサービスを再起動

この一連の流れにより、長期的なアクセスキーを管理することなく、安全にAWSリソースを操作できます。

4. 具体的な設定例

4.1. IAMロールの準備(AWS側)

まず、GitHubからのOIDC接続を許可するIAMロールを作成します。

# Terraform例
resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"
  client_id_list = ["sts.amazonaws.com"]
  thumbprint_list = ["<thumbprint>"] # 
}

resource "aws_iam_role" "github_actions_role" {
  name = "GitHubActionsRole"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = "arn:aws:iam::<account-id>:oidc-provider/token.actions.githubusercontent.com"
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringLike = {
            "token.actions.githubusercontent.com:sub" = "repo:<your-org>/<your-repo>:*"
          }
        }
      }
    ]
  })
}

# 必要なポリシーをアタッチ(例:ECS, ECR, CloudWatch Logsなど)
hcl

#### thumbprintとは

thumbprintは、OIDCプロバイダー(GitHub)のSSL証明書の指紋(フィンガープリント)です。AWSがGitHubからのリクエストを信頼するために使用されます。

GitHub ActionsのOIDCプロバイダー用のthumbprintは以下の値です。

  • 6938fd4d98bab03faadb97b34396831e3780aea1
  • 1c58a3a8518e8759bf075b76b750d4f2df264fcd

thumbprintは以下のコマンドで取得できます。

echo | openssl s_client -servername token.actions.githubusercontent.com -connect token.actions.githubusercontent.com:443 2>/dev/null | openssl x509 -fingerprint -noout -sha1 | sed 's/://g' | sed 's/.*=//g' | tr '[:upper:]' '[:lower:]'
bash

4.2. GitHub Actionsの定義

name: Deploy to ECS

on:
  push:
    branches: [main]

permissions:
  id-token: write  # JWTトークンの取得に必要
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest

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

      - name: Configure AWS Credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::<account-id>:role/GitHubActionsRole
          aws-region: ap-northeast-1

      - name: Build and Push to ECR
        run: |
          docker build -t myapp .
          docker tag myapp:latest <account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest
          aws ecr get-login-password | docker login --username AWS --password-stdin <account-id>.dkr.ecr.ap-northeast-1.amazonaws.com
          docker push <account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest          

      - name: Deploy to ECS
        run: |
          aws ecs update-service \
            --cluster my-cluster \
            --service my-service \
            --force-new-deployment          
yaml

5. 注意点

個人的にハマったことを

  • OIDCを使う場合、permissions.id-token: write が必須です(JWTトークンの取得権限)
  • IAMロールの信頼ポリシーは、リポジトリ単位で最小にする
  • ECRへのPUSH、ECSのタスク定義の更新やCloudWatchの権限もIAMロールに付与。この権限周りが悩むところだと思います。動かないから調べてPolicy追加→実機で試すというループを何度したことか。。。