GitHub ActionsからOIDCを使って安全にAWS ECSへデプロイする
背景
GitHub Actionsを使ってECSへデプロイする際、これまでアクセスキーをSecretsに保存していました。
しかし、このやり方には以下の問題があります:
- 権限が広すぎる
- 有効期限の管理が面倒
- キー漏洩のリスク
OIDC(OpenID Connect)を使えば、一時的な認証情報だけで安全にAWSへアクセスできます。
この記事では、GitHub ActionsからOIDCを使ってECSタスクをデプロイする方法を記録します。
OIDCを使うメリット
GitHub ActionsとAWSを連携する方法を以下に比較してみます。
| 方法 | セキュリティ | 運用負荷 |
|---|---|---|
| アクセスキーをSecretsに登録 | 中(漏洩リスクあり) | 高(ローテーション管理) |
| OIDCで一時認証情報を取得 | 高(キー不要) | 低(管理自動化) |
OIDCのメリット
- アクセスキーの管理が不要
- GitHubが発行するJWTトークンでAWSにアクセス
- 最小権限の原則に則った安全な構成が可能
OIDCの仕組み
GitHub ActionsからOIDCを使ってAWSにアクセスする流れです。

① GitHub ActionsでJWTトークンを取得
GitHub Actionsワークフローが実行されると、GitHub内蔵のOIDCプロバイダーからJWTトークン(JSON Web Token)を取得します。
② 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>
③ AWSがJWTトークンを検証
AWSは受け取ったJWTトークンを検証します:
- 発行者の確認:
issがhttps://token.actions.githubusercontent.comであることを確認 - 署名の検証: GitHubの公開鍵でJWTトークンの署名を検証
- 条件の確認: IAMロールの信頼ポリシーで設定した条件(リポジトリ名、ブランチなど)とJWTトークンが一致するかチェック
検証に成功すると、AWSは一時的な認証情報を返します:
- アクセスキーID
- シークレットアクセスキー
- セッショントークン
- 有効期限(デフォルトで最大1時間)
④ 一時的な認証情報でECSにデプロイ
GitHub Actionsは、取得した一時的な認証情報を使ってAWS操作を実行します:
- ECRへのPUSH: コンテナイメージをECRリポジトリにアップロード
- タスク定義の更新: 新しいイメージを使用するタスク定義を登録
- ECSサービスの更新:
update-service --force-new-deploymentでサービスを再起動
この認証情報には、IAMロールに付与したポリシーの権限のみが含まれるため、最小権限の原則に従った安全な操作が可能です。
設定手順
1. IAMロールの作成(AWS側)
GitHubからのOIDC接続を許可するIAMロールを作成します。
# OIDCプロバイダーの作成
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [
"6938fd4d98bab03faadb97b34396831e3780aea1",
"1c58a3a8518e8759bf075b76b750d4f2df264fcd"
]
}
# IAMロールの作成
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は以下の値です:
6938fd4d98bab03faadb97b34396831e3780aea11c58a3a8518e8759bf075b76b750d4f2df264fcd
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:]'
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
注意点
個人的にハマったポイント:
-
permissions.id-token: writeは必須: JWTトークンの取得権限として必要 - 信頼ポリシーは最小限に: リポジトリ単位で権限を制限する
- IAMポリシーの調整: ECRへのPUSH、ECSのタスク定義更新、CloudWatchの権限が必要。動かないから調べてPolicy追加→実機で試すというループを何度も繰り返しました
セキュアで運用性が高くなったぜ!
アクセスキーの管理が不要になり、セキュリティと運用性が大幅に向上しました。
Discussion