简短版本:如何"按需"制作签名URL,以使用Python模拟Nginx的X-Accel-Redirect行为(即保护下载)与Amazon CloudFront/S3.
我有一个Django服务器,并运行Nginx前端.我一直受到了对它的请求的打击,最近不得不将其安装为Tornado WSGI应用程序,以防止它在FastCGI模式下崩溃.
现在我遇到了一个问题,我的服务器陷入困境(因为它的大部分带宽正在用完),因为有太多的媒体请求,我一直在寻找CDN,我相信Amazon CloudFront/S3对我来说是合适的解决方案.
我一直在使用Nginx的X-Accel-Redirect标头来保护文件免受未经授权的下载,但我没有使用CloudFront/S3的能力 - 但是它们确实提供了签名的URL.到目前为止,我不是Python专家,并且肯定不知道如何正确创建签名URL,所以我希望有人可以获得如何"按需"制作这些URL的链接,或者愿意解释如何在这里,我们将不胜感激.
而且,这是正确的解决方案吗?我不太熟悉CDN,是否有更适合这种情况的CDN?
Amazon CloudFront签名URL与Amazon S3 签名URL的工作方式不同.CloudFront使用基于单独CloudFront密钥对的RSA签名,您必须在Amazon帐户凭据页面中设置该密钥对.以下是使用M2Crypto库在Python中实际生成限时URL的一些代码:
为CloudFront创建密钥对
我认为唯一的方法是通过亚马逊的网站.进入您的AWS"帐户"页面,然后单击"安全凭据"链接.单击"密钥对"选项卡,然后单击"创建新密钥对".这将为您生成一个新的密钥对,并自动下载私钥文件(pk-xxxxxxxxx.pem).保持密钥文件安全和私密.另请注意亚马逊的"密钥对ID",因为我们将在下一步中使用它.
在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(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} #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 = "APKAIAZVIO4BQ" #from the AWS accounts CloudFront tab priv_key_file = "cloudfront-pk.pem" #your private keypair file # Use the FULL URL for non-streaming: resource = "http://34254534.cloudfront.net/video.mp4" #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) print(signed_url) #Flash player doesn't like query params so encode them if you're using a streaming distribution #enc_url = encode_query_param(signed_url) #print(enc_url)
确保使用设置为持有密钥对的帐户的TrustedSigners参数设置您的分配(如果是您自己的帐户,则设置为"Self")
请参阅使用Python开始使用安全的AWS CloudFront流式传输,以获得有关使用Python进行流式传输的完整工作示例
Botocore现已支持此功能,Botocore是最新的官方适用于Python的AWS SDK Boto3的底层库.(以下示例需要安装rsa软件包,但您也可以使用其他RSA软件包,只需定义自己的"规范化RSA签名者".)
用法如下:
from botocore.signers import CloudFrontSigner # First you create a cloudfront signer based on a normalized RSA signer:: import rsa def rsa_signer(message): private_key = open('private_key.pem', 'r').read() return rsa.sign( message, rsa.PrivateKey.load_pkcs1(private_key.encode('utf8')), 'SHA-1') # CloudFront requires SHA-1 hash cf_signer = CloudFrontSigner(key_id, rsa_signer) # To sign with a canned policy:: signed_url = cf_signer.generate_presigned_url( url, date_less_than=datetime(2015, 12, 1)) # To sign with a custom policy:: signed_url = cf_signer.generate_presigned_url(url, policy=my_policy)
免责声明:我是该公关的作者.
正如许多人已经评论过的那样,最初接受的答案实际上并不适用于Amazon CloudFront,因为通过CloudFront服务私有内容需要使用专用的CloudFront签名URL - 因此secretmike的答案是正确的,但同时他自己已经过时了花了很多时间并添加了为CloudFront生成签名URL的支持(非常感谢!).
boto现在支持专用的create_signed_url方法,前一个二进制依赖项M2Crypto最近也被纯Python RSA实现取代,请参阅不要使用M2Crypto进行云端URL签名.
随着越来越普遍,人们可以找到(请参阅相关的单元测试中的一个或更多的好用法示例test_signed_urls.py),例如test_canned_policy(个体经营) -见设置(个体经营)为引用的变量self.pk_id
和self.pk_str
(很明显,你需要自己键):
def test_canned_policy(self): """ Generate signed url from the Example Canned Policy in Amazon's documentation. """ url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes" expire_time = 1258237200 expected_url = "http://example.com/" # replaced for brevity signed_url = self.dist.create_signed_url( url, self.pk_id, expire_time, private_key_string=self.pk_str) # self.assertEqual(expected_url, signed_url)