41

S3 バケットを作成し、動画をアップロードし、CloudFront でストリーミング配信を作成しました。静的 HTML プレーヤーでテストしたところ、動作しました。アカウント設定からキーペアを作成しました。現在、秘密鍵ファイルをデスクトップに置いています。それが私がいるところです。

私の目標は、私の Django/Python サイトが安全な URL を作成し、私のページのいずれかから来ていない限り、人々がビデオにアクセスできないようにすることです。問題は、私が Amazon のやり方にアレルギーを持っていて、ますます混乱していることです。

これが StackOverflow に関する最良の質問ではないことは承知していますが、CloudFront/S3 の安全な状況をセットアップする方法について頭も尻尾もつかめない愚か者は私だけではないはずです。私はあなたの助けに本当に感謝しており、(2 日が経過したら)最良の回答に 500 ポイントの報奨金を差し上げます。

いくつかの質問がありますが、一度答えれば、私が求めていることを達成する方法の 1 つの説明に収まるはずです。

  • ドキュメント (次のポイントに例があります) には、さまざまな場所に移動する必要があることを伝える XML がたくさんありますPOST。これを行うためのオンラインコンソールはありますか? それとも、文字通り cURL (et al) を介してこれを強制する必要がありますか?

  • CloudFront のオリジン アクセス ID を作成してディストリビューションにバインドするにはどうすればよいですか? 私はこのドキュメントを読みましたが、最初の点について、どうすればよいかわかりません。私のキーペアはこれにどのように適合しますか?

  • それが完了したら、S3 バケットを制限して、ユーザーがその ID を介してのみダウンロードできるようにするにはどうすればよいですか? これが Web UI をクリックするのではなく別の XML ジョブである場合は、どこでどのようにこれをアカウントに取り込むべきか教えてください。

  • Python で、ファイルの有効期限が切れる URL を生成する最も簡単な方法は何ですか? インストールしましたがboto、ストリーミング配信からファイルを取得する方法がわかりません。

  • この服装を設定するのが難しいアプリケーションやスクリプトはありますか? Ubuntu (Linux) を使用していますが、VM が Windows のみの場合は XP を使用しています。私はすでに CloudBerry S3 Explorer Pro を見てきましたが、オンライン UI と同じくらい理にかなっています。

4

2 に答える 2

53

その通りです。この設定を行うには、多くの 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ここで答えを更新します。

目的を達成するには、以下で詳しく説明する次の手順を実行する必要があります。

  1. s3 バケットを作成し、いくつかのオブジェクトをアップロードします (これは既に完了しています)。
  2. Cloudfront の「オリジン アクセス ID」を作成します (基本的には、Cloudfront が s3 バケットにアクセスできるようにするための AWS アカウント)。
  3. オブジェクトの ACL を変更して、Cloudfront オリジン アクセス ID のみが読み取りを許可されるようにします (これにより、人々が Cloudfront をバイパスして s3 に直接アクセスするのを防ぎます)。
  4. 基本 URL と署名付き URL を必要とするクラウドフロント ディストリビューションを作成する
  5. 基本的なクラウドフロント ディストリビューションからはオブジェクトをダウンロードできるが、s3 または署名付きクラウドフロント ディストリビューションからはダウンロードできないことをテストする
  6. URL に署名するための鍵ペアを作成する
  7. Python を使用していくつかの URL を生成する
  8. 署名付き 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 生成コードを入れることができるかどうかを調べます!

于 2011-07-06T02:38:46.307 に答える
3

Python で、ファイルの有効期限が切れる URL を生成する最も簡単な方法は何ですか? boto をインストールしましたが、ストリーミング配信からファイルを取得する方法がわかりません。

リソースの期限切れの署名付き URL を生成できます。Boto3 のドキュメントには、そのための優れたソリューションの例があります。

import datetime

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from botocore.signers import CloudFrontSigner


def rsa_signer(message):
    with open('path/to/key.pem', 'rb') as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(), 
            password=None,
            backend=default_backend()
        )
    signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
    signer.update(message)
    return signer.finalize()

key_id = 'AKIAIOSFODNN7EXAMPLE'
url = 'http://d2949o5mkkp72v.cloudfront.net/hello.txt'
expire_date = datetime.datetime(2017, 1, 1)

cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)

# Create a signed url that will be valid until the specfic expiry date
# provided using a canned policy.
signed_url = cloudfront_signer.generate_presigned_url(
    url, date_less_than=expire_date)
print(signed_url)
于 2016-03-15T12:11:22.457 に答える