はじめに
2021年10月に、GitHub ActionsはOpenID Connect(OIDC)のサポートを開始しました。
最近、IAMユーザーのアクセスキーを利用した認証から、OIDCを用いた認証方式へ移行する機会があったので、そのために必要な作業と考慮すべき重要なポイントを本記事でまとめてみました。
OIDCとは何か?
OpenID Connect(OIDC)は、OAuth 2.0をベースにしたユーザー認証のプロトコルです。
ユーザーの身元を確認し、基本的なプロファイル情報を取得するために使用されます。サードパーティのアプリケーションは、OIDCを通じてユーザー認証を行い、JWT(JSON Webトークン)を使用してユーザー情報を安全に交換します。
他の認証手段と比較して、よりセキュアで柔軟な認証が可能です。
詳細は以下をご参照ください。
OIDCにすると何が嬉しいの?
以前は、IAMユーザーのアクセスキーとシークレットキーをGitHubリポジトリのSecrets
に保管し、CIで環境変数としてセットしてAWSへのアクセス認証を制御していました。
-
AWS_ACCESS_KEY_ID
: IAMユーザーに関連づけられるアクセスキー -
AWS_SECRET_ACCESS_KEY
: アクセスキーに関連づけられるシークレットキー
これらはSecrets
に保管されているため暗号化されていますが、永続的なクレデンシャルをAWS外に持つというセキュリティの課題や漏洩リスクの対処として定期的なローテーションを行う等の運用課題があります。
一方で、OIDC認証方式に移行すると、GitHub ActionsのワークフローからAWSリソースへアクセスする際に、IAMロールに紐付いた一時クレデンシャルを利用することになります。
一時クレデンシャルには期限があるため、万が一悪意ある第三者に窃取されたとしても、永続的に利用可能なアクセスキーに比べて危険性は低くなります。
認証構成の概要
GitHub ActionsとAWSでの認証にOIDCを使用する場合、通常以下のようなプロセスで認証が行われます。
認証プロセス
具体的には以下の通りです。
01. GitHub ActionsからGitHub OIDCプロバイダーへのトークン要求
- GitHub ActionsはGitHubのOIDCプロバイダーにOIDCトークンを要求
02. GitHub OIDCプロバイダーからのトークンの応答
- GitHubのOIDCプロバイダーはOIDCトークンをGitHub Actionsに返す
03. AWS IAMへのロール引き受け要求
- GitHub ActionsはOIDCトークンを使用してAWS IAMに一時的なアクセス権限を要求
04. AWS IAMからの一時的なクレデンシャルの応答
- AWS IAMは信頼されたGitHubリポジトリからの認証トークン付きリクエストに基づいて、一時的なアクセストークンを発行
05. AWSサービスへのアクセス
- GitHub Actionsはこの一時トークンを使用して、許可されたAWSサービス(例:ECRへのプッシュ、S3へのアクセスなど)にアクセス
構成イメージ
GitHub Actionsを使用してAWS環境にOIDC認証を通じてアクセスする際の関係性を構成図にしてみると、以下のようになります。
実装の流れ
GitHub ActionsとAWSの認証にOIDCを使用する際の実装の流れは、以下の通りです。
- GitHub OIDC プロバイダーをAWSアカウント側で準備
- 信頼関係を登録したGitHubリポジトリ用のIAM Roleを用意
- GitHub ActionsワークフローでAssume Roleを指定
本記事では、これらのリソースを設定する際の重要なポイントと具体的な設定例をご紹介します。
1. OIDCプロバイダー作成
通常、OIDCプロバイダーの作成には、AWS Management Console、Cloudformation、AWS CLI、またはAWS SDKを使用しますが、今回はCloudformationで作成する例をご紹介します。
CloudFormationを使用してOIDCプロバイダーを作成するには、OIDCプロバイダーを定義するためのリソースタイプ AWS::IAM::OIDCProvider
を利用します。
テンプレートでは、以下を指定します。
- プロバイダーのURL
- クライアントIDリスト
- サムプリント指定
yaml形式で記載すると以下のようになります。
GithubOidc:
Type: AWS::IAM::OIDCProvider
Properties:
Url: 'https://token.actions.githubusercontent.com'
ClientIdList:
- 'sts.amazonaws.com'
ThumbprintList:
- 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
プロバイダーのURLやクライアントIDリストは固定値となりますが、サムプリントについてはこちらを参照すると、Thumbprint IDを使ってIdp通信をすることはなく、Dummy値(適当な40桁)でも良さそうです。
2023年6月27日に GitHub Changelog でサムプリントを2種類設定するという記事が公開されていましたが、2023年7月6日からAWS側で証明書の検証が可能になったため、サムプリントの指定は不要のようです。
ただし、CloudformationのAWS::IAM::OIDCProvider
でリソースを作る場合 ThumbprintList
は必須項目なので、ランダム文字列「40文字(0-9 or A-F)」を指定すれば良いということになります。
2. OIDC認証用IAMロール作成
OIDC認証用IAMロールをCloudFormationで作成する際の設定ポイントを以下に示します。
yaml形式で記載すると以下のようになります。
Type: AWS::IAM::Role
Properties:
RoleName: '<Roleの名前>'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: sts:AssumeRoleWithWebIdentity
Principal:
Federated: !GetAtt GithubOidc.Arn
Condition:
StringEquals:
token.actions.githubusercontent.com:aud:
- 'sts.amazonaws.com'
StringLike:
token.actions.githubusercontent.com:sub:
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/<ブランチ名>'
Policies:
- PolicyName: 'ECRAccess'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:BatchCheckLayerAvailability
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource: 'arn:aws:ecr:ap-northeast-1:123456789012:repository/my-repository' # ここは適切なECRリソースARNに変更
解説
-
AssumeRolePolicyDocument
は、このロールを引き受けることができるエンティティ(この場合はOIDCプロバイダー)を定義 - Principalに
Federated: !GetAtt GithubOidc.Arn
を使用して、先に作成したGitHub OIDCプロバイダーのARNを参照 -
token.actions.githubusercontent.com:aud
は、トークンの発行者(audience)を指定します。ここでは 'sts.amazonaws.com' を使用 -
token.actions.githubusercontent.com:sub
は、トークンのサブジェクトを指定し、特定のGitHubリポジトリやブランチに対するアクセスを制限します。 -
Policies
セクションで、OIDC認証用IAMロールにAWSリソースへのアクセス権を付与します。(上記例ではECR関連のアクセスと操作を許可)
重要なポイント
ここで重要なのは、token.actions.githubusercontent.com:sub
クレームになります。
repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/<ブランチ名>
のように指定することで、特定のリポジトリの特定のブランチからのGitHub ActionsワークフローによるAWSリソースへのアクセスを制御できます。
これは、セキュリティを強化し、不正なアクセスや意図しない変更からリソースを保護するために重要です。
また、こちらを確認すると、token.actions.githubusercontent.com:sub
クレームは主にリポジトリ名とGitリファレンス(ブランチやタグ)に関連する情報と、pull_request
イベントも制御可能なようです。
つまり、リポジトリやブランチ以外にもpull_request
をトリガーとしたワークフローによるAWSリソースへのアクセス制限が可能なようです。
補足
筆者は実装当初、上記のpull_request
イベントを許可しておらず、Assume Roleの接続試行に失敗しました。
Cloud TrailでGitHub ActionsからAWSへのアクセスログを確認していくと、pull_request
をトリガーにしたGitHub Actionsワークフローからのアクセスは、userName
がrepo:<GitHubユーザー名>/<GitHubリポジトリ名>:pull_request
になることがわかります。
完全に盲点でした。
CronやWorkflow Dispatch、Merge、Pushをトリガーとしたイベントは指定しなくていいのか?ですが、GitHub ActionsのOIDCトークンのsubクレームではpull_request
イベント以外、直接特定できないようなので、リポジトリやブランチ制限に依存することになります。
参考記事
以下にsubクレームによるアクセス制限のユースケースをいくつかご紹介します。
ユースケース1. 特定のリポジトリの全てのブランチを許可
特定のリポジトリの全てのブランチを許可する場合は、ワイルドカード(*
)を使用します。
StringLike:
token.actions.githubusercontent.com:sub:
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/*'
ユースケース2. 特定のリポジトリの特定のプレフィックスを持つブランチを許可
特定のプレフィックスを持つブランチにアクセスするためのポリシーを設定したい場合、ワイルドカードを使って以下のように指定できます。
StringLike:
token.actions.githubusercontent.com:sub:
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/prefix-*'
この設定により、「prefix-」で始まる任意のブランチに対するアクセスが許可されます。
ユースケース3. 特定のリポジトリの複数ブランチを許可
複数のブランチに対するアクセスを許可する場合、ForAnyValue:StringLike
を使用できます。
これにより、指定されたパターンに一致する任意のブランチに対して条件が適用されます。
ForAnyValue:StringLike:
token.actions.githubusercontent.com:sub:
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/develop'
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/feature'
この設定により、特定のリポジトリの「develop」ブランチと「feature」ブランチに対するアクセスが許可されます。
ユースケース4. 特定のリポジトリの複数ブランチを許可 かつ pull_request
をトリガーとしたワークフローも許可
ForAnyValue:StringLike:
token.actions.githubusercontent.com:sub:
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/develop'
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/feature'
- 'repo:<GitHubユーザー名>/<GitHubリポジトリ名>:pull_request'
この設定により、特定のリポジトリの「develop」ブランチと「feature」ブランチに対するアクセスが許可され、かつ、特定のリポジトリからpull_request
をトリガーとしたワークフローによるAWSリソースへのアクセス制限が可能になります。
上記のようにアクセス制限をかけた場合に「test」ブランチからアクセスを行うと、「Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentity」といったエラーメッセージで、Assume Roleに失敗します。
3. GitHub Actions ワークフローの設定
最後に、GitHub Actionsのワークフローで、先に作成したIAMロールを指定するよう設定します。
AWSへのアクセス認証には、aws-actions/configure-aws-credentialsアクションを使用します。
role-to-assume
に、先に作成したIAMロールのARN
を指定することで、OIDCを使用したAWS認証を行うことができます。
GitHub Actionsを使用してOIDC認証を行い、AWS Elastic Container Registry (ECR) にログインし、イメージをECRにプッシュするワークフローの場合、以下のように記述できます。
name: ECR Push Workflow
on:
push:
branches:
- develop # このワークフローをトリガーするブランチを指定
jobs:
deploy:
name: Deploy to ECR
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: YOUR_AWS_REGION # AWSリージョンを指定
role-to-assume: YOUR_OIDC_ROLE_ARN # OIDCを使用してアクセスするIAMロールのARN
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: YOUR_ECR_REPOSITORY_NAME # ECRリポジトリ名を指定
IMAGE_TAG: latest
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
# その他の必要なステップを追加
この例では、GitHub Actionsのワークフローが特定のブランチにプッシュされたときにトリガーされるように設定されています。
重要なポイント
ここで重要なのは、permissions
セクションです。
id-token: write
を設定しないと OIDC を使えないので注意です。
これにより、GitHub ActionsがIDトークンを生成し、外部サービスに対して認証情報として使用できるようになります。
permissions
キーを用いて、IDトークンの利用を明示的に許可することに伴い、
他のScopeもデフォルトの権限が「無効」となります。
そのため、「必要な権限は全て明示的に記載」する必要があリます。
各種Scopeの説明は以下をご参照ください。
ワークフロー内で実行されるジョブによって、必要なpermissions
は異なりますが、筆者が確認し、主に利用するpermissions
は以下の通りでした。
Scope | R/W | Remark |
---|---|---|
actions |
write |
slack-notice-action の利用など。ワークフローが他のジョブやワークフローをトリガーする、またはアーティファクトやログを書き込む場合に必要。 |
contents |
read |
actions/checkout の利用など。 |
id-token |
write |
GitHubのOIDCプロバイダから発行されるトークンを利用するために必要。 (MUST) |
pull-requests |
write |
プルリクエストにコメントを投稿したり、ステータスを更新するなどの操作を行う場合など。 |
deployments |
write |
リポジトリのデプロイメント情報を取得する場合など。 |
これらの権限が不足すると、ワークフロー実行時に「Resource not accessible by integration」というエラーメッセージが返ってくることがあります。
GitHub ActionsワークフローでOIDC認証を利用した結果例
GitHub ActionsがAWSのIAMロールを引き受けるプロセスが成功した例は以下の通りです。
Assuming role with OIDC:
GitHub ActionsがOIDC認証を使用してAWSのIAMロールを引き受けています。
Authenticated as assumedRoleId xxxx:GitHubActions:
この部分は、GitHub Actionsが特定のIAMロール(この例ではIDがxxxxのロール)を引き受け、そのロールを利用して認証されたことを示しています。
以上の3点が、OIDC認証を利用するまでの実装手順になります。
まとめ
ここまで、GitHub ActionsがサポートするOpenID Connect(OIDC)を用いてAWS環境にセキュアにアクセスする方法についてまとめてみました。
OIDC認証の実装には、GitHub OIDCプロバイダーの準備、IAMロールの設定、GitHub ActionsワークフローでのAssume Roleの指定が主な流れとなります。
特に、IAMロールで指定するtoken.actions.githubusercontent.com:sub
は、特定のGitHubリポジトリやブランチに対するアクセス制限を行う設定となるため、よりセキュアな環境とするには重要な設定です。
また、Gihub Actionsワークフローで指定するpermissions
のid-token: write
設定を含め、セキュリティを確保するための適切な設定が何かをワークフローの特性によって判断し、明示的に定義してあげる必要があります。
最後に、本記事の情報が少しでも誰かのお役に立てれば幸いです。