「GitHub Actionsにアクセスキーを埋めたくない!」と願う全エンジニア(主に自分)へ
안녕하신게라!パナソニック コネクト株式会社クラウドソリューション部の加賀です。
前回の記事では、EC2やLambdaのようなクラウド内部のワークロード(機械)が、AssumeRoleに相当する機能を使って権限を委任する仕組みを比較しました。
今回はその続編として、「外部のワークロード」に焦点を当てます。
現代の開発において、GitHub ActionsやCircleCIといったCI/CDツールは不可欠な存在です。これらのツールがビルドやデプロイを行う際、クラウド上のリソース(S3バケット、コンテナリポジトリ、VMなど)を操作する必要があります。
しかし、ここで大きな課題が立ちはだかります。
「CI/CDツールに対し、どうやって安全にクラウドの権限を渡すのか?」
リポジトリのSecretsに有効期間の長いアクセスキーを保存する方法は、漏洩時の影響範囲が甚大であることや、定期的なローテーションの手間と管理コストを考えると、もはやアンチパターンと言えるでしょう。
この記事では、この課題を解決する手法「Workload Identity連携(OIDC連携)」について、
- CI/CDパイプラインのセキュリティを向上させたい方
- AWSのOIDC連携の知識をベースに、GCP/Azureでの実装方法を知りたい方
- キーレス認証の裏側で何が起きているのか、概念的に理解したい方
に向けて、AWSのAssumeRoleWithWebIdentityを基準に、GCPとAzureでのキーレス認証の仕組みを、今回も各クラウドの「思想の違い」から比較・解説します。
先にこの記事の結論をまとめると、以下の3点です。
-
3クラウド共通
OIDC(OpenID Connect)を利用し、CI/CDツールが発行する短期的な「IDトークン」を信頼することで、キーレス認証を実現する。 -
AWS/GCP
STS(Security Token Service) が外部IDプロバイダ(IdP)を信頼し、クラウド内の役割(IAMロール/サービスアカウント)を引き受けさせるアプローチ。実装は似ている。 -
Azure
Entra IDのアプリケーション自体に外部IdPの信頼情報を追加するアプローチ。ID基盤であるEntra IDがすべてを仲介する、Azureならではの設計。
ここで紹介するOIDC連携は、有効期間の長い静的なアクセスキーが不要になる、いわゆる「キーレス認証」を実現する強力な手法です。
この記事では代表例としてGitHub Actionsを取り上げますが、OIDCをサポートする他のCI/CDツール(GitLab CI/CD, CircleCIなど)でも同様の考え方でセキュアな連携が可能です。
それでは、CI/CDパイプラインをセキュアにするための旅に出かけましょう!
※この記事も概念的な理解を目的としており、詳細な設定手順には触れません。
2種類のトークン
と、本題に入る前に、キーレス認証の核となる2種類のトークンについて簡単に触れます。
-
IDトークン
- OIDCプロバイダ(今回はGitHub)が発行する、「私は誰か」を証明するための身分証明書のようなもの。
- リポジトリ名 (
repository) やブランチ名 (ref) といった情報(クレーム)を含み、改ざんされないよう電子署名されています。 - 有効期間が非常に短く(GitHub Actionsの場合は数分)、一度きりの認証に利用します。
-
アクセストークン
- IDトークンをクラウド側に提示して得られる、「何ができるか」を証明するための通行許可証のようなもの。
- このトークンを使って、実際にクラウドリソースを操作します。
- この「通行許可証」の実装はクラウドごとに異なり、GCP/Azureでは文字通りのアクセストークンが、AWSでは「一時的なセキュリティクレデンシャル」(アクセスキーID、シークレットアクセスキー、セッショントークン)という形で発行されます。本記事では、総称して「アクセストークン」として記述します。
- 有効期間は通常1時間程度に設定されます。
CI/CDツールはまず「IDトークン」を取得し、それをクラウドに渡して信頼できる相手だと認めてもらった上で、操作権限を持つ「アクセストークン」を受け取る、という流れが基本になります。
【AWS】IAMロールと外部IDプロバイダ(OIDC)
まずは基準となるAWSの仕組みです。AWSでは、sts:AssumeRoleWithWebIdentityというAPIコールがこの仕組みの心臓部となります。
権限管理の仕組み → OIDCプロバイダと信頼ポリシー
AWSで外部ワークロード連携を実現するには、「IAM Identity Provider」と「IAMロールの信頼ポリシー」の2つを設定します。
-
IAM Identity Providerの作成
まず、信頼したい外部IDプロバイダ(IdP)の情報をAWSに登録します。GitHub Actionsの場合、https://token.actions.githubusercontent.comをプロバイダURL1として指定します。これは「このURLから発行されたIDトークンを検証する」という、信頼の起点となる宣言です。 -
IAMロールの信頼ポリシーの設定
次に、CI/CD用のIAMロールを作成し、その信頼ポリシーを編集します。前回の記事で解説した信頼ポリシーと似ていますが、今回は信頼する相手(Principal)がAWSのサービスではなく、先ほど作成したIAM Identity Providerになります。
さらに、Condition(条件) を使って、信頼をより厳格に絞り込みます。例えば、IDトークンに含まれるsub(Subject)というクレームをチェックすることで、「特定のリポジトリの、特定のブランチからの要求のみを許可する」といった制御が可能になります。
AWSのキーレス認証フロー
GitHub Actionsのワークフローで公式のaws-actions/configure-aws-credentialsアクションを利用した場合、裏側では以下のことが起きています。
- GitHub Actionsのランナーが、GitHubのOIDCプロバイダに対して「IDトークン」の発行を要求します。このトークンには、リポジトリ名やブランチ名などの情報が含まれています。
-
configure-aws-credentialsアクションが、このIDトークンを認証情報として利用し、AWSのSTSに対しAssumeRoleWithWebIdentityAPIを呼び出します。 - AWSのIAMは、IAM Identity Providerの登録情報とIAMロールの信頼ポリシー(PrincipalとCondition)を検証し、正当な要求であれば、そのIAMロールの権限を持つ短期的なAWSクレデンシャル(アクセスキーID、シークレットアクセスキー、セッショントークン)を発行します。これがAWSにおける「アクセストークン」に相当するものです。
- 以降のステップでは、この短期的なAWSクレデンシャルを使って、アクセスキーを保存することなくAWSリソースを安全に操作できます。
この仕組みは、STSが外部のIDを信頼して、一時的にAWS内のロールの役割を与えるという、AWSの基本的な権限委任モデル(AssumeRole)をWeb Identity向けに拡張したものであることが分かります。
【GCP】Workload Identity Federation
次にGCPです。GCPにはそのものずばりの「Workload Identity Federation」という機能があります。
AWSのOIDC連携と思想は非常に似ていますが、より「ID」に焦点を当てた実装になっています。
権限管理の仕組み → プールと属性マッピング
GCPでは、「Workload Identityプール」と「サービスアカウントのIAMバインディング」を使ってキーレス認証を実現します。
-
Workload Identityプールの作成
まず、外部IdPを管理するための「プール」を作成します。そして、そのプール内に「プロバイダ」としてGitHub(https://token.actions.githubusercontent.com)の情報を登録します。 -
属性マッピング
次に、IdPが発行するIDトークンのクレームを、GCPが理解できる属性にマッピングします。例えば、「GitHubのrepositoryクレームを、GCPのassertion.repositoryという属性として扱う」といったルールを定義します。 -
サービスアカウントへのIAMバインディング
最後に、CI/CDで使いたい権限を持つサービスアカウントに対して、「Workload Identity ユーザ (roles/iam.workloadIdentityUser)」という特殊なIAMロールを割り当てます。
このロールを割り当てる際のプリンシパルとして、先ほどマッピングした属性を使って「特定のリポジトリからの要求」を指定します。これにより、「GitHubのmy-org/my-repoリポジトリは、このサービスアカウントとして振る舞うこと(権限借用、Impersonate)を許可される」という信頼関係が結ばれます。
GCPのキーレス認証フロー
- GitHub ActionsがIDトークンを発行します。
- GCPの公式
google-github-actions/authアクションが、このIDトークンをGCPのSTS(Google Security Token Service)に提示し、Workload Identity Federation APIを通じてGCPサービスアカウントの短期的なアクセストークンとの交換を要求します。 - GCPのIAMは、Workload Identityプールの設定とサービスアカウントのIAMバインディングを検証し、正当な要求であれば、サービスアカウントの権限を持つアクセストークンを発行します。
- 以降のステップでは、このアクセストークンを使ってGCPリソースを操作します。
AWSが「外部IDにIAMロールをAssumeさせる」のに対し、GCPは「外部IDにサービスアカウントを権限借用(Impersonate)させる」というアプローチです。
どちらもSTSが仲介役となる点は共通していますが、GCPの方がより「IDの権限を借用する」という概念が色濃く出ています。
【Azure】Workload Identityフェデレーション
最後にAzureです。名前こそGCPと似ていますが、その設計思想はAWS/GCPとは大きく異なります。ここでもまた、すべての中心にMicrosoft Entra ID(以下、Entra ID)が存在します。
権限管理の仕組み → アプリケーションのフェデレーション資格情報
Azureでは、CI/CDパイプラインのような外部ワークロードは、Entra ID上に「アプリケーション登録」という定義として表現され、そのテナント内でのインスタンスである「サービスプリンシパル」として権限を持つことになります。そして、そのアプリケーションに対して「フェデレーション資格情報(Federated identity credential)」を設定することで、キーレス認証を実現します。
-
Entra IDにアプリケーションを登録
まず、CI/CDパイプラインに対応する「アプリケーション登録」をEntra IDに行い、ワークロードのIDである「サービスプリンシパル」を作成します。 -
Azureロールの割り当て
次に、このサービスプリンシパルに対して、必要なAzureロール(例「ストレージBLOBデータ共同作成者」)を、適切なスコープ(リソースグループなど)で割り当てます。これは通常のAzure RBACの設定と同じです。 -
フェデレーション資格情報の設定
最後に、このアプリケーションに「フェデレーション資格情報」を追加します。ここで、信頼する外部IdP(GitHub)のIssuer(発行者)やSubject identifier(サブジェクト識別子)を指定し、信頼する要求元を厳格に絞り込みます。
この設定により、「このサービスプリンシパルは、クライアントシークレットの代わりに、指定された外部IdPから発行されたIDトークンを認証情報として受け入れる」という状態になります。
Azureのキーレス認証フロー
- GitHub ActionsがIDトークンを発行します。
- Azureの公式
azure/loginアクションが、このIDトークンをEntra IDに提示し、アクセストークンを要求します。 - Entra IDは、対象のアプリケーション登録に設定された「フェデレーション資格情報」を検証し、認証が成功すれば、そのサービスプリンシパルに割り当てられたロールの権限を持つアクセストークンを発行します。
- 以降のステップでは、このアクセストークンを使ってAzureリソースを安全に操作できます。
AWS/GCPではSTSが外部IDと内部ロールの「仲介」をしていたのに対し、AzureではEntra IDのIDオブジェクト(サービスプリンシパル)自体が、外部IDトークンを直接信頼する構成になっています。どこまでもID中心であるAzureの思想は、ここでも貫かれています。
まとめ、OIDC連携のモデル比較
| 目的/機能 | AWS | GCP | Azure |
|---|---|---|---|
| 連携機能名 | AssumeRoleWithWebIdentity | Workload Identity Federation | Workload Identityフェデレーション |
| 設定の主体 | IAMロールの信頼ポリシー | サービスアカウントへのIAMバインディング | Entra IDアプリケーションのフェデレーション資格情報 |
| 仲介役 | AWS STS | Google STS | Entra ID |
| 思想 |
ロール委任型 外部IDにAWSロールの権限を一時的に委任する。 |
ID借用型 外部IDにGCPサービスアカウントの権限を一時的に借用させる。 |
IDフェデレーション型 Entra IDのID自体が外部IDを信頼し、自身の権限を行使させる。 |
セキュリティをさらに高めるために
OIDC連携は非常にセキュアですが、設定を誤ると意図しないアクセスを許可するリスクも伴います。設計・導入する際は、以下の点を常に意識してください。
-
最小権限の原則
CI/CDで利用するロールやサービスアカウントには、本当に必要な権限のみを付与します。 -
信頼条件の厳格化
AWSのConditionやAzureのサブジェクト識別子などで、リポジトリ名、ブランチ名、タグ名などを厳密に指定し、信頼する要求元を可能な限り絞り込みましょう。repo:my-org/*のようなワイルドカード指定は慎重に利用する必要があります。GitHub Actionsのenvironmentクレームを利用して、本番環境へのデプロイは特定ブランチからのみ許可する、といった制御は特に重要です。 -
監査ログの活用
キーレス認証による操作は、各クラウドの監査ログ(AWS CloudTrail, Google Cloud Audit Logs, Azure Activity Log)に記録されます。IDトークンのクレーム情報(リポジトリ名など)もログに残るため、「どのリポジトリの、どのワークフローが、いつ何をしたか」を正確に追跡できます。定期的な監査や、異常検知アラートの設定に活用しましょう。
3つのクラウドはすべて、OIDCというオープンな標準規格に乗ることで、セキュアなキーレス認証を実現しています。
しかしその実装方法は、各クラウドのID管理基盤の思想を色濃く反映した、三者三様のアプローチとなっています。
-
AWS
柔軟な権限委任が可能なSTSをハブとして外部IDと連携。 -
GCP
サービスアカウントというIDになりすまさせることで、疎結合な連携を実現。 -
Azure
すべてのIDの源泉であるEntra IDに、外部IDとの信頼関係を集約。
CI/CDパイプラインのセキュリティは、モダンなクラウド開発の生命線です。
この記事を参考に、皆さんのパイプラインからアクセスキーを追放し、よりセキュアで管理しやすい開発環境を構築する一助となれば幸いです。
次回予告
ここまで「ワークロードID(機械)」の権限管理について見てきました。次回は「ユーザID(人間)」に焦点を当てます。
OktaやEntra IDといったIdPからSSOでクラウドにログインする際、「IdPのグループとクラウドの権限はどう紐付けられるのか?」その実装の差からクラウドの設計思想の違いに触れていきます。お楽しみに!
お断り
記事内容は個人の見解であり、所属組織の立場や戦略・意見を代表するものではありません。
あくまでエンジニアとしての経験や考えを発信していますので、ご了承ください。
-
各クラウドは、このプロバイダURLを元に
.well-known/openid-configurationというメタデータエンドポイントにアクセスし、IDトークンの署名を検証するための公開鍵の場所(JWKS URI)などを自動的に取得します。これにより、提示されたIDトークンが本当にそのプロバイダから発行されたもので、かつ改ざんされていないことを保証します。 ↩