Python
AWS
S3

Python で Amazon CloudFront の Signed URL を発行する

More than 5 years have passed since last update.


CloudFront x S3 => 期限付き URL

facebook や flickr などのように、一部ユーザにだけ画像を見せたいというケース、ありますよね。こういうサービスでは、画像に対して期限付きの URL を発行するのが一般的のようです。Amazon CloudFront と Amazon S3 を使うと、こんな形の期限付き URL を発行できます。


http://d3iwlid5dsmdb3.cloudfront.net/_default_san.png?Expires=1354332636&Signature=EWVDLqfSRDJFlOazy9ruQV96xPravIFbiUq9GOsTVaL0Uhj3u6LEWXoM3042rWddnc4+CLop/mBCY5P/WcMyDcR5LGFfxEvCltsLYXtjCR+Djnh0WEsp+ogWlGjYAn6eZNtjUfl9FlMbiC6tn7Ehs9oMJu+EaYgpGM6aSlyQ88A=&Key-Pair-Id=APKAJ4YNPASFVCNBXK3A


CloudFront と S3 のつなぎ込みや、鍵ペアの発行などの手順は公式ドキュメントの Serving Private Content through CloudFront に書いてあります。そして最終的な URL の生成についてのコードが Perl、PHP、C#(.NET Framework)、Java で書かれているのですが、Python がありません。これでは仕事になりませんね。


tlslite を使って Signed URL を発行する

Python で AWS といえば boto……なのですが、今回は違った方法を使います。boto が駄目だった理由は後述します。

"python sign private key" で検索したところ、Signing a string with RSA private key on Google App Engine Python SDK がヒットしました。そこにはこう書いてありました。


The library tlslite included in the gdata python library is a good option.


ふむふむ、tlslite がいいらしい。さっそくインストールしてみましょう。

(venv)rch850mba:Desktop rch850$ pip install tlslite

Downloading/unpacking tlslite
Downloading tlslite-0.4.3.tar.gz (562Kb): 562Kb downloaded
Running setup.py egg_info for package tlslite

Installing collected packages: tlslite
Running setup.py install for tlslite
changing mode of build/scripts-2.7/tls.py from 644 to 755
changing mode of build/scripts-2.7/tlsdb.py from 644 to 755

changing mode of /Users/rch850/Desktop/cloudfront/venv/bin/tls.py to 755
changing mode of /Users/rch850/Desktop/cloudfront/venv/bin/tlsdb.py to 755
Successfully installed tlslite
Cleaning up...

そして例を見ながらコーディング。

from tlslite.utils import keyfactory

from base64 import b64encode
import time

url = "http://d3iwlid5dsmdb3.cloudfront.net/_default_san.png"
expires = int(time.time() + 60)
privkey_file = "pk-APKAJ4YNPASFVCNBXK3A-cloudfront.pem"
key_pair_id = "APKAJ4YNPASFVCNBXK3A"

policy = '{"Statement":[{"Resource":"%s","Condition":{"DateLessThan":{"AWS:EpochTime":%d}}}]}' % (url, expires)
privkey = keyfactory.parsePrivateKey(open(privkey_file).read())
print '%s?Expires=%d&Signature=%s&Key-Pair-Id=%s' % (url, expires, b64encode(privkey.hashAndSign(policy)), key_pair_id)

これでできた!!!はずです!!!!このあたりの調査に数日かけたので、感嘆符が多くなるのは仕方ないです!!!!!


boto が駄目だった理由

boto で一応 CloudFront を使えそうだったので、適当に書いてみたところ……

import boto

c = boto.connect_cloudfront()
dist = c.get_all_distributions()[0].get_distribution()
dist.create_signed_url("http://d3iwlid5dsmdb3.cloudfront.net/apple-touch-icon.png", "APKAJ4YNPASFVCNBXK3A", expire_time=time.time() + 60, private_key_file="./pk-APKAJ4YNPASFVCNBXK3A-cloudfront.pem")

すると、以下のエラーが。


NotImplementedError: Boto depends on the python M2Crypto library to generate signed URLs for CloudFront


M2Crypto が要るとのこと。インストールしてみる。

pip install M2crypto

(略)
error: command 'swig' failed with exit status 1

swig コマンドが無くてインストールできないとのことです。そこまでして M2Crypto に頼るのは嫌だなぁ、ということで、紆余曲折を経て tlslite にたどり着きました。boto が M2Crypto を使うことになった経緯はこちら

インストールのしやすさ以外だと M2Crypto と tlslite どっちが優れてるんだろう?


調査にあたって参考にしたサイト