Google Cloud Platform その2 Advent Calendar 2018の3日目の投稿となります。
Google App Engine(GAE)でアプリ開発をしていたのですが、ファイルアップロード機能を実装していると、アップロードできるファイルサイズに制限があったため、Google Cloud Storage(GCS)の署名付きURLを作成して直接アップロードすることにしました。
署名付きURLを作成するのに1日ほどハマったので、まとめておきます。
環境構築
仮想環境を作成して環境を構築します。
ソースはGitHubにアップしていますので、よければご参考ください。
https://github.com/kai-kou/create-cloud-storage-signed-url
> python --version Python 3.6.6 > python -m venv venv >. venv/bin/activate > touch requirements.txt > touch main.py
Google Cloud Platform(GCP)のサービスアカウントを利用してURLを生成するのに、oauth2clientを利用します。
requirements.txt
oauth2client
実装は下記を参考にさせてもらいました。
authentication – Cloud storage and secure download strategy on app engine. GCS acl or blobstore – Stack Overflow
https://stackoverflow.com/questions/29847759/cloud-storage-and-secure-download-strategy-on-app-engine-gcs-acl-or-blobstore
署名付きURLの作成方法については公式ドキュメントが詳しかったです。
Generating Signed URLs with Your Own Program | Cloud Storage | Google Cloud
https://cloud.google.com/storage/docs/access-control/signing-urls-manually
main.py
import time import urllib from datetime import datetime, timedelta import os import base64 from oauth2client.service_account import ServiceAccountCredentials API_ACCESS_ENDPOINT = 'https://storage.googleapis.com' def sign_url(bucket, bucket_object, method, expires_after_seconds=60): gcs_filename = '/%s/%s' % (bucket, bucket_object) content_md5, content_type = None, None credentials = ServiceAccountCredentials.from_json_keyfile_name('[サービスアカウントキーのファイルパス]') google_access_id = credentials.service_account_email expiration = datetime.now() + timedelta(seconds=expires_after_seconds) expiration = int(time.mktime(expiration.timetuple())) signature_string = '\n'.join([ method, content_md5 or '', content_type or '', str(expiration), gcs_filename]) _, signature_bytes = credentials.sign_blob(signature_string) signature = base64.b64encode(signature_bytes) query_params = {'GoogleAccessId': google_access_id, 'Expires': str(expiration), 'Signature': signature} return '{endpoint}{resource}?{querystring}'.format( endpoint=API_ACCESS_ENDPOINT, resource=gcs_filename, querystring=urllib.parse.urlencode(query_params)) if __name__ == '__main__': url = sign_url('[バケット名]', '[オブジェクト名(ファイル名)]', 'GET') print(url)
上記で指定する[サービスアカウントキーのファイルパス]はGCPのサービスアカウントを作成すると取得できるjsonファイルとなります。サービスアカウントには必要なGCSの役割を付与します。
サービスアカウントの作成方法は下記が詳しかったです。
Google Cloud Platform のサービスアカウントキーを作成する | MAGELLAN BLOCKS
https://www.magellanic-clouds.com/blocks/guide/create-gcp-service-account-key/
実行する
実行するとURLが生成されます。URLにアクセスする、指定したGCSのバケットにあるオブジェクトが取得できることが確認できます。
> python main.py https://storage.googleapis.com/[バケット名]/[オブジェクト名]?GoogleAccessId=xxxxx%40xxxxx.iam.gserviceaccount.com&Expires=1542687588&Signature=xxxxxxxxxx
ハマりポイント
GCPサービス上でGoogleCredentials.get_application_default()
が使えない
Google App Engine(GAE)やGoogle Cloud Functions(GCF)だと、実行に利用しているサービスアカウントの情報が取得できるので、GoogleCredentials.get_application_default()
で Credentials
を利用しようとしたのですが、service_account_email
が空で、sign_blob
メソッドも利用できませんでした。
まとめ
AWSだと署名付きURLの作成はもう少し簡単だった記憶があったので、GCPでも同じ感覚でいたら、ひどい目にあいました。自前でURLを組み立てる必要があったので、手間がかかりましたが、作成方法さえわかれば、あとは、GAEやGCFでの利用も簡単そうです。
参考
authentication – Cloud storage and secure download strategy on app engine. GCS acl or blobstore – Stack Overflow
https://stackoverflow.com/questions/29847759/cloud-storage-and-secure-download-strategy-on-app-engine-gcs-acl-or-blobstore
Generating Signed URLs with Your Own Program | Cloud Storage | Google Cloud
https://cloud.google.com/storage/docs/access-control/signing-urls-manually
Google Cloud Platform のサービスアカウントキーを作成する | MAGELLAN BLOCKS
https://www.magellanic-clouds.com/blocks/guide/create-gcp-service-account-key/