概要 | AWS OIDC
OIDC についてまとめています。
OIDC の利点は長期認証情報の排除です(デフォルトで一時認証情報の有効期限は 1 時間です)。
さらに使用元も制限できます(例えば CircleCI の特定のプロジェクトからの実行のみ許可)。
本記事では接続元を CircleCI として進めます。
bot ユーザー + アクセスキーの問題点
| 問題 | 内容 |
|---|---|
| 長期認証情報 | アクセスキーは手動で削除するまで有効。漏洩しても気づきにくい |
| 漏洩リスク | CircleCI の環境変数に保存 → CircleCI が侵害されたら即アウト |
| ローテーション負担 | 定期的に手動で交換 + CircleCI の環境変数も更新が必要 |
| 使用元の制限が弱い | IAM ポリシーで IP 制限などはできるが、CI のプロジェクト単位では制限できない。IP 制限などを設定していなければ、漏洩したキーは世界中どこからでも使える |
OIDC が解決する点
OIDC では CircleCI がジョブ実行ごとに JWT(JSON Web Token)トークン(一時的な身元証明書)を発行し、AWS STS ( AWS Security Token Service )がそれと引き換えに 有効期限付きの一時認証情報 を返します。
CircleCI Job
|
| (1) JWT トークン発行( CircleCI が署名)
|
▼
AWS STS ( AssumeRoleWithWebIdentity )
|
| (2) JWT を検証 → 一時認証情報を発行(有効期限: 1 時間等)
▼
AWS リソース操作
|
| (3) 有効期限切れで自動失効
|
▼
(終了)
| 比較 | bot ユーザー + アクセスキー | OIDC |
|---|---|---|
| 認証情報の寿命 | 永続(手動削除まで) | 短期(自動失効) |
| CircleCI に保存するシークレット | あり(アクセスキー) | なし |
| 漏洩時の影響 | 失効させるまで悪用され続ける | すぐ期限切れ |
| 使用元の制限 | IP 等では可能だが、CircleCI の project 単位では不可 | 可(特定の org / project のみ許可) |
| ローテーション | 手動で必要 | 不要 |
条件の絞り込み例(trust policy の Condition):
"Condition": {
"StringEquals": {
"oidc.circleci.com/org/xxx:aud": "xxx"
},
"StringLike": {
"oidc.circleci.com/org/xxx:sub": "org/xxx/project/yyy/user/*"
}
}
xxx:CircleCI の Organization IDyyy:CircleCI の Project ID
NOTE
ブランチ単位の制限はできません
CircleCI のsubはorg/<Organization ID>/project/<Project ID>/user/<User ID>の形式で、ブランチ名は含まれません。そのため AWS の trust policy で「特定のブランチからのみ許可」とすることはできず、絞り込めるのは project 単位までです(GitHub Actions のsubにはref:refs/heads/...が含まれるためブランチ単位で制限できます)。
Appendix: JWT トークン
JWT とは
JWT(JSON Web Token)は、2 者間で安全に通信するための 署名付き JSON です。OIDC では接続元(CircleCI)の身元を証明するために使います。
構造
JWT は . で区切られた 3 つの部分から構成されます。
eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL29pZGMuY2lyY2xlY2kuY29tL29yZy94eHgifQ.signature
▲ ▲ ▲
Header Payload Signature
| 部分 | 内容 |
|---|---|
| Header | 署名アルゴリズム(例:RS256)とトークン種別 |
| Payload | クレーム(誰が・どこから・いつまで有効か) |
| Signature | Header + Payload を秘密鍵で署名したもの |
各部分は Base64 URL エンコードされているだけなので、デコードすれば中身を読めます(ただし改ざんは署名で検出できます)。
Payload のクレーム例(CircleCI)
以下は主要なクレームの抜粋です(実際のトークンにはこのほかのクレームも含まれます)。
{
"iss": "https://oidc.circleci.com/org/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"sub": "org/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/project/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/user/zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz",
"aud": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"exp": 1713800000,
"iat": 1713796400
}
| クレーム | 意味 |
|---|---|
| iss(Issuer) | トークンの発行者(CircleCI の OIDC エンドポイント。Organization ごとに URL が異なる) |
| sub(Subject) | トークンの主体(どの org / project / user か) |
| aud(Audience) | トークンの受取先。CircleCI では Organization ID(AWS の OIDC プロバイダーに設定する Audience と一致させる) |
| exp(Expiration) | 有効期限(Unix タイムスタンプ) |
| iat(Issued At) | 発行日時 |
AWS が JWT を信頼できる理由
AWS は、IAM に登録した OIDC プロバイダー(OIDC IdP:OpenID Connect Identity Provider)の URL(https://oidc.circleci.com/org/<Organization ID>)を元に、/.well-known/openid-configuration から辿れる JWKS(公開鍵のセット)を取得して Signature を検証します。ユーザーが公開鍵を登録するわけではありません。秘密鍵は CircleCI だけが持っているため、署名が正しければ CircleCI が発行したトークンと確認できます。
CircleCI(秘密鍵で署名)→ JWT → AWS STS(OIDC IdP の JWKS から取得した公開鍵で検証)→ OK → 一時認証情報を発行
trust policy の Condition は、この Payload のクレーム値(aud / sub)を使って「どの CircleCI project からの実行か」を絞り込んでいます。