LoginSignup
8
9

More than 5 years have passed since last update.

Pythonを使ってCloudFrontのサイン付きURLを作る

Posted at

前提

当方では、Ploneを用いて動画(Video)をストリーミング再生させるプロダクトを公開しています。その際に、Ploneのユーザ情報(ログインユーザ情報)を元に、閲覧の制限をさせたいと考えました。ストリーミング動画のアクセス管理は非常に手間のかかることですが、AWS CloudFrontのサイン付きURL(signed url)機能を使うことで実現できます。

Ploneとの組合せで使用する際には、 c2.app.streamingaws を用いることで、ソースコードを書かずにセキュアに動画配信が可能です。

ここでは、 Python + boto を使って、サイン付きURLの生成方法及びVideoJSを用いたHTMLの書き方をまとめます。

必要な条件・情報

  • Python 2.7 or 3.4 (botoに依存/他のバージョンでも可能かもしれないが試していない) (私は 2.7系のみで試した)
  • boto (使用したのは、boto 2.36.0 (現状の最終バージョン) / 以前のバージョンでも機能すると思う)
  • AWS アカウント (IAMユーザでも可能 / 適切な権限付は必要)
  • CloudFront keyペア
  • S3 及び CloudFront の設定 (別のエントリーを見て下さい)
  • URL有効時間の決定 (ストリーミング動画再生の場合、動画の時間以上を設定する必要がある)

ここまで準備、決まったら実装するのみ。

サイン付きURLの生成

create_url.py
import time
from boto.cloudfront import CloudFrontConnection
from boto.cloudfront.distribution import Distribution

access_key_id = "YOUR_KEY_ID" # AWS (or IAM) アカウントのID
secret_access_key = "YOUR_SECRET_KEY" # AWS (or IAM) アカウントのシークレットキー
cloudfront_key_id = "CLOUD_FRONT_KEY_ID" # CloudFront のID
cloudfront_private_key_str = """XXXXXXXXXXXXX
""" # CloudFront の 秘密鍵 (-----BEGIN RSA PRIVATE KEY-----を含めて)

expire_time = int(time.time() + 3600) # 有効時間を3600秒 (1時間) に設定

conn = CloudFrontConnection(access_key_id, secret_access_key)
distribution = Distribution(connection=conn)

def get_signed_url(url, policy_url):
    signed_url = distribution.create_signed_url(url=url,
                            keypair_id=cloudfront_key_id,
                            expire_time=expire_time,
                            policy_url=policy_url,
                            private_key_string=cloudfront_private_key_str)
    return signed_url

if __name__ == '__main__:
    url = ""
    policy_url = ""
    print get_signed_url(url, policy_url)

VideoJSでの表示

video.js を用いる場合、以下のように video タグ や source タグ を記述します。video.js でのHTMLの記述方法は以下のエントリーを参照してください。

画像の場合

create_signed_url_for_image.py
form .create_url import get_signed_url
domain = "xxxx.cloudfront.net" # ClundFront の HTTP(S) の Domain
base_path = "video/" # 画像が保管されているS3上のPATH
img_id = "thumb.png" # 画像のファイル名
url = "https://" + domain + "/" + base_path + img_id
policy_url = "https://" + domain + "/" + base_path + "*"
signed_url = get_signed_url(url, policy_url)
video.html
<video id="video_1" class="video-js vjs-default-skin"
      controls preload="auto" width="640" height="360"
      poster="${signed_url}" <!-- 上記のsigned_urlを動的にはめる -->
      data-setup='{}'>
    <source src="" type="" />
</video>

RTMPの場合

create_signed_url_for_rtmp.py
form .create_url import get_signed_url
domain = "xxxx2.cloudfront.net" # ClundFront の RTMP の Domain
base_path = "video/" # 動画が保管されているS3上のPATH
video_id = "video.mp3" # 動画のファイル名
url = "rtmp://" + domain + "/cfx/st/&mp4:" + base_path + video_id
policy_path = "base_path + video_id # RTMPの場合はドメイン等を含まない
signed_url = get_signed_url(url, policy_path)

policy_path には、 rtmp://を含むドメイン 等は含めない。

video.html
<source src="${signed_url}"
        type='rtmp/mp4' />

HLSの場合

HLSの場合は、 m3u8 というメタデータファイルをCloudFront経由で取得し、メタデータファイルを動的に書きなおしたものを、HTTP(S)通信で渡す必要があります。

まずは、この m3u8 ファイルを自身のサーバにリクエストが来るようにHTMLを記載します。

video.html
<source src="http://MY_SERVER_DOMAIN/${signed_url}"
        type="application/x-mpegURL" />

次に、 m3u8 を動的に生成する必要があります。

m3u8ファイルには、分割された動画ファイルのURL(m3u8と同じ階層ならファイル名のみ)が列挙されています。それらの動画ファイルのURLを、サイン付きURLに書き換えます。
m3u8ファイルはシンプルなテキストファイルなので、書き換えは容易です。以下のようにファイルを書き換え、何かしらで自身のサーバからHTTP(S)通信で提供します。

m3u8ファイルを取得するための signed_url の取得。

create_signed_url_for_hls.py
form .create_url import get_signed_url
domain = "xxxx.cloudfront.net" # ClundFront の HTTP(S) の Domain
base_path = "video/" # 動画が保管されているS3上のPATH
meta_id = "index.m3u8" # 動画メタデータのファイル名
url = "https://" + domain + "/" + base_path + meta_id
policy_url = "https://" + domain + "/" + base_path + "*"
signed_url = get_signed_url(url, policy_url)

取得した、 signed_url から、m3u8ファイルデータを取得し、以下の様なスクリプトで、データ内部を書き換え、HTTP(S)通信で提供します。

zope_server.py
import urllib2

def _adding_param_for_m3u8(data, base_url, signed_param):
    for li in data.split("\n"):
        if not li.strip():
            yield li
        elif li.startswith("#"):
            yield li
        else:
            yield base_url + "/" + li + "?" + signed_param

class View:
    def render(self, signed_url):
        """
        """
        sp_signed_url = signed_url.split("?", 1)
        if len(sp_signed_url) < 2:
            return None
        base_url = sp_signed_url[0].rsplit("/", 1)[0]
        signed_param = sp_signed_url[1]
        m3u8_data = urllib2.urlopen(signed_url)
        data_list = _adding_param_for_m3u8(m3u8_data.read(),
                                           base_url, signed_param)
        m3u8_data.close()
        self.request.RESPONSE.setHeader('Content-Type', 'application/x-mpegURL')
        return "\n".join(data_list)

関連コンテンツ

最後に

このエントリーは、Ploneに実装したプロダクトから、サイン付きURLを作る部分を抜き出し、VideoJSで表示させる部分の解説をしました。

部分的に記載ミスが残っている可能性があります。ミスについてはご指摘いただけたら幸いです。

著者関連Blog記事とあわせてお読みください。

AWS CloudFront でプライベートコンテンツ配信

8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9