首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >开始使用使用Python的安全AWS CloudFront流

开始使用使用Python的安全AWS CloudFront流
EN

Stack Overflow用户
提问于 2011-07-01 15:10:28
回答 2查看 17.8K关注 0票数 43

我创建了一个S3桶,上传了一个视频,在CloudFront中创建了一个流分发。用一个静态HTML播放器测试它,它可以工作。我已经通过帐户设置创建了一个键盘。我把私钥文件放在我的桌面上。我就在那儿

我的目标是达到这样的程度:我的Django/Python站点创建了安全的URL,人们无法访问这些视频,除非他们来自我的某个页面。问题是我对亚马逊的布局方式过敏,我只是越来越困惑。

我意识到这并不是StackOverflow上最好的问题,但我确信,我不能成为这里唯一的傻瓜,在如何设置安全的CloudFront/S3的问题上,我不能自作主张。我真的很感谢你的帮助,我愿意(两天过去了)对最好的答案给予500英镑的赏金。

我有几个问题,一旦得到回答,就应该融入一个关于如何完成我所追求的目标的解释:

  • 在文档中(下一点有一个例子),到处都是XML,告诉我需要在不同的地方使用POST。是否有在线控制台进行此操作?或者我真的需要通过cURL (等人)来强迫这一点吗?
  • 如何为CloudFront创建一个原产地访问标识并将其绑定到我的发行版?我读过本文件,但根据第一点,我不知道该如何处理它。我的键盘是怎么进去的?
  • 一旦这样做了,我如何限制S3桶只允许人们通过该身份下载东西?如果这是另一个XML作业(而不是点击web ),请告诉我应该在哪里和如何将它输入我的帐户。
  • 在Python中,为文件生成过期URL的最简单方法是什么。我已经安装了boto,但是我不知道如何从流发行版中获取文件。
  • 是否有任何应用程序或脚本可以承担设置这件衣服的困难?我使用Ubuntu (Linux),但如果只使用Windows,则在VM中有XP。我已经看过CloudBerry S3资源管理器Pro了,但是它和在线UI一样有意义。
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-07-06 02:38:46

你说得对,需要大量的API工作才能完成这个设置。我希望他们很快就能拿到AWS控制台!

更新:我已经向boto提交了这段代码--从botov2.1(发布于2011-10-27)开始,这就容易多了。对于boto < 2.1,请使用这里的说明。对于boto 2.1或更高版本,请在我的博客上获得更新的说明: http://www.secretmike.com/2011/10/aws-cloudfront-secure-streaming.html 一旦boto v2.1被更多的发行版打包,我将在这里更新答案。

为了完成您想要的,您需要执行以下步骤,我将在下面详细介绍:

  1. 创建您的s3桶并上传一些对象(您已经这样做了)
  2. 创建一个Cloudfront“原始访问标识”(基本上是一个允许cloudfront访问您的s3桶的AWS帐户)
  3. 修改对象上的ACL,以便只允许您的Cloudfront访问标识读取它们(这可以防止人们绕过Cloudfront直接到s3)
  4. 创建一个具有基本URL的cloudfront发行版,该发行版需要有签名的URL
  5. 测试可以从基本cloudfront发行版下载对象,但不能从s3或已签名的cloudfront发行版下载对象
  6. 创建用于签名URL的密钥对
  7. 使用Python生成一些URL
  8. 测试签名的URL是否有效

1 -创建桶和上传对象

最简单的方法是通过AWS控制台,但为了完整起见,我将展示如何使用boto。Boto代码如下所示:

代码语言:javascript
运行
复制
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“原产地访问标识”

目前,这个步骤只能使用API执行。Boto代码在这里:

代码语言:javascript
运行
复制
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对象。我们可以轻松地使用AWS控制台打开对象的(而不是桶的!)权限选项卡,单击“添加更多权限”按钮,并将上面获得的非常长的S3CanonicalUserId粘贴到新的“授权”字段中。确保您授予新的权限“打开/下载”权限。

您还可以使用以下boto脚本在代码中执行此操作:

代码语言:javascript
运行
复制
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 -创建云阵发行版

请注意,自定义源和私有发行版在boto中直到2.0版本才得到完全支持,而在编写时还没有正式发布。下面的代码从boto 2.0分支中提取一些代码,并将其合并起来以使其正常运行,但并不好看。2.0分支处理这个问题要优雅得多--如果可能的话,一定要使用它!

代码语言:javascript
运行
复制
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下载对象

现在您应该能够验证:

  • 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创建一个键盘

我认为唯一的办法是通过亚马逊的网站。进入AWS“帐户”页面,点击“安全凭据”链接。单击“密钥对”选项卡,然后单击“创建新密钥对”。这将为您生成一个新的密钥对,并自动下载一个私有密钥文件(pK-xxxxxxxx.pem)。确保密钥文件的安全和私密性。另外,请记下amazon的“密钥对ID”,因为我们在下一步需要它。

7 -在Python中生成一些URL

在BOTO2.0版本中,似乎没有任何对生成签名CloudFront URL的支持。Python不包括标准库中的RSA加密例程,因此我们必须使用额外的库。在本例中,我使用了M2Crypto。

对于非流式发行版,必须使用完整的cloudfront URL作为资源,但是对于流,我们只使用视频文件的对象名。有关生成URL的完整示例,请参见下面的代码,该URL仅持续5分钟。

这段代码松散地基于CloudFront文档中亚马逊提供的PHP示例代码。

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQ

将其放入js中,您应该有如下所示的内容(来自于亚马逊CloudFront文档中的PHP示例):

代码语言:javascript
运行
复制
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生成代码,以及改进这个伟大的库!

票数 53
EN

Stack Overflow用户

发布于 2016-03-15 12:11:22

在Python中,为文件生成过期URL的最简单方法是什么。我已经安装了boto,但我不知道如何从流发行版中获取文件。

您可以为资源生成一个到期的签名-URL。Boto3文档有一个良好的示例解

从cryptography.hazmat.backends导入default_backend从cryptography.hazmat.primitives导入散列从cryptography.hazmat.primitives导入序列化从cryptography.hazmat.primitives.asymmetric导入填充从botocore.signers导入CloudFrontSigner def rsa_signer(消息):打开(‘path/to/key.pem’,'rb')为key_file: private_key = serialization.load_pem_private_key( key_file.read(),password=None,backend=default_backend()签名者= private_key.signer(padding.PKCS1v15(),hashes.SHA1() signer.update(message)返回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) #创建一个签名的url,直到使用屏蔽策略提供的特定到期日期#为止。signed_url = cloudfront_signer.generate_presigned_url( url,date_less_than=expire_date)打印(Signed_url)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6549787

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档