その通りです。この設定を行うには、多くの API 作業が必要です。すぐに AWS コンソールで入手できることを願っています!
更新: このコードを boto に提出しました - boto v2.1 (2011 年 10 月 27 日リリース) の時点で、これははるかに簡単になりました。boto < 2.1 の場合は、こちらの手順を使用してください。boto 2.1 以降については、私のブログで更新された手順を入手して ください: http://www.secretmike.com/2011/10/aws-cloudfront-secure-streaming.htmlここで答えを更新します。
目的を達成するには、以下で詳しく説明する次の手順を実行する必要があります。
- s3 バケットを作成し、いくつかのオブジェクトをアップロードします (これは既に完了しています)。
- Cloudfront の「オリジン アクセス ID」を作成します (基本的には、Cloudfront が s3 バケットにアクセスできるようにするための AWS アカウント)。
- オブジェクトの ACL を変更して、Cloudfront オリジン アクセス ID のみが読み取りを許可されるようにします (これにより、人々が Cloudfront をバイパスして s3 に直接アクセスするのを防ぎます)。
- 基本 URL と署名付き URL を必要とするクラウドフロント ディストリビューションを作成する
- 基本的なクラウドフロント ディストリビューションからはオブジェクトをダウンロードできるが、s3 または署名付きクラウドフロント ディストリビューションからはダウンロードできないことをテストする
- URL に署名するための鍵ペアを作成する
- Python を使用していくつかの URL を生成する
- 署名付き URL が機能することをテストする
1 - バケットを作成してオブジェクトをアップロードする
これを行う最も簡単な方法は AWS コンソールを使用することですが、完全を期すために boto の使用方法を示します。Boto コードを次に示します。
import boto
#credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
s3 = boto.connect_s3()
#bucket name MUST follow dns guidelines
new_bucket_name = "stream.example.com"
bucket = s3.create_bucket(new_bucket_name)
object_name = "video.mp4"
key = bucket.new_key(object_name)
key.set_contents_from_filename(object_name)
2 - Cloudfront の「オリジン アクセス ID」を作成する
現時点では、このステップは API を使用してのみ実行できます。Boto コードは次のとおりです。
import boto
#credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
cf = boto.connect_cloudfront()
oai = cf.create_origin_access_identity(comment='New identity for secure videos')
#We need the following two values for later steps:
print("Origin Access Identity ID: %s" % oai.id)
print("Origin Access Identity S3CanonicalUserId: %s" % oai.s3_user_id)
3 - オブジェクトの ACL を変更する
特別な S3 ユーザー アカウント (上で作成した S3CanonicalUserId) を取得したので、s3 オブジェクトへのアクセスを許可する必要があります。オブジェクトの (バケットではなく!) 権限タブを開き、[権限を追加] ボタンをクリックして、上記で取得した非常に長い S3CanonicalUserId を新規の [Grantee] フィールドに貼り付けることで、AWS コンソールを使用して簡単にこれを行うことができます。新しいアクセス許可に「開く/ダウンロード」権限を付与していることを確認してください。
次の boto スクリプトを使用してコードでこれを行うこともできます。
import boto
#credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
s3 = boto.connect_s3()
bucket_name = "stream.example.com"
bucket = s3.get_bucket(bucket_name)
object_name = "video.mp4"
key = bucket.get_key(object_name)
#Now add read permission to our new s3 account
s3_canonical_user_id = "<your S3CanonicalUserID from above>"
key.add_user_grant("READ", s3_canonical_user_id)
4 - クラウドフロント ディストリビューションを作成する
カスタム オリジンとプライベート ディストリビューションは、執筆時点で正式にリリースされていないバージョン 2.0 まで、boto で完全にサポートされていないことに注意してください。以下のコードは、boto 2.0 ブランチからいくつかのコードを抜き出し、それをハックして実行していますが、見栄えがよくありません。2.0 ブランチはこれをよりエレガントに処理します - 可能であれば絶対に使用してください!
import boto
from boto.cloudfront.distribution import DistributionConfig
from boto.cloudfront.exception import CloudFrontServerError
import re
def get_domain_from_xml(xml):
results = re.findall("<DomainName>([^<]+)</DomainName>", xml)
return results[0]
#custom class to hack this until boto v2.0 is released
class HackedStreamingDistributionConfig(DistributionConfig):
def __init__(self, connection=None, origin='', enabled=False,
caller_reference='', cnames=None, comment='',
trusted_signers=None):
DistributionConfig.__init__(self, connection=connection,
origin=origin, enabled=enabled,
caller_reference=caller_reference,
cnames=cnames, comment=comment,
trusted_signers=trusted_signers)
#override the to_xml() function
def to_xml(self):
s = '<?xml version="1.0" encoding="UTF-8"?>\n'
s += '<StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">\n'
s += ' <S3Origin>\n'
s += ' <DNSName>%s</DNSName>\n' % self.origin
if self.origin_access_identity:
val = self.origin_access_identity
s += ' <OriginAccessIdentity>origin-access-identity/cloudfront/%s</OriginAccessIdentity>\n' % val
s += ' </S3Origin>\n'
s += ' <CallerReference>%s</CallerReference>\n' % self.caller_reference
for cname in self.cnames:
s += ' <CNAME>%s</CNAME>\n' % cname
if self.comment:
s += ' <Comment>%s</Comment>\n' % self.comment
s += ' <Enabled>'
if self.enabled:
s += 'true'
else:
s += 'false'
s += '</Enabled>\n'
if self.trusted_signers:
s += '<TrustedSigners>\n'
for signer in self.trusted_signers:
if signer == 'Self':
s += ' <Self/>\n'
else:
s += ' <AwsAccountNumber>%s</AwsAccountNumber>\n' % signer
s += '</TrustedSigners>\n'
if self.logging:
s += '<Logging>\n'
s += ' <Bucket>%s</Bucket>\n' % self.logging.bucket
s += ' <Prefix>%s</Prefix>\n' % self.logging.prefix
s += '</Logging>\n'
s += '</StreamingDistributionConfig>\n'
return s
def create(self):
response = self.connection.make_request('POST',
'/%s/%s' % ("2010-11-01", "streaming-distribution"),
{'Content-Type' : 'text/xml'},
data=self.to_xml())
body = response.read()
if response.status == 201:
return body
else:
raise CloudFrontServerError(response.status, response.reason, body)
cf = boto.connect_cloudfront()
s3_dns_name = "stream.example.com.s3.amazonaws.com"
comment = "example streaming distribution"
oai = "<OAI ID from step 2 above like E23KRHS6GDUF5L>"
#Create a distribution that does NOT need signed URLS
hsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True)
hsd.origin_access_identity = oai
basic_dist = hsd.create()
print("Distribution with basic URLs: %s" % get_domain_from_xml(basic_dist))
#Create a distribution that DOES need signed URLS
hsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True)
hsd.origin_access_identity = oai
#Add some required signers (Self means your own account)
hsd.trusted_signers = ['Self']
signed_dist = hsd.create()
print("Distribution with signed URLs: %s" % get_domain_from_xml(signed_dist))
5 - オブジェクトを cloudfront からダウンロードできるが s3 からはダウンロードできないことをテストする
これで、次のことを確認できるはずです。
- stream.example.com.s3.amazonaws.com/video.mp4 - AccessDenied を与える必要があります
- signed_distribution.cloudfront.net/video.mp4 - MissingKey を与える必要があります (URL が署名されていないため)
- basic_distribution.cloudfront.net/video.mp4 - 正常に動作するはずです
ストリーム プレーヤーで動作するようにテストを調整する必要がありますが、基本的な考え方は、基本的な cloudfront URL のみが動作するということです。
6 - CloudFront のキーペアを作成する
これを行う唯一の方法は、Amazon の Web サイトを使用することだと思います。AWS の「アカウント」ページに移動し、「セキュリティ認証情報」リンクをクリックします。[キー ペア] タブをクリックし、[新しいキー ペアの作成] をクリックします。これにより、新しいキー ペアが生成され、秘密キー ファイル (pk-xxxxxxxxx.pem) が自動的にダウンロードされます。キー ファイルは安全かつプライベートに保管してください。また、次のステップで必要になるため、Amazon からの「キー ペア ID」を書き留めます。
7 - Python でいくつかの URL を生成する
boto バージョン 2.0 の時点では、署名付き CloudFront URL の生成はサポートされていないようです。Python の標準ライブラリには RSA 暗号化ルーチンが含まれていないため、追加のライブラリを使用する必要があります。この例では M2Crypto を使用しました。
非ストリーミング配信の場合は、完全なクラウドフロント URL をリソースとして使用する必要がありますが、ストリーミングの場合はビデオ ファイルのオブジェクト名のみを使用します。5 分間しか持続しない URL を生成する完全な例については、以下のコードを参照してください。
このコードは、Amazon が CloudFront ドキュメントで提供している PHP サンプルコードに大まかに基づいています。
from M2Crypto import EVP
import base64
import time
def aws_url_base64_encode(msg):
msg_base64 = base64.b64encode(msg)
msg_base64 = msg_base64.replace('+', '-')
msg_base64 = msg_base64.replace('=', '_')
msg_base64 = msg_base64.replace('/', '~')
return msg_base64
def sign_string(message, priv_key_string):
key = EVP.load_key_string(priv_key_string)
key.reset_context(md='sha1')
key.sign_init()
key.sign_update(str(message))
signature = key.sign_final()
return signature
def create_url(url, encoded_signature, key_pair_id, expires):
signed_url = "%(url)s?Expires=%(expires)s&Signature=%(encoded_signature)s&Key-Pair-Id=%(key_pair_id)s" % {
'url':url,
'expires':expires,
'encoded_signature':encoded_signature,
'key_pair_id':key_pair_id,
}
return signed_url
def get_canned_policy_url(url, priv_key_string, key_pair_id, expires):
#we manually construct this policy string to ensure formatting matches signature
canned_policy = '{"Statement":[{"Resource":"%(url)s","Condition":{"DateLessThan":{"AWS:EpochTime":%(expires)s}}}]}' % {'url':url, 'expires':expires}
#now base64 encode it (must be URL safe)
encoded_policy = aws_url_base64_encode(canned_policy)
#sign the non-encoded policy
signature = sign_string(canned_policy, priv_key_string)
#now base64 encode the signature (URL safe as well)
encoded_signature = aws_url_base64_encode(signature)
#combine these into a full url
signed_url = create_url(url, encoded_signature, key_pair_id, expires);
return signed_url
def encode_query_param(resource):
enc = resource
enc = enc.replace('?', '%3F')
enc = enc.replace('=', '%3D')
enc = enc.replace('&', '%26')
return enc
#Set parameters for URL
key_pair_id = "APKAIAZCZRKVIO4BQ" #from the AWS accounts page
priv_key_file = "cloudfront-pk.pem" #your private keypair file
resource = 'video.mp4' #your resource (just object name for streaming videos)
expires = int(time.time()) + 300 #5 min
#Create the signed URL
priv_key_string = open(priv_key_file).read()
signed_url = get_canned_policy_url(resource, priv_key_string, key_pair_id, expires)
#Flash player doesn't like query params so encode them
enc_url = encode_query_param(signed_url)
print(enc_url)
8 - URL を試す
うまくいけば、次のような URL が機能するはずです。
video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQ
これを js に入れると、次のようになります (Amazon の CloudFront ドキュメントの PHP の例から)。
var so_canned = new SWFObject('http://location.domname.com/~jvngkhow/player.swf','mpl','640','360','9');
so_canned.addParam('allowfullscreen','true');
so_canned.addParam('allowscriptaccess','always');
so_canned.addParam('wmode','opaque');
so_canned.addVariable('file','video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQ');
so_canned.addVariable('streamer','rtmp://s3nzpoyjpct.cloudfront.net/cfx/st');
so_canned.write('canned');
概要
ご覧のとおり、それほど簡単ではありません。boto v2 は、ディストリビューションのセットアップに大いに役立ちます。このすばらしいライブラリを改善するために、そこに URL 生成コードを入れることができるかどうかを調べます!