🚀

GCPでgsutilは通るのにstorage.Client()が通らなかった話

2023/05/16に公開

困ったこと

Google Cloud Storageからオブジェクトを2通りの方法で取得しようとした。実行環境はローカル。

  1. gsutil
gsutil ls gs://bucket/[bucket]/[object]

成功。

gs://bucket/[bucket_name]/[object_path]
  1. Python Cloud Storage Client
from google.cloud import storage

def main():
    client = storage.Client()
    bucket = client.bucket("[bucket_name]")
    blob = bucket.blob("[object_path]")
    if blob.exists():
        print(blob.name)
    
if __name__ == "__main__":
    main()

失敗。

~~~(前略)~~~
google.api_core.exceptions.Forbidden: \
403 GET https://storage.googleapis.com/storage/~~~(中略)~~~ \
?fields=name&prettyPrint=false: \
XXX@gmail.com does not have storage.objects.get access to the Google Cloud Storage object. \
Permission 'storage.objects.get' denied on resource (or it may not exist).

gsutilは通ったが、storage.Client()403 Errorだった。

オチ

今回は2つのプロジェクト (それぞれ別のユーザアカウント)を切り替えながら作業していた。

  1. プロジェクトAでgcloud authgcloud auth application-default loginした
  2. プロジェクトBでgcloud authした (プロジェクトをBに切り替えた)

の順で行った後、プロジェクトBでgcloud auth application-default loginし忘れた
その結果、プロジェクトAのユーザアカウント (XXX@gmail.com)でプロジェクトBのstorage.Client()を呼び出してしまった。
プロジェクトAのユーザアカウントはプロジェクトBで何の権限ももたない (AとBはお互いに何の関係もないプロジェクトだった)ため、プロジェクトBではXXX@gmail.com does not have storage.objects.get access to the Google Cloud Storage object.になった訳である。

解決

前者 (認証済みアカウント)の一覧は

gcloud auth list

で確認できる。後者は確認できないっぽい。

教訓

両者の違いは次の通り (引用元)。

目的
gcloud auth login GCP CLIを認証する gcloud, gsutil, bq, etc.
gcloud auth application-default login 各言語のアプリケーションを認証する pythonのgoogle.cloud.storage

APIをコマンドラインから呼ぶときはgcloud auth loginを、python等から呼ぶときはgcloud auth application-default loginが必要な訳ですね。

その他

  • 環境変数GOOGLE_APPLICATION_CREDENTIALSに認証情報のjsonファイルを与えることで、gcloud auth application-default login (デフォルト認証情報)の代わりに好きなアカウントの認証情報を指定できる。
  • 今回はローカルでの話だったが、GCP (GKEインスタンス等)の内部ではWorkload Identityを用いることができる。ローカルではADCを、GCP上ではWorkload Identityをそれぞれ用いることで、サービスアカウントキーの使用を避け、セキュリティリスクを下げられる (引用元)。

参考記事

Discussion