Help us understand the problem. What is going on with this article?

PythonでGoogle Cloud Storageの署名付きURLを作成する

More than 1 year has passed since last update.

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/

kai_kou
2004年からWeb系のシステムエンジニアとして開発、運用、マネジメントを経験。現在はアイレット(クラウドパック)に所属。 べ、別にいいね貰えたからってモチベーションが上がって記事とコードの品質があがるわけじゃないんだからね///
https://twitter.com/k_aik_ou
cloudpack
Amazon Web Services (AWS) の導入設計、環境構築、運用・保守をサポートするマネジドホスティングサービス
https://cloudpack.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした