LoginSignup
12
4

GitHub ActionsとAWSの連携におけるOIDC認証の活用

Last updated at Posted at 2023-12-12

はじめに

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を使用する場合、通常以下のようなプロセスで認証が行われます。
image.png

認証プロセス

具体的には以下の通りです。

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認証を通じてアクセスする際の関係性を構成図にしてみると、以下のようになります。
image.png

実装の流れ

GitHub ActionsとAWSの認証にOIDCを使用する際の実装の流れは、以下の通りです。

  1. GitHub OIDC プロバイダーをAWSアカウント側で準備
  2. 信頼関係を登録したGitHubリポジトリ用のIAM Roleを用意
  3. 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の接続試行に失敗しました。
image.png

Cloud TrailでGitHub ActionsからAWSへのアクセスログを確認していくと、pull_requestをトリガーにしたGitHub Actionsワークフローからのアクセスは、userNamerepo:<GitHubユーザー名>/<GitHubリポジトリ名>:pull_requestになることがわかります。
image.png

完全に盲点でした。

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ロールを引き受けるプロセスが成功した例は以下の通りです。
image.png

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ワークフローで指定するpermissionsid-token: write設定を含め、セキュリティを確保するための適切な設定が何かをワークフローの特性によって判断し、明示的に定義してあげる必要があります。

最後に、本記事の情報が少しでも誰かのお役に立てれば幸いです。

12
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4