短いバージョン:Pythonを使用してAmazon CloudFront/S3でNginxのX-Accel-Redirect動作(ダウンロードの保護)を模倣するために、署名付きURLを「オンデマンド」にするにはどうすればよいですか。
Djangoサーバーが起動し、Nginxフロントエンドで実行されています。サーバーへのリクエストが殺到しており、最近 としてインストールする必要がありました。 )Tornado WSGIアプリケーション。FastCGIモードでクラッシュするのを防ぎます。
メディアへのリクエストが多すぎるためにサーバーがダウンする(つまり、帯域幅のほとんどが使い果たされる)という問題が発生しています。CDNを調べており、Amazon CloudFront/S3を使用していると思います。私にとって適切な解決策になるでしょう。
NginxのX-Accel-Redirectヘッダーを使用して、ファイルを不正なダウンロードから保護してきましたが、CloudFront/S3にはその機能がありません-ただし、署名付きURLは提供されます。私はPythonの専門家ではなく、署名付きURLを適切に作成する方法を確実に知らないので、誰かがこれらのURLを「オンデマンド」にする方法のリンクを持っていることを望んでいました。 「または、ここでの方法を喜んで説明していただければ幸いです。
また、これは適切な解決策ですか?私はCDNにあまり詳しくありませんが、これにより適したCDNはありますか?
Amazon CloudFront署名付きURL AmazonS3署名付きURLとは動作が異なります。 CloudFrontは、Amazonアカウント認証情報ページで設定する必要がある個別のCloudFrontキーペアに基づくRSA署名を使用します。これは、Python M2Crypto ライブラリを使用して時間制限付きURLを実際に生成するためのコードです。
CloudFrontのキーペアを作成します
これを行う唯一の方法は、AmazonのWebサイトを使用することだと思います。 AWSの[アカウント]ページに移動し、[セキュリティ認証情報]リンクをクリックします。 [キーペア]タブをクリックしてから、[新しいキーペアの作成]をクリックします。これにより、新しいキーペアが生成され、秘密キーファイル(pk-xxxxxxxxx.pem)が自動的にダウンロードされます。キーファイルを安全かつプライベートに保ちます。また、次のステップで必要になるため、Amazonの「キーペアID」を書き留めておきます。
PythonでいくつかのURLを生成します
Botoバージョン2.0の時点では、署名されたCloudFrontURLの生成はサポートされていないようです。 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)
キーペアを保持しているアカウント(または、自分のアカウントの場合は「Self」)にTrustedSignersパラメーターを設定してディストリビューションを設定してください。
Pythonを使用したストリーミング用にこれを設定するための完全に機能する例については、 Pythonを使用した安全なAWS CloudFrontストリーミングの開始 を参照してください。
この機能は現在 Botocoreですでにサポートされています 、これは Boto3、Python用の最新の公式AWS SDK の基盤となるライブラリです。 (次のサンプルでは、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)
免責事項:私はそのPRの作者です。
多くの人がすでにコメントしているように、 最初に受け入れられた回答 は適用されません Amazon CloudFront 実際、 CloudFrontを介したプライベートコンテンツの提供 は使用が必要です専用の CloudFront署名付きURL -したがって secretmikeの回答 は正しいですが、彼自身が時間をかけて 署名付きURLを生成するためのサポートを追加しましたCloudFront (これに感謝します!)。
boto は専用の create_signed_url メソッドをサポートするようになり、以前のバイナリ依存関係M2Cryptoは最近 純粋なPython RSA実装 に置き換えられました。-を参照してください。 クラウドフロントURL署名にM2Cryptoを使用しないでください 。
ますます一般的になっているように、関連する単体テスト内で1つ以上の適切な使用例を見つけることができます( test_signed_urls.py を参照)。たとえば、 test_canned_policy(self)setUp(self) 参照される変数の場合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)
簡単な解決策は変更する必要がないことがわかりましたs3.generate_url
方法、
cloudfront設定を選択するだけです:Yes, Update bucket policy
。
その後の変更から:
https://xxxx.s3.amazonaws.com/hello.png&Signature=sss&Expires=1585008320&AWSAccessKeyId=kkk
に
https://yyy.cloudfront.net/hello.png&Signature=sss&Expires=1585008320&AWSAccessKeyId=kkk
yyy.cloudfront.net
はCloudFrontドメインです
参照: https://aws.Amazon.com/blogs/developer/accessing-private-content-in-Amazon-cloudfront/
これは、同じ「署名」を持つ複数のファイルへのアクセスを許可できるように、ポリシーを作成するために使用するものです。
import json
import rsa
import time
from base64 import b64encode
url = "http://your_domain/*"
expires = int(time.time() + 3600)
pem = """-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----"""
key_pair_id = 'ABX....'
policy = {}
policy['Statement'] = [{}]
policy['Statement'][0]['Resource'] = url
policy['Statement'][0]['Condition'] = {}
policy['Statement'][0]['Condition']['DateLessThan'] = {}
policy['Statement'][0]['Condition']['DateLessThan']['AWS:EpochTime'] = expires
policy = json.dumps(policy)
private_key = rsa.PrivateKey.load_pkcs1(pem)
signature = b64encode(rsa.sign(str(policy), private_key, 'SHA-1'))
print '?Policy=%s&Signature=%s&Key-Pair-Id=%s' % (b64encode(policy),
signature,
key_pair_id)
http://your_domain/*
の下のすべてのファイルに使用できます。例:
http://your_domain/image2.png?Policy...
http://your_domain/image2.png?Policy...
http://your_domain/file1.json?Policy...
secretmikeの答えは機能しますが、M2Crypto
の代わりに rsa
を使用することをお勧めします。
boto
を使用しました。これはrsa
を使用します。
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")
boto documentation
を使用します