Pregunta

Versión corta:. ¿Cómo hago URL firmados "a la carta" a la conducta X-Accel-redirección mímica de Nginx (es decir, la protección de descargas) con Amazon CloudFront / S3 usando Python

Tengo un servidor de Django y funcionando con un front-end Nginx. He estado recibiendo golpes con las peticiones a la misma y recientemente ha tenido que instalar como una aplicación Tornado WSGI para evitar que se estrellarse en modo FastCGI.

Ahora estoy teniendo un problema con mi servidor empantanarse (es decir, la mayor parte de su ancho de banda está siendo utilizado hasta) debido a demasiadas peticiones de los medios de comunicación que se están realizando a ella, he estado mirando en CDN y creo que Amazon CloudFront / S3 sería la solución adecuada para mí.

He estado usando cabecera X-Accel-redirección de Nginx para proteger los archivos de descarga no autorizada, pero no tengo esa habilidad con CloudFront / S3 - sin embargo, sí ofrecen firmado URL. No soy experto en Python, con mucho, y sin duda no saben cómo crear una URL firmado correctamente, así que estaba esperando que alguien podría tener un enlace de cómo hacer estas URL "a la carta" o estaría dispuesto a explicar cómo aquí, sería muy apreciado.

Además, es esta la solución adecuada, incluso? No estoy muy familiarizado con la CDN, es que hay un CDN que sería más adecuado para esto?

¿Fue útil?

Solución

Amazon CloudFront URL Firmado trabajo de manera diferente a Amazon S3 firmó URL. CloudFront utiliza firmas RSA en base a un par de claves CloudFront separado que se tiene que configurar en su página Credenciales de cuenta de Amazon. Aquí hay algo de código para generar realmente una URL por tiempo limitado en Python usando el href="https://pypi.python.org/pypi/M2Crypto" rel="nofollow noreferrer"> M2Crypto biblioteca :

Crear un par de claves para CloudFront

Creo que la única manera de hacer esto es a través de la página web de Amazon. Ve a tu página de AWS "Cuenta" y haga clic en el enlace "credenciales de seguridad". Haga clic en la pestaña "pares de claves" y luego haga clic en "Crear un nuevo par de claves". Esto generará un nuevo par de claves para usted y descargar automáticamente un archivo de clave privada (pk-xxxxxxxxx.pem). Mantener la seguridad y archivo de clave privada. También tenga en cuenta por el "par de claves de identificación" de Amazon, ya que lo necesitaremos en el siguiente paso.

Generar algunas de las URL en Python

Como boto de la versión 2.0 no parece haber ningún soporte para generar firmado CloudFront URL. Python no incluir rutinas de cifrado RSA en la biblioteca estándar, por lo que tendremos que utilizar una biblioteca adicional. He usado M2Crypto en este ejemplo.

Para una distribución no transmisión, debe utilizar la URL completa CloudFront como el recurso, sin embargo para el streaming sólo utilizamos el nombre del objeto del archivo de vídeo. Ver el código de abajo para un ejemplo completo de generar una URL que sólo tiene una duración de 5 minutos.

Este código se basa libremente en el código de ejemplo PHP proporcionada por Amazon en la documentación 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)

Asegúrese de que configura su distribución con un conjunto de parámetros TrustedSigners a la cuenta de haberes de su par de claves (o "Auto" si es su propia cuenta)

Introducción a AWS segura CloudFront el streaming con Python para un ejemplo totalmente trabajado en establecer esto para el streaming con Python

Otros consejos

Esta característica es ahora ya soportado en Botocore , que es la biblioteca subyacente de Boto3, la última AWS SDK oficial para Python . (El siguiente ejemplo requiere la instalación del paquete de RSA, pero se puede utilizar otro paquete RSA también, al definir su propio "firmante RSA normalizada".)

Las miradas de uso como esta:

    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)

exención de responsabilidad: yo soy el autor de ese PR

.

Como muchos han comentado ya, la aceptado inicialmente respuesta no se aplica a Amazon CloudFront , de hecho, en la medida Sirviendo contenido privado a través CloudFront requiere el uso de dedicada CloudFront firmado URL - consecuencia respuesta de secretmike ha sido correcta, pero mientras tanto está anticuado después de que él mismo tomó el tiempo y el apoyo Agregado para generar firmado URL para CloudFront (gracias mucho por esto!).

boto ahora es compatible con una dedicada create_signed_url método y la antigua dependencia binario M2Crypto ha sido recientemente sustituido con un < a href = "http://stuvel.eu/rsa" rel = "nofollow noreferrer"> pura-Python RSA aplicación así, consulte no utilice M2Crypto de CloudFront URL firma .

A medida que cada vez más común, se puede encontrar uno o varios buenos ejemplos de uso dentro de las pruebas unitarias relacionadas (ver test_signed_urls.py ), por ejemplo test_canned_policy (auto) - vea Configuración (auto) para las variables referenciados self.pk_idand self.pk_str (obviamente, que necesita sus propias claves):

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 respuesta de obras secretmike, pero es mejor usar rsa en lugar de M2Crypto.

I utiliza boto que utiliza 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")

Utilice el boto documentation

Esto es lo que utilizo para crear una política para que pueda dar acceso a varios archivos con el mismo "firma":

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)

se puede utilizar para todos los archivos bajo http://your_domain/* por ejemplo:

 http://your_domain/image2.png?Policy...
 http://your_domain/image2.png?Policy...
 http://your_domain/file1.json?Policy...
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top