📚

gcloud CLI から Directory API (とくに users )を操作する方法が分からなかったのでソースコードを眺めた

2023/06/10に公開

「gcloud CLI から Directory API (とくに users API) をたたく方法」が分からなかったので、ソースコードを眺めて処理を追ってみた備忘録。結論としては結局やっぱりよく分からなかったが、できなさそう。

gcloud CLI には identity サブコマンドがあるが、そのサブコマンドには実は groups しかない。
gcloud identity groups

identity のサブコマンドの ヘルプからもこれから意図された設計であることが分かる。

$ gcloud identity --help | head -3
NAME
    gcloud identity - manage Cloud Identity Groups and Memberships resources

ということで、そもそも gcloud CLI から直接的に Cloud Identity の User 操作する方法はないと思われる。 Cloud Identity は Google Cloud のサービスではないと思う(あってる?)ので、むしろ groups が操作できるのが異端なのかもしれない。

というわけで gcloud auth print-access-token コマンドを使って Directory API の Users を操作できるアクセストークンを発行できないか、というふうに実現方法を変えた。できたところでたぶん実際には使わないんだけど興味本位。

例えば各言語の SDK だとこの方法が提供されていて、例えば Python だと
https://github.com/hatappo/sample-google-admin-user-api-python/blob/main/get-users-list.py#L12-L13

delegated_credentials = credentials.with_subject('*****@*****.jp')

のような感じで(DWD が適切に設定されている前提で)、 GCP ではなく Cloud Identity 側で権限のあるユーザアカウントのメールアドレスを oatuh の sub に指定することでそのユーザとして Deligation されることができる。
Javascript の SDK のほうが クレームで sub を追加する操作がよりわかりやすい。
https://github.com/hatappo/sample-google-admin-user-api-javascript/blob/main/main.ts#L11-L15

const auth = new JWT({subject: "*****@*****.jp"});

というわけで、これと同様のことを gcloud auth print-access-token で実行する方法があるのか、という話になる。

例えば brew で入れた gcloud CLI の場合、コマンドの実体は単なる shell script

$ command -v gcloud
/opt/homebrew/bin//gcloud

$ head -4 /opt/homebrew/bin//gcloud
#!/bin/sh
#
# Copyright 2013 Google Inc. All Rights Reserved.
#

gcloud shell script はソースコードでいうとこれ(※ 謎のミラーリポジトリしか見つからなかったんだけど本家はどこですか?)。
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/bin/gcloud
単に適切な Google Cloud SDK の場所をファイルシステムから探して、見つけたらそこの ib/gcloud.py を実行しているだけ(L182)。

ここで大きな問題に気付く。自分は Python をそんなに使ったことがない。いったん無視して進みます。

ib/gcloud.py
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/lib/gcloud.py
これもエラーハンドリングや path の調整をしているだけで、最終的に SDK 本体の gcloud_main.py を呼んでいる(L128)。

lib/googlecloudsdk/gcloud_main.py
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/lib/googlecloudsdk/gcloud_main.py
これも必要なサブコマンドのモジュールを取ってきて gcloud オブジェクトを作るかロードしてきてるだけ。
各サブコマンドのモジュールは lib/surface フォルダでサブフォルダに配置されている。

lib/surface フォルダ
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/tree/master/lib/surface
ここに確かにさっきの identity サブコマンドのフォルダがあって、その中に groups しかないのも確認できる。
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/tree/master/lib/surface/identity

gcloud auth print-access-token が見たいので、たどると該当のモジュール はこれ。
lib/surface/auth/print_access_token.py
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/lib/surface/auth/print_access_token.py
トークンを取り出す処理を見ると、クリデンシャルのオブジェクトの token あるいは acess_token プロパティに保持していることがわかる(2種類あるのは oauth2client と google-auth の両方のライブラリのオブジェクトをサポートしているため。前者は既に非推奨。)
クリデンシャルを作っているのが L122 や L157 のあたりで、 store という、キャッシュを兼ねてクリデンシャルの作成をしているコードが呼ばれている。L32 で Import されている。

また lib/surface/auth/print_access_token.py クラスを見ると、

class FakeCredentials(object):
  """An access token container.

  oauth2client and google-auth are both supported by gcloud as the auth library.
  credentials in oauth2client store the access token in the "access_token"
  filed. google-auth stores it in the "token" filed. We use this fake
  credentials class to unify them.
  """

という感じで oauth2clientgoogle-auth という2つの OAuth ライブラリがあることが書かれている。前者は既に古く非推奨になっている。

lib/googlecloudsdk/core/credentials/store.py
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/lib/googlecloudsdk/core/credentials/store.py
このコードがやや長いけど、基本的にクリデンシャルのライフサイクル管理を全体的にやっているっぽい。
id_token の取得の処理はここにあるが、 access_token は隣の creds.py でやってる。

lib/googlecloudsdk/core/credentials/creds.py
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/lib/googlecloudsdk/core/credentials/creds.py
ここで、 OAuth の処理は print_access_token.py でも触れた2種類のライブラリを使っている。

  • oauth2client
  • google-auth

前者の oauth2client のほう。
oauth2client/service_account.py
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/lib/third_party/oauth2client/service_account.py#L523-L542
のほうでは create_with_claims という自由に claim を足してクリデンシャルを作る目的の関数がある。隣の create_delegated という関数は単に sub claim を設定する形で create_with_claims を呼び出している。このあたりの関数を呼べば sub を設定できるっぽいが、検索してもリポジトリの中で使われている気配はない。

後者の google-auth のほう。
https://github.com/google-cloud-sdk-unofficial/google-cloud-sdk/blob/master/lib/third_party/google/oauth2/service_account.py#L326-L338
with_subject 関数が sub claim を設定するのに使える。

全体的に OAuth 2.0 Token Exchange を行っている?
https://qiita.com/TakahikoKawasaki/items/d9be1b509ade87c337f2

Next

気が向いたらもっと読んで更新します。

Discussion