⬇️

Cloud Run上でサービスアカウントキー.jsonファイルを使わずにCloud Storageの署名付きURLを生成したい

2025/03/01に公開

ハマった経緯と内容

Cloud Run上で動かすアプリケーションにおいて「Cloud Storageに格納しているファイルを署名付きURLを利用してダウンロードする」機能を実装する必要がありました。

ローカル環境では、サービスアカウントキーのファイル(jsonファイル)を利用してGoogle Cloudのドキュメントを参考にプログラムを作成し、署名付URLの生成とファイルのダウンロードも成功しました。

https://cloud.google.com/storage/docs/access-control/signing-urls-with-helpers?hl=ja

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:
https://cloud.google.com/storage/docs/access-control/signing-urls-with-helpers?hl=ja
https://stackoverflow.com/questions/64234214/how-to-generate-a-blob-signed-url-in-google-cloud-run

① 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