Pergunta

Versão curta:Como faço para criar URLs assinados "sob demanda" para imitar o comportamento X-Accel-Redirect do Nginx (ou seja,protegendo downloads) com Amazon CloudFront/S3 usando Python.

Eu tenho um servidor Django instalado e funcionando com um front-end Nginx.Tenho sido bombardeado com solicitações e recentemente tive que instalá-lo como um Tornado Aplicativo WSGI para evitar que ele trave no modo FastCGI.

Agora estou tendo um problema com meu servidor travando (ou seja,a maior parte de sua largura de banda está sendo usada) devido a muitas solicitações de mídia feitas a ele, estive pesquisando CDNs e acredito que o Amazon CloudFront/S3 seria a solução adequada para mim.

Tenho usado o cabeçalho X-Accel-Redirect do Nginx para proteger os arquivos contra downloads não autorizados, mas não tenho essa capacidade com o CloudFront/S3 - no entanto, eles oferecem URLs assinados.Não sou de longe um especialista em Python e definitivamente não sei como criar um URL assinado corretamente, então esperava que alguém tivesse um link sobre como tornar esses URLs "sob demanda" ou estivesse disposto a explicar como fazer isso. aqui, seria muito apreciado.

Além disso, esta é a solução adequada, mesmo?Não estou muito familiarizado com CDNs. Existe um CDN que seria mais adequado para isso?

Foi útil?

Solução

Amazon CloudFront Signed URLs Trabalhe de maneira diferente dos URLs assinados pela Amazon S3. O CloudFront usa assinaturas RSA com base em um KeyyPair do CloudFront separado, que você deve configurar na página Credenciais da sua conta da Amazon. Aqui está algum código para realmente gerar um URL com tempo limitado em python usando o M2CRYPTO biblioteca:

Crie um teclado para o CloudFront

Eu acho que a única maneira de fazer isso é através do site da Amazon. Entre na sua página da "conta" da AWS e clique no link "Credenciais de segurança". Clique na guia "Pares de teclas" e clique em "Crie um novo par de teclas". Isso gerará um novo par de chaves para você e baixará automaticamente um arquivo de chave privado (pk-xxxxxxxxx.pem). Mantenha o arquivo chave seguro e privado. Anote também o "ID do par de chaves" da Amazon, pois precisaremos dele na próxima etapa.

Gerar alguns URLs em python

A partir do BOTO, versão 2.0, não parece haver suporte para gerar URLs do CloudFront assinado. O Python não inclui rotinas de criptografia RSA na biblioteca padrão, por isso teremos que usar uma biblioteca adicional. Eu usei M2Crypto neste exemplo.

Para uma distribuição não transmitida, você deve usar o URL do CloudFront completo como recurso, no entanto, para o streaming, usamos apenas o nome do objeto do arquivo de vídeo. Veja o código abaixo para obter um exemplo completo de geração de um URL que dura apenas 5 minutos.

Este código é baseado vagamente no código de exemplo PHP fornecido pela Amazon na documentação do 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)

Certifique -se de configurar sua distribuição com um parâmetro TrustedSigners definido para a conta que segura o seu teclado (ou "eu" se for sua própria conta)

Ver Introdução ao streaming seguro da AWS Cloudfront com Python Para um exemplo totalmente trabalhado para configurar isso para streaming com Python

Outras dicas

Este recurso é agora já suportado em botocore, que é a biblioteca subjacente de BOTO3, o mais recente AWS SDK oficial para Python. (A amostra a seguir requer a instalação do pacote RSA, mas você também pode usar outro pacote RSA, basta definir seu próprio "signatário RSA normalizado".).)

O uso é assim:

    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)

Isenção de responsabilidade: Eu sou o autor desse pr.

Como muitos já comentaram, o resposta inicialmente aceita não se aplica a Amazon Cloud Front na verdade, na medida em Servindo conteúdo privado por meio do CloudFront requer o uso de dedicado URLs assinados do CloudFront - de acordo resposta do secretmike estava correto, mas entretanto está desatualizado depois que ele próprio despendeu tempo e Adicionado suporte para geração de URLs assinados para CloudFront (muito obrigado por isso!).

boto agora suporta um dedicado criar_url_assinado método e a antiga dependência binária M2Crypto foi recentemente substituída por um implementação RSA em Python puro também, veja Não use M2Crypto para assinatura de URL na nuvem.

Como é cada vez mais comum, pode-se encontrar um ou mais bons exemplos de uso nos testes unitários relacionados (veja test_signed_urls.py), por exemplo test_canned_policy(auto) - ver configuração(auto) para as variáveis ​​referenciadas self.pk_ide self.pk_str (obviamente você precisará de suas próprias chaves):

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)

A resposta de Secretmike funciona, mas é melhor usar rsa ao invés de M2Crypto.

eu usei boto que usa 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")

Use o boto documentation

É isso que eu uso para criar uma política para poder dar acesso a vários arquivos com a mesma "assinatura":

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)

Eu posso usá -lo para todos os arquivos em http://your_domain/* por exemplo:

 http://your_domain/image2.png?Policy...
 http://your_domain/image2.png?Policy...
 http://your_domain/file1.json?Policy...
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top