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

この記事は、[AWS S3 + CloudFront で動画ストリーム](http://qiita.com/terapyon/items/b77ac981da790662e247 "AWS S3 + CloudFront で動画ストリーム")の続きです。

# 目的
期限付きURLでの接続を行う。(ワンタイムURL)
動画ストリームに閲覧制限し配信したい。(今回は画像で試した)
最終的にはPloneの認証を使って動画を配信したい。(次回以降にチャレンジ)

# S3 及び CloudFront の設定

新規にバケットを作り、お試しの画像を置く。この画像をプライベート配信する事を試す。

## S3

- 新規にバケットを作る (c2-video-test-private)
  * ログの設定は行う
- Upload
  * 画像ファイルをアップロード "terada_pythonbr.jpg"


## CloundFront

- CloudFront アクセスIDの取得
  * IAMのWeb管理コンソールへ行く
  * Details内の、 "CloudFront Key Pair" で、"Create New Key Pair"を取得
  * 秘密鍵と公開鍵をダウンロードする
- CloudFront Origin
CloudFrontのWeb管理画面で、"Private Content"から"Origin Access Identity"へ行く（あとで作る方法もある）
  * Create OAI をクリック
  * コメントに "OAI for c2-video-test-private" と入れて作成
- 新規に"Private Distribution"を作る (Web用）
- 設定を行う
  * Restrict Bucket Access: Yes
  * Origin Access Identity: Use an Existing Identity
  * Your Identities: を前もって作ったものを選択（またはここで新規に作ることも可能）
  * Grant Read Permissions on Bucket: Yes
  * HTTPS Only:Yes
  * Restrict Viewer Access: Yes
  * Trusted Signers
  * Logging: On
  * その他Loggingの設定（バケットとPath)
- Domain Name: dn045wcqv2fv4.cloudfront.net となった


## Signed URLを作る
以下の botoの項目を参照しローカルのPython環境で作ってみる。


### 制限なしURLは、以下となるはず。
https://dn045wcqv2fv4.cloudfront.net/terada_pythonbr.jpg
当然ですが、エラーが帰ってきました。

```error.xml
<Error>
  <Code>MissingKey</Code>
  <Message>Missing Key-Pair-Id query parameter</Message>
</Error>
```

### 制限付きURLを作る
(boto関係の詳細は下部に記載）

```sample.py
ACCESS_KEY_ID = "xxxxxxxxxxxxxxxxx"
SECRET_ACCESS_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxx"
YOUR_KEYPAIR_ID = "xxxxxxxxxxxxxx"
YOUR_PRIVATE_KEY_FILE_LOCATION = "xxxxxxxxxxxxxxxxxxxxx.pem"
url = "https://dn045wcqv2fv4.cloudfront.net/terada_pythonbr.jpg"
expire_time = int(time.time() +3600)

conn = CloudFrontConnection(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
distribution = Distribution(connection=conn, config=None, domain_name='', id='', last_modified_time=None, status='')

signed_url = distribution.create_signed_url(url=url, keypair_id=YOUR_KEYPAIR_ID, expire_time=expire_time, private_key_file=YOUR_PRIVATE_KEY_FILE_LOCATION)
```

```
>>> print signed_url
https://dn045wcqv2fv4.cloudfront.net/terada_pythonbr.jpg?Expires=1406023941&Signature=Ihps3u0~jQ-Twkghg2tqQ-dTMEZoRC-Jb13F4FgNe~1MVAsXQ-yAbXtEKSTsIhN6OLZmuUii6E1xs7nWfe-76SGk26Jmx5edd35EgwQ8mH~2XlY4QOqP1Fgza93q02oQNhJWte1mef4tc8p3fJO6yzebpTEh4MqvQOt9s5bcKq9ad8EGSmfXfUlgL6b87z6TOAXVFQuIotttu6ajlfbCpplIUD-~r-6W~SB6n2dyB8SaoKgJBTkEKJwFiBWx2S5M20-06hsLOeV23UBJcuq6~C-pE1r1ViE8Ia-tM8L6v7zcbtLfOdw9lgFzYoMoh-KiWOAdz7pc-koYMVPXZ-9DuQ__&Key-Pair-Id=APKAJAJZQHPKZI6OK5UQ
```

### ブラウザでアクセステスト

https://dn045wcqv2fv4.cloudfront.net/terada_pythonbr.jpg?Expires=1406023941&Signature=Ihps3u0~jQ-Twkghg2tqQ-dTMEZoRC-Jb13F4FgNe~1MVAsXQ-yAbXtEKSTsIhN6OLZmuUii6E1xs7nWfe-76SGk26Jmx5edd35EgwQ8mH~2XlY4QOqP1Fgza93q02oQNhJWte1mef4tc8p3fJO6yzebpTEh4MqvQOt9s5bcKq9ad8EGSmfXfUlgL6b87z6TOAXVFQuIotttu6ajlfbCpplIUD-~r-6W~SB6n2dyB8SaoKgJBTkEKJwFiBWx2S5M20-06hsLOeV23UBJcuq6~C-pE1r1ViE8Ia-tM8L6v7zcbtLfOdw9lgFzYoMoh-KiWOAdz7pc-koYMVPXZ-9DuQ__&Key-Pair-Id=APKAJAJZQHPKZI6OK5UQ

2014/07/22 18:14にアクセスできた。
2014/07/22 19:15になったらアクセス出来ないはず


# boto

[boto](https://pypi.python.org/pypi/boto "boto") は、AWSのAPIを操作するPythonモジュールです。以前はAmazon非公式でしたが、いまは開発者がAmazon所属になったはずなので、半公式なはずです。

## 事前準備

```
$ pip install rsa
$ pip install boto
```

## Signed URLの取得方法 
説明が以下にありました。
http://boto.readthedocs.org/en/latest/ref/cloudfront.html

```
create_signed_url(url, keypair_id, expire_time=None, valid_after_time=None, ip_address=None, policy_url=None, private_key_file=None, private_key_string=None)
```

- url (str) 
- keypair_id (str) 
- expire_time (int) 
- valid_after_time (int)
- ip_address (str)
- policy_url (str)
- private_key_file (str or file object.) 
- private_key_string (str) 

以下にサンプルがあった

http://stackoverflow.com/questions/2573919/creating-signed-urls-for-amazon-cloudfront

```
import boto
from boto.cloudfront import CloudFrontConnection
from boto.cloudfront.distribution import Distribution

expire_time = int(time.time() +3000)
conn = CloudFrontConnection('ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')

##enter the id or domain name to select a distribution
distribution = Distribution(connection=conn, config=None, domain_name='', id='', last_modified_time=None, status='')
signed_url = distribution.create_signed_url(url='YOUR_URL', keypair_id='YOUR_KEYPAIR_ID_example-APKAIAZVIO4BQ',expire_time=expire_time,private_key_file="YOUR_PRIVATE_KEY_FILE_LOCATION")
```



# 調査時に参照したURL

- [AWSマイスターシリーズ] Amazon CloudFront / Amazon Elastic Transcoderによるコンテンツ配信
http://www.slideshare.net/AmazonWebServicesJapan/20131120-aws-medisterregeneratecloudfrontetspublic
P29に説明がある

- Amazon CloudFront でプライベートコンテンツにアクセス その1
http://dev.classmethod.jp/etc/amazon-cloudfront-private-contents-access-1/
AWS管理コンソールに英語でマニュアルが有り、現状では、Web管理コンソールから全ての操作が出来そう。

- Code and Examples for Creating a Signature for a Signed URL
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateCFSignatureCodeAndExamples.html
Pythonの情報が無いけど参考になりそう


# 動画で試してみる
TODOにしていた、動画ストリームに上記を対応させる

## 準備

- c2-video-test-private バケットに、videoフォルダを追加
- PyConAPACTW2014terada.mp4 を videoフォルダに追加
- CloudFrontにストリーム対応のDistributionを追加
  * Create時に、RTMPを選択
  * Origin Domain Name : c2-video-test-private
  * Restrict Bucket Access: Yes
  * Origin Access Identity: Use an Existing Identity
  * Your Identities: を前もって作ったものを選択  (OAI for c2-video-test-private)
  * Grant Read Permissions on Bucket: Yes
  * Restrict Viewer Access: Yes
  * Trusted Signers: Self
  * Logging: On
  * その他Loggingの設定（バケットとPath)

ここまでは、今までのおさらいで簡単に出来るはず。

## Signed URLを作る
  上記同様に、botoを使って作業
  
- url = "rtmp://s3lse3ja7xuop9.cloudfront.net/cfx/st/&mp4:video/PyConAPACTW2014terada.mp4"
- policy_url = "video/*"
  ワイルドカードを指定している。 **rtmpの時は** 上記のようにPATHだけを書く(rtpm://から書くとうまくいかない＜これで数日ハマった)

```
>>> url = "rtmp://s3lse3ja7xuop9.cloudfront.net/cfx/st/&mp4:video/PyConAPACTW2014terada.mp4"
>>> policy_url = "video/*"
>>> signed_url = distribution.create_signed_url(url=url, keypair_id=YOUR_KEYPAIR_ID, expire_time=expire_time, private_key_file=YOUR_PRIVATE_KEY_FILE_LOCATION, policy_url=policy_url)
>>> print signed_url
rtmp://s3lse3ja7xuop9.cloudfront.net/cfx/st/&mp4:video/PyConAPACTW2014terada.mp4?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoidmlkZW8vKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTQwNjg5OTIwMX19fV19&Signature=m10lfH4vpepAWVNLCGO9xr~TFs-ZCUklra-I4D6WZvqO3aHhijIbtPQOiK00BPZzTxnu-enkvTuao1aDoP~HSONzF5xIqGqMuP4RYJ-B0~g5iQxvac1VVkb~3lZXMRN3Oz45BsrRWQawyXp1o~0M9sFr~zIVQAbM8aAji1WmTu0~fY0UcfgO74DNevYw4-I4S8S2KLnSimFGeU0bz3b8bXtgxketPz0JhTyM4akK8gD7xWjskrxFOZ4pskCaLvJr3Pyb9pJsqQ9T2izNsPs0Ms5pi94FnOdyejSRWezqrxp00KNttnlc3DGCOcmcCNDNYdIMxmNC6MuAfQKx~a1tig__&Key-Pair-Id=APKAJAJZQHPKZI6OK5UQ
```


## 動画表示用のテンプレート

srcの部分をSigned URLにする。

```html
<source src="rtmp://s3lse3ja7xuop9.cloudfront.net/cfx/st/&mp4:video/PyConAPACTW2014terada.mp4?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoidmlkZW8vKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTQwNjg5OTIwMX19fV19&Signature=m10lfH4vpepAWVNLCGO9xr~TFs-ZCUklra-I4D6WZvqO3aHhijIbtPQOiK00BPZzTxnu-enkvTuao1aDoP~HSONzF5xIqGqMuP4RYJ-B0~g5iQxvac1VVkb~3lZXMRN3Oz45BsrRWQawyXp1o~0M9sFr~zIVQAbM8aAji1WmTu0~fY0UcfgO74DNevYw4-I4S8S2KLnSimFGeU0bz3b8bXtgxketPz0JhTyM4akK8gD7xWjskrxFOZ4pskCaLvJr3Pyb9pJsqQ9T2izNsPs0Ms5pi94FnOdyejSRWezqrxp00KNttnlc3DGCOcmcCNDNYdIMxmNC6MuAfQKx~a1tig__&Key-Pair-Id=APKAJAJZQHPKZI6OK5UQ"
                 type='rtmp/mp4' />
```

## テストページ

すでに、閲覧期限が過ぎていると思うが、以下で見れることを確認
http://c2-video-test.s3-website-ap-northeast-1.amazonaws.com/index2.html



# 次回以降のTODO

- 動画ストリームに閲覧制限し配信したい。 -> 20140801 上記の「動画で試してみる」項目に記載。
- iOS用HLSの時ってどうなるのか調査 -> http://qiita.com/terapyon/items/fadecf13ed1d11dcd34d
- 最終的にはPloneの認証を使って動画を配信したい。

# 著者関連Blog記事

[AWS CloudFront でプライベートコンテンツ配信](https://www.cmscom.jp/blog/hz2f2t)