やりたいこと!
やりたいことはシンプルで「GKE上のPodに鍵(JSON)を置かずに署名付きURLを発行したい」です。
そのままでは秘密鍵が無いので失敗しますが、Impersonated Credentials (サービスアカウント成りすまし) を使えば一時的に署名可能なクレデンシャルを取得でき、blob.generate_signed_url() に渡して生成できます。
ポイントは次の3つです:
- 署名に使うサービスアカウントを決める (最小権限)
- GKEの実行中サービスアカウントから成りすまし (TokenCreator 権限必須)
- 取得した一時クレデンシャルで署名付きURLを発行
背景と困りごと
GKE上のアプリは Workload Identity などでアクセストークンは簡単に取れます。
しかし「署名付きURL」はアクセストークンだけでは足りず、内部的に秘密鍵か署名API相当が必要になります。
そこで以下のエラー。
AttributeError: you need a private key to sign credentials.
the credentials you are currently using <class 'google.auth.compute_engine.credentials.Credentials'> just contains a token.
compute_engine.credentials.Credentials はアクセストークンのみで秘密鍵を保持していないため、そのままでは署名付きURL (とくに V4) を発行できません。
解決アプローチざっくり
考え方は「鍵ファイルを置く代わりに その場で署名可能な一時資格 を作る」。
これで JSON の配布や Secret 管理を避けつつ、最小権限 + 有効期限付きで安全に回せます。
長期鍵を持たない = うっかりGitにコミット / 流出リスクを減らせる のが嬉しいところ。
アーキテクチャ図 (動きのイメージ)
前提条件 (ロールと設定)
まず IAM 周りを整えます。最低限これが揃えば動きます。
| 対象 | 必要ロール例 | 用途 |
|---|---|---|
| 成りすます元 (Pod/GKEのSA) |
roles/iam.serviceAccountTokenCreator (対象SAに対して) |
Impersonation用トークン生成 |
| 署名用サービスアカウント |
roles/storage.objectViewer など最小限 |
対象オブジェクトの参照 |
Workload Identity 使用時は Kubernetes のサービスアカウントと GCP のサービスアカウントを関連付けるバインド(IAM policy binding)が必要です。
「紐付け忘れで 403」になりがちなので最初に確認しておくと安心です。
ライブラリ / 実行環境
動作確認は Python 3.10+。他バージョンでも大きな差はありません。
インストールはこれだけ:
pip install google-cloud-storage==2.11.0
署名付きURL生成関数のシグネチャ (抜粋)
google-cloud-storage の Blob.generate_signed_url は内部で秘密鍵か署名APIを呼び出します。
https://github.com/googleapis/python-storage/blob/main/google/cloud/storage/blob.py
主な引数抜粋 (よく使うものだけ):
-
expiration: 有効期限 (datetime / timedelta) -
method: GET, PUT など HTTP メソッド -
version: ほぼ"v4"を推奨 -
credentials: 署名可能クレデンシャル -
service_account_email: 署名実行主体のメール (一部ケースで必須)
実装ステップ
1. Impersonated Credentials を取得
ここが肝です。google.auth.impersonated_credentials.Credentials を使って「署名用サービスアカウントに一瞬だけなりすます」イメージです。
from google import auth
from google.auth import impersonated_credentials
TARGET_SERVICE_ACCOUNT = "署名用サービスアカウント@<your-project-id>.iam.gserviceaccount.com"
SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
def get_impersonated_credentials():
# GKE上なら Workload Identity / メタデータサーバ経由で source_credentials が取得される
source_credentials, project_id = auth.default()
return impersonated_credentials.Credentials(
source_credentials=source_credentials,
target_principal=TARGET_SERVICE_ACCOUNT,
target_scopes=SCOPES,
lifetime=3600, # 必要に応じて (最大3600)
)
2. 署名付きURLを発行
取得した credentials をそのまま渡せばOK。Client(credentials=...) としておくと混乱しにくいです。
import datetime
from google.cloud import storage
BUCKET = "your-bucket-name"
def generate_signed_url(file_path: str, minutes: int = 10) -> str:
credentials = get_impersonated_credentials()
client = storage.Client(credentials=credentials) # 念のため credentials 明示
blob = client.bucket(BUCKET).blob(file_path)
return blob.generate_signed_url(
expiration=datetime.timedelta(minutes=minutes),
method="GET",
version="v4",
credentials=credentials,
service_account_email=credentials.service_account_email,
)
3. 使用例 (シンプルな呼び出し)
if __name__ == "__main__":
url = generate_signed_url("path/to/object.txt")
print(url) # 有効期限内のみアクセス可能
よくあるエラー / つまづきポイント
ハマりがちなポイントをまとめました。初回はだいたいこのどれかです。
| 症状 | 原因 | 対処 |
|---|---|---|
AttributeError: you need a private key... |
Impersonation未設定 / roles不足 |
roles/iam.serviceAccountTokenCreator を付与 |
| 403 / 権限エラー | 署名用SAにObject閲覧権限なし |
roles/storage.objectViewer など追加 |
| 署名後アクセス拒否 | バケット名 / パス不整合 | 実オブジェクトパスを再確認 |
| 有効期限が想定より短い | lifetime / expiration の設定不備 |
lifetime と expiration を見直し |
セキュリティ / 運用上の注意
便利さと引き換えに権限が広がりすぎないようにだけ注意しましょう。
- 成りすまし可能な範囲を最小限 (対象SA限定) にする
- 署名専用サービスアカウントの権限は Object Viewer など必要最小限
- URLの有効期限は短め (数分〜1日) を基本にしましょう
- 監査ログで
GenerateAccessToken/Impersonateを定期確認
さらに発展 (オプション)
慣れてきたらここも検討すると便利になります。
- PUT/POST 用の署名付きURLでクライアント直接アップロード
-
content_typeやresponse_dispositionを指定してダウンロード挙動制御 - Virtual hosted-style / カスタムドメイン (Cloud CDN + Cloud Storage) との統合
まとめ
Impersonated Credentials を使うと「鍵ファイル置かないけど署名付きURLは作れる」状態が作れます。
ポイントは TokenCreator 権限付与と最小権限化、それから短めの期限運用。
ここを押さえておけば安全性と開発体験を両立できます。
参考リンク
- Google Cloud Storage Signed URLs: https://cloud.google.com/storage/docs/access-control/signing-urls-with-helpers
- python-storage 実装: https://github.com/googleapis/python-storage
- Impersonated Credentials: https://cloud.google.com/iam/docs/service-account-creds
- Workload Identity: https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity
ご自身の環境に合わせてロール / 有効期限を微調整してみてください。
質問・フィードバック歓迎です 🙌