CloudFront x S3 => 期限付き URL
facebook や flickr などのように、一部ユーザにだけ画像を見せたいというケース、ありますよね。こういうサービスでは、画像に対して期限付きの URL を発行するのが一般的のようです。Amazon CloudFront と Amazon S3 を使うと、こんな形の期限付き URL を発行できます。
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 どっちが優れてるんだろう?