CircleCI の OpenID Connect サポート

CircleCI Advent Calendar 2021 24 日目 です.
担当の makotom こと CircleCI の Senior DevOps Customer Engineer の水上がお送りします.

今年は去年と違ってちゃんとテクノロジーの話をします! 出オチにならないように最後まで書ききれるといいな......

# CircleCI が OpenID Connect (OIDC) をサポートします

**はい. します. 2022 年 1 月一般公開予定.**

このサポートにより, CircleCI のジョブに CircleCI が署名した OpenID Connect 準拠の ID Token (JWT) が付与されるようになります.
この ID Token を使用することで, 例えば AWS や GCP, Azure のようなクラウド プラットフォームでパスワードレスな認証を実現できたり, HashiCorp Vault のようなシークレット管理ソリューションとのパスワードレスな連携を実現できたりします.

GitHub Actions や BitBucket Pipelines にも類似機能があるので, それらに相当すると考えてもらうとわかりやすいと思います.
特に GitHub Actions については [miyajan さんがすばらしい記事を書いてくださっている](https://zenn.dev/miyajan/articles/github-actions-support-openid-connect) ので, こちらを参考にするとイメージしやすいかもしれません.

# エビデンス

開発中の様子をちょこっとだけお見せしますね!
画像では, 環境変数にあらかじめ格納されている JWT をファイルに書き出して AWS CLI から読みだせるようにすることで, `aws sts get-caller-identity` を呼び出したときに AWS 側の OIDC ID プロバイダーの設定に紐づけられたロールが返ってきているのがわかります.

![circle-oidc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/936920/03f16369-d513-e7b4-49c2-4ee4d8629b8b.png)

**ちなみに開発中の POC のスクリーンショットです. 製品版が公開される際には細部が異なる可能性があります.**

あとこのデモのジョブはカナダ在住のプロダクト エンジニアが用意してくれて, つかっていいよって気持ちよく言ってくれました. Thank you!
あ, CircleCI の中の人, 彼チョットデキル T シャツがほしいって言っていました. 送ってあげてください. "チョットデキル" に相応しい凄腕エンジニアです.

# どうやって使うの?

えーと...... 実は今日はここはお話しません.
一言でいうと "開発中なので" なのですが, 今現在 JWT のクレームの内容や OIDC を使えるようにするのに不可欠な OIDC Discovery の構成が未確定で変更の可能性が残っているため, 現時点の実装をベースにユーザーのみなさまが設定を実施すると将来的に非常に危険な状態に至る可能性があります.
だから, 今日はここでは使い方はお話ししません.

来年頭にはこのあたりも確定して公式ドキュメントでお伝えできると思いますのでお楽しみに!

# どういうこと?

非常に危険とかいわれるとビビりますよね.
ですので今日は何が危険なのかも含めて, CircleCI の実装を念頭に OIDC のしくみについてお話しします.

## そもそも OpenID Connect とはなにか

OpenID Connect とは, そもそもは認証者やアプリ提供者の分離などを特徴とするオープン スタンダードな認証プロトコルです。
おそらくわかりやすい例は [Google アカウント](https://developers.google.com/identity/protocols/oauth2/openid-connect) や [Microsoft アカウント / Azure AD アカウント](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc) でのログインや [Yahoo! ID 連携 v2](https://developer.yahoo.co.jp/yconnect/v2/) で, これらでは OIDC のプロトコルを活用することでシングル サインオン (SSO) やサービス連携を実現しています.

こう書くと Twitter や GitHub の OAuth っぽいな, と思われる方もいらっしゃると思います.
ご名答です.
OpenID Connect は [OAuth 2.0 の拡張として設計された経緯があります](https://openid.net/connect/faq/).

あと個人的には, SSO の例のように, OIDC や OAuth 2.0 は, 人間の認証 (AuthN) や人間による認可 (AuthZ) に重きがおかれている側面があるように思います.
そのため, 実際に CircleCI のようなコンピューター・プログラム実行環境のような人間ではないアイデンティティの証明の用途で使おうとするとちょっとした工夫や発想の転換が必要になります.
個人的には OIDC のように Web フレンドリーかつ OIDC よりは汎用的な公開鍵証明書 (certificate) に基づく認証プロトコルが普及すると世の中幸せになれるんだろうなあと思うところです.
TLS みたいな生の X.509 ベースにするとルート証明書管理がヘビーになりすぎるし, PGP/GPG は鍵サーバーが潰れたりするのでちょっと話にならんもんなあ......

## CircleCI ではどのように OIDC プロトコルを活用しているか

CircleCI では OIDC の公開鍵署名 (signature) に依拠するアイデンティティ証明のプロトコルを活用します.

少し噛み砕いて言うと, OIDC による SSO ではそもそも, Google や Microsoft, Yahoo! Japan が "このユーザーは誰々です" という情報を, それぞれプロバイダーが内容を証明する署名をつけてアプリに送ることで, アプリ側ではパスワードを要求することなくユーザーを特定・認証することができるようになっています.
そこで, 同じように "このジョブをは誰々により起動された何々のプロジェクト配下のジョブです" という情報を CircleCI が内容証明の署名をつけることで, その署名付き情報 (これを ID Token と呼び, JWT 呼びます) をもとにクラウド プラットフォーム/シークレット管理側ではパスワードのような共有鍵を使用することなくリクエスト元を特定・認証することができるようになるのです.

プロトコルの流れを図にしてみました.

![oidc-protocol.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/936920/6bcc69e6-7d8f-9b4c-5065-4b3615fc311e.png)

ところで, 図を見てみると注意すべき点が 2 つ見えてきます.

まず第一に, アクセス先の動作が JWT のクレーム内容の検証結果に依存するという点です.
もう少し細かく述べると, 実際のリソースのデプロイやキー発行といったリクエストの承認・拒否は, アクセス先 (AWS, GCP, Azure, Vault など) 側の設定による JWT のクレーム内容の精査結果に依存しているということです.
したがって, OIDC の大まかな流れや JWT の内容を正しく理解しないまま AWS や Vault を設定すると, 最悪のシナリオでは権限昇格が容易にできる状態になってしまいます.
特に, 一般に OIDC の実装においては, 一部の JWT のクレーム内容 (特に `aud` やメール アドレスに相当するデータ) をエンド ユーザーが実質的もしくは理論的に任意指定できるケースがあり, アクセス先の構成がそのことを考慮していないと誰でもなりすまし行為ができる状態となるため, 大変危険です. (これが先ほど言及のあった "将来的に非常に危険な状態に至る可能性" です.)

**一般にクラウドの世界で OIDC を活用する際には, まずは何が起きるのかをよく理解し, ぜひ注意を怠らずに使って**いただければと思います.
また CircleCI では, ユーザーのみなさまに安全に安心して使っていただけるよう, 設計を十分に検討し, またドキュメントも整備していく予定です.

そして第二に, この枠組みは CircleCI とユーザーのみなさま (細かく言えばはユーザーの AWS, GCP, Azure, Vault など) との間での公開鍵暗号を用いたデータ交換を基軸とした信頼関係の上に成り立っているという点です.
したがって, "OIDC のおかげでパスワードがなくなって安全になったハッピー!" と考えるのはすこし短絡的です.
なんなら AWS では OIDC にしても IAM インスタンス ロールにしても内部的にはセッション トークン = パスワードが使われているので, どこまでいってもパスワードはなくなりません.
それに, OIDC の世界でも, 一般に JWT は適切に取り扱う必要があります (安易に第三者に見せてよいようなものではありません).

むしろ, OIDC のサポートにより実現されるのは, 従来必要だったユーザーのみなさまご自身によるパスワード保管やキー ローテーションといったやっかいな作業の省略です.
OIDC の JWT は使い捨て利用が前提で, 最初から有効期限が短く設定されているため, 認証・認可を OIDC ベースのに切り替えられればユーザー自身でのパスワード保管やキー ローテーションは必要なくなります.
つまり, 有効期限の短い使い捨て可能なトークンの発行や, 用途別の細かい権限の割り当てといった, セキュリティ上のリスクを軽減するための対策を, 手作業ではなく, CircleCI とのシステマティックな信頼関係の構築で実現できる, ということです.
CI/CD の文脈でいえば, **ゼロリスクははなから不可能なのであって, リスクのコントロールやトラブル発生時の影響範囲の限定が重要であり, 人手を介さず複雑なオペレーションを必要としない方法としてそれを実現してくれるのが CI/CD における OIDC**, ということです.
はい, 弊社 CTO 著の[本番環境でテストするための合理的ガイド](https://www2.circleci.com/TestinProduction-JP.html)につながってきましたね!!!

# おわりに

ということで, この記事では 2022 年 1 月一般公開予定の CircleCI の OpenID Connect サポートをネタに, CI/CD と OIDC の組み合わせについて考えてみました.
この記事が CircleCI をつかって他サービス・ソリューションとのいい感じな連携を実現するきっかけになれば大変嬉しいですし, CircleCI に限らず OIDC を活用する際の参考にしていただけますと幸いです.

明日 25 日 Christmas Day は @RyuNen344 さんがご担当です. よろしくお願いしますー!