Création d'URL signé pour Amazon CloudFront
-
24-09-2019 - |
Question
Version courte:. Comment puis-je signé URL "à la demande" pour imiter Nginx X-Accel-Rediriger le comportement (à savoir la protection de téléchargements) avec Amazon CloudFront / S3 en utilisant Python
J'ai un serveur Django et en cours d'exécution avec un front-end Nginx. Je reçois des demandes martelées à et avait récemment l'installer comme demande WSGI de Tornado pour l'empêcher de écraser en mode FastCGI.
Maintenant, je vais avoir un problème avec mon serveur enlisement (ie plus de sa bande passante est utilisée vers le haut) en raison de trop de demandes pour les médias qui lui est présentée, je l'ai cherchée dans CDNs et je crois Amazon CloudFront / S3 serait la bonne solution pour moi.
Je me sers de X-Accel-Rediriger Nginx en-tête pour protéger les fichiers de téléchargement non autorisé, mais je n'ai pas cette capacité avec CloudFront / S3 - mais ils offrent des URL signées. Je ne suis pas expert en Python de loin et certainement ne savent pas comment créer une URL correctement signé, donc j'espérais que quelqu'un aurait un lien pour savoir comment faire ces URL « à la demande » ou seraient prêts à expliquer comment ici, il serait grandement apprécié.
Aussi, est-ce la bonne solution, même? Je ne suis pas trop familier avec NAA, est-il un CDN qui serait mieux adapté pour cela?
La solution
Amazon CloudFront URL Signed travail différemment Amazon S3 signé URL. CloudFront utilise des signatures RSA à partir d'une paire de clés CloudFront séparée que vous devez mettre en place dans votre page compte Informations d'identification Amazon. Voici un code pour générer effectivement une URL limitée dans le temps en Python en utilisant le M2Crypto bibliothèque:
Créer une paire de clés pour CloudFront
Je pense que la seule façon de le faire est par le biais du site Web d'Amazon. Allez dans votre AWS page « compte » et cliquez sur le lien « Informations d'identification de sécurité ». Cliquez sur l'onglet « paires de clés », puis cliquez sur « Créer une nouvelle paire de clés ». Cela va générer une nouvelle paire de clés pour vous et télécharger automatiquement un fichier de clé privée (pk-xxxxxxxxx.pem). Gardez le fichier de clé privée et sécurité. Notez également le « ID paire de clés » de amazon que nous en aurons besoin à l'étape suivante.
Générer des URL en Python
de la version 2.0 de Boto il ne semble pas y avoir de soutien pour générer des URL signées CloudFront. Python ne comprend pas les routines de chiffrement RSA dans la bibliothèque standard que nous devrons utiliser une bibliothèque supplémentaire. Je l'ai utilisé M2Crypto dans cet exemple.
Pour une distribution non-diffusion en continu, vous devez utiliser l'URL complète CloudFront comme ressource, mais pour le streaming, nous utilisons uniquement le nom d'objet du fichier vidéo. Voir le code ci-dessous pour un exemple complet de générer une URL qui ne dure que 5 minutes.
Ce code est vaguement basé sur l'exemple de code PHP fourni par Amazon dans la documentation CloudFront.
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)
Assurez-vous que vous configurez votre distribution avec un paramètre TrustedSigners réglé sur le compte tenue de votre paire de clés (ou « Auto » si elle est votre propre compte)
Voir Mise en route avec AWS sécurisé CloudFront le streaming avec Python pour un exemple entièrement travaillé sur cette mise en place pour le streaming avec Python
Autres conseils
Cette fonctionnalité est maintenant déjà pris en charge Botocore, qui est la bibliothèque sous-jacente de Boto3, le dernier SDK AWS officiel pour Python . (L'exemple suivant nécessite l'installation du package rsa, mais vous pouvez utiliser un autre package RSA aussi, il suffit de définir votre propre « RSA normalisée signeur ».)
L'utilisation ressemble à ceci:
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)
Disclaimer:. Je suis l'auteur de ce PR
Comme beaucoup l'ont déjà commenté, le initialement accepté réponse ne s'applique à Amazon CloudFront en fait, dans la mesure où au service de contenu privé par le biais CloudFront nécessite l'utilisation de dédié noreferrer CloudFront URL signées - ce donc secretmike réponse a été correct, mais il est quant à lui dépassé après avoir pris le temps lui-même et Ajout du support pour générer URL signées pour CloudFront (merci beaucoup pour cela!).
Boto prend désormais en charge un dédié pur-Python mise en œuvre RSA ainsi, voir ne pas utiliser M2Crypto de signature URL CloudFront.
Comme de plus en plus commune, on peut trouver un ou plusieurs bons exemples d'utilisation dans les tests unitaires connexes (voir test_signed_urls.py ), par exemple test_canned_policy (auto) - voir setUp (auto) pour les variables référencées self.pk_id
and self.pk_str
(vous aurez évidemment besoin de vos propres clés):
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)
La réponse de secretmike fonctionne, mais il est préférable d'utiliser rsa
au lieu de M2Crypto
.
boto
qui utilise 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")
Utilisez le boto documentation
est ce que je l'utilise pour créer une politique afin que je puisse donner accès à plusieurs fichiers avec le même « signature »:
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)
Je peux l'utiliser pour tous les fichiers sous http://your_domain/*
par exemple:
http://your_domain/image2.png?Policy...
http://your_domain/image2.png?Policy...
http://your_domain/file1.json?Policy...