Cloud Run上でサービスアカウントキー.jsonファイルを使わずにCloud Storageの署名付きURLを生成したい
ハマった経緯と内容
Cloud Run上で動かすアプリケーションにおいて「Cloud Storageに格納しているファイルを署名付きURLを利用してダウンロードする」機能を実装する必要がありました。
ローカル環境では、サービスアカウントキーのファイル(jsonファイル)を利用してGoogle Cloudのドキュメントを参考にプログラムを作成し、署名付URLの生成とファイルのダウンロードも成功しました。
import datetime
from google.cloud import storage
def generate_download_signed_url_v4(bucket_name, blob_name):
"""Generates a v4 signed URL for downloading a blob.
Note that this method requires a service account key file. You can not use
this if you are using Application Default Credentials from Google Compute
Engine or from the Google Cloud SDK.
"""
# bucket_name = 'your-bucket-name'
# blob_name = 'your-object-name'
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(blob_name)
url = blob.generate_signed_url(
version="v4",
# This URL is valid for 15 minutes
expiration=datetime.timedelta(minutes=15),
# Allow GET requests using this URL.
method="GET",
)
print("Generated GET signed URL:")
print(url)
print("You can use this URL with any user agent, for example:")
print(f"curl '{url}'")
return url
Google Cloud環境では、サービスアカウントキーのjsonファイルを利用せずに、Cloud Runにサービスアカウントを紐づける方法が一般的です。(jsonファイルは漏洩すると、世界中の誰でもリソースにアクセスできてしまい、セキュリティのリスクが高いため)
Cloud Runにストレージ オブジェクト閲覧者(roles/storage.objectViewer
)ロールを付与してアプリケーション動かしてみたところ、署名付きURLを利用したファイルのダンロードに失敗しました。
エラーの内容は以下でした。
また、サンプルコードのコメントアウト部分を読むと「このメソッドはサービスアカウントキーのファイルが必要です。Google Compute Engine、またはGoogle Cloud SDKからデフォルト認証情報を使用している場合、利用できません。」と丁寧に書かれていました。
Google Cloud環境でアプリケーションを動かす際もサービスアカウントキーが必要になるという経験がなかったので、少し驚きました。
ただ、サービスアカウントキーのjsonファイルを発行したままにすることに抵抗があったため、jsonファイルを発行せずに、Cloud Runのサービスアカウントのみで署名付きURLを生成できる方法を探しました。
jsonファイルを利用せずに署名付きURLを生成する
参考にしたURL:
① Cloud Runに紐づけるロール
ストレージ オブジェクト閲覧者(roles/storage.objectViewer
)ロールに加えて、以下のロールを付与します。
- サービス アカウント トークン作成者(
roles/iam.serviceAccountTokenCreator
)
秘密鍵ファイルがローカルに提供されていない場合に、サービス アカウントに有効期間の短い認証情報を生成するには、このロールが必要です。このロールは、署名付き URL を作成するプリンシパルに付与する必要があります。
② サンプルコードの変更
サンプルコードを以下のように変更します。
import datetime
+ # pip install google-auth
+ import google.auth
+ import google.auth.transport.requests
from google.cloud import storage
def generate_download_signed_url_v4(bucket_name, blob_name):
""" Generates a v4 signed URL for downloading a blob.
Note that this method requires a service account key file. You can not use
this if you are using Application Default Credentials from Google Compute
Engine or from the Google Cloud SDK.
"""
+ credentials, _ = google.auth.default()
+ credentials.refresh(google.auth.transport.requests.Request())
# bucket_name = 'your-bucket-name'
# blob_name = 'your-object-name'
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(blob_name)
url = blob.generate_signed_url(
version="v4",
# This URL is valid for 15 minutes
expiration=datetime.timedelta(minutes=15),
# Allow GET requests using this URL.
method="GET",
+ service_account_email=credentials.service_account_email,
+ access_token=credentials.token,
)
print("Generated GET signed URL:")
print(url)
print("You can use this URL with any user agent, for example:")
print(f"curl '{url}'")
return url
これで、jsonファイルなしでもCloud Run上で署名付きURLを生成できるようになりました。
Discussion