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>
3.4. ③ AWSがJWTトークンを検証してIAMロールを引き受け許可
AWSは受け取ったJWTトークンを以下の手順で検証します。
- 発行者の確認: JWTトークンの
iss
クレームがhttps://token.actions.githubusercontent.com
であることを確認 - 署名の検証: GitHubの公開鍵でJWTトークンの署名を検証
- 条件の確認: 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など)
#### 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:]'
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
5. 注意点
個人的にハマったことを
- OIDCを使う場合、
permissions.id-token: write
が必須です(JWTトークンの取得権限) - IAMロールの信頼ポリシーは、リポジトリ単位で最小にする
- ECRへのPUSH、ECSのタスク定義の更新やCloudWatchの権限もIAMロールに付与。この権限周りが悩むところだと思います。動かないから調べてPolicy追加→実機で試すというループを何度したことか。。。