🚀
GCPでgsutilは通るのにstorage.Client()が通らなかった話
困ったこと
Google Cloud Storageからオブジェクトを2通りの方法で取得しようとした。実行環境はローカル。
- gsutil
gsutil ls gs://bucket/[bucket]/[object]
成功。
gs://bucket/[bucket_name]/[object_path]
- 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つのプロジェクト (それぞれ別のユーザアカウント)を切り替えながら作業していた。
- プロジェクトAで
gcloud auth
とgcloud auth application-default login
した - プロジェクト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