Создание подписанных URL-адресов для Amazon CloudFront

StackOverflow https://stackoverflow.com/questions/2573919

  •  24-09-2019
  •  | 
  •  

Вопрос

Сокращенная версия:Как мне сделать подписанные URL-адреса "по требованию", чтобы имитировать поведение X-Accel-Redirect в Nginx (т.е.защита загрузок) с помощью Amazon CloudFront / S3 с использованием Python.

У меня запущен сервер Django с интерфейсом Nginx.Я был забит запросами к нему, и недавно мне пришлось установить его как Торнадо Приложение WSGI, чтобы предотвратить его сбой в режиме FastCGI.

Теперь у меня возникла проблема с тем, что мой сервер застрял (т.е.большая часть его пропускной способности израсходована) из-за слишком большого количества запросов на мультимедиа, поступающих к нему, я изучал CDN и считаю, что Amazon CloudFront / S3 был бы для меня подходящим решением.

Я использовал заголовок X-Accel-Redirect от Nginx для защиты файлов от несанкционированной загрузки, но у меня нет такой возможности с CloudFront / S3 - однако они предлагают подписанные URL-адреса.Я далеко не эксперт по Python и определенно не знаю, как правильно создать подписанный URL-адрес, поэтому я надеялся, что у кого-нибудь найдется ссылка на то, как сделать эти URL-адреса "по требованию", или он был бы готов объяснить, как это сделать здесь, это было бы с благодарностью.

Кроме того, является ли это вообще правильным решением?Я не слишком знаком с CDN, есть ли CDN, который лучше подходил бы для этого?

Это было полезно?

Решение

Amazon CloudFront подписал URL Работайте по-разному, чем Amazon S3 подписали URL. CloudFront использует подписи RSA на основе отдельной CloudFront KeyPair, который вы должны настроить на странице учетных данных Amazon Account. Вот какой-то код, который фактически генерирует URL-адрес, ограниченный временем в Python, используя M2CRYPTO библиотека:

Создайте клавиатуру для Cloudfront

Я думаю, что единственный способ сделать это через веб-сайт Amazon. Зайдите на страницу AWS «Account» и нажмите ссылку «Учетные данные безопасности». Нажмите на вкладку «Пары ключей», затем нажмите «Создать новую ключевую пару». Это будет генерировать новую ключевую пару для вас и автоматически загружать файл закрытого ключа (PK-XXXXXXXXX.PEM). Держите ключевой файл безопасным и частным. Также обратите внимание на «клавишную пару идентификатора» от Amazon, насколько нам понадобится на следующем шаге.

Генерировать некоторые URL в Python

По состоянию на Boto версии 2.0, кажется, не может быть никакой поддержки для генерации подписанных URL-фронтов. Python не включает в себя процедуры шифрования RSA в стандартной библиотеке, поэтому нам придется использовать дополнительную библиотеку. Я использовал M2Crypto в этом примере.

Для распределения без потокового потока необходимо использовать полный URL CloudFront в качестве ресурса, однако для потоковой передачи мы используем только имя объекта видеофайла. Смотрите код ниже для полного примера генерации URL, который длится только 5 минут.

Этот код осторожно основан на примере PHP, предоставленный Amazon в документации 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)

Убедитесь, что вы устанавливаете ваше распределение с параметром TrustedSigners, установленным на учетную запись, удерживающую keypair (или «Self», если это ваша учетная запись)

Видеть Начало работы с безопасными AWS CloudFront Trequing с Python Для полностью обработанного примера при установке этого для потоковой передачи с Python

Другие советы

Эта особенность сейчас уже поддерживается в ботокоре, что является базовой библиотекой BOTO3, последний официальный AWS SDK для Python. Отказ (Следующий образец требует установки пакета RSA, но вы также можете использовать другой пакет RSA, просто определите свой собственный «Нормализованный RSA Signer».)

Использование выглядит так:

    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 требует использования специальных Подписанные URL-адреса CloudFront - соответственно ответ secretmike был правильным, но тем временем он устарел после того, как он сам потратил время и Добавлена поддержка генерации подписанных URL-адресов для CloudFront (большое спасибо за это!).

бото теперь поддерживается выделенный создать_значение_url метод и прежняя двоичная зависимость M2Crypto недавно были заменены на реализация RSA на чистом Python кроме того, смотрите Не используйте M2Crypto для подписи URL-адресов cloudfront.

Поскольку это становится все более распространенным явлением, можно найти один или несколько хороших примеров использования в соответствующих модульных тестах (см. 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)

Ответ Secretmike работает, но лучше использовать rsa вместо M2Crypto.

я использовал 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

Это то, что я использую для создания политики, чтобы я мог дать доступ к нескольким файлам с той же «подписью»:

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...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top