Начало работы с безопасным потоковым потоком AWS CloudFront с Python

Я создал ведро S3, загрузил видео, создал потоковое распространение в CloudFront. Протестировал его со статическим HTML-плеером, и он работает. Я создал пару ключей через настройки учетной записи. На данный момент у меня есть файл секретного ключа, сидящий на моем рабочем столе. Вот где я.

Моя цель – добраться до того места, где мой сайт Django / Python создает безопасные URL-адреса, и люди не могут получить доступ к видеороликам, если они не пришли с одной из моих страниц. Проблема в том, что у меня аллергия на то, как Амазонка выложила вещи, и я просто все больше и больше запутываюсь.

Я понимаю, что это не лучший вопрос в StackOverflow, но я уверен, что я не могу быть единственным дураком здесь, который не может сделать головы или хвосты из того, как настроить безопасную ситуацию CloudFront / S3. Я был бы очень признателен за вашу помощь и желаю (один раз два дня прошло) дать 500pt щедрости на лучший ответ.

У меня есть несколько вопросов, которые, после ответа, должны вписываться в одно объяснение того, как выполнить то, что мне нужно:

  • В документации (есть пример в следующем пункте) есть много XML, лежащих вокруг, говоря, что мне нужно, чтобы POST делался в разных местах. Есть ли онлайн-консоль для этого? Или я буквально вынуждаю это через cURL (et al)?

  • Как создать идентификатор исходного входа для CloudFront и связать его с моим дистрибутивом? Я прочитал этот документ, но, по первому вопросу, не знаю, что с ним делать. Как моя ключевая пара вписывается в это?

  • Как только это будет сделано, как ограничить ведро S3 только тем, чтобы люди могли загружать вещи через эту личность? Если это другая XML-задание, а не щелчок по веб-интерфейсу, сообщите мне, где и как я должен использовать это в своей учетной записи.

  • В Python, что является самым простым способом генерации истекающего URL для файла. Я установил boto но я не вижу, как получить файл из потокового дистрибутива.

  • Есть ли какие-либо приложения или сценарии, которые могут затруднить установку этого одеяния? Я использую Ubuntu (Linux), но у меня есть XP в VM, если он только для Windows. Я уже посмотрел на CloudBerry S3 Explorer Pro, но он имеет такой же смысл, как и в онлайн-интерфейсе.

2 Solutions collect form web for “Начало работы с безопасным потоковым потоком AWS CloudFront с Python”

Вы правы, для получения этой настройки требуется много работы API. Надеюсь, они скоро попадут в консоль AWS!

ОБНОВЛЕНИЕ: я отправил этот код на boto – с boto v2.1 (выпущен в 2011-10-27) это становится намного проще. Для boto <2.1 используйте здесь инструкции. Для boto 2.1 или выше получите обновленные инструкции в своем блоге: http://www.secretmike.com/2011/10/aws-cloudfront-secure-streaming.html Как только boto v2.1 будет упакован больше дистрибутивами, я буду обновите ответ здесь.

Чтобы выполнить то, что вы хотите, вам необходимо выполнить следующие шаги, которые я подробно рассмотрю ниже:

  1. Создайте свой ведро s3 и загрузите некоторые объекты (вы уже это сделали)
  2. Создайте облачный «Идентификатор доступа к исходному окну» (в основном, учетную запись AWS, чтобы облачная служба могла получить доступ к вашему ведро s3)
  3. Измените списки ACL на своих объектах, чтобы разрешить их просматривать только ваш идентификатор доступа к домену Cloudfront (это предотвращает обход пользователей CloudPoint и переход на s3)
  4. Создайте облачный дистрибутив с базовыми URL-адресами и тот, который требует подписанных URL-адресов
  5. Проверьте, что вы можете загружать объекты из основного облачного распределения, но не из s3 или подписанного облачного распределения
  6. Создание пары ключей для подписи URL-адресов
  7. Создайте несколько URL-адресов, используя Python
  8. Проверьте, что подписанные URL-адреса работают

1 – Создать ведро и загрузить объект

Самый простой способ сделать это – через консоль AWS, но для полноты я покажу, как использовать boto. Код Boto показан здесь:

 import boto #credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY s3 = boto.connect_s3() #bucket name MUST follow dns guidelines new_bucket_name = "stream.example.com" bucket = s3.create_bucket(new_bucket_name) object_name = "video.mp4" key = bucket.new_key(object_name) key.set_contents_from_filename(object_name) 

2 – Создайте облачный «Идентификатор доступа к исходному окну»

На данный момент этот шаг можно выполнить только с помощью API. Код Boto находится здесь:

 import boto #credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY cf = boto.connect_cloudfront() oai = cf.create_origin_access_identity(comment='New identity for secure videos') #We need the following two values for later steps: print("Origin Access Identity ID: %s" % oai.id) print("Origin Access Identity S3CanonicalUserId: %s" % oai.s3_user_id) 

3 – Измените списки управления доступом на своих объектах

Теперь, когда у нас есть специальная учетная запись S3 (S3CanonicalUserId, которую мы создали выше), нам нужно предоставить ей доступ к нашим объектам s3. Мы можем легко это сделать, используя консоль AWS, открыв вкладку «Разрешения» объекта (а не ведро!), Нажмите кнопку «Добавить дополнительные разрешения» и вставив очень длинный S3CanonicalUserId, который мы получили выше, в поле «Грантополучатель» нового. Убедитесь, что вы предоставили новое разрешение «Открыть / загрузить».

Вы также можете сделать это в коде, используя следующий сценарий boto:

 import boto #credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY s3 = boto.connect_s3() bucket_name = "stream.example.com" bucket = s3.get_bucket(bucket_name) object_name = "video.mp4" key = bucket.get_key(object_name) #Now add read permission to our new s3 account s3_canonical_user_id = "<your S3CanonicalUserID from above>" key.add_user_grant("READ", s3_canonical_user_id) 

4 – Создайте облачную рассылку

Обратите внимание, что пользовательские корни и частные дистрибутивы не полностью поддерживаются в boto до версии 2.0, которые официально не были выпущены на момент написания. Приведенный ниже код вытаскивает код из ветки boto 2.0 и взламывает его вместе, чтобы получить его, но это некрасиво. Развертка 2.0 обрабатывает это гораздо более элегантно – определенно используйте это, если это возможно!

 import boto from boto.cloudfront.distribution import DistributionConfig from boto.cloudfront.exception import CloudFrontServerError import re def get_domain_from_xml(xml): results = re.findall("<DomainName>([^<]+)</DomainName>", xml) return results[0] #custom class to hack this until boto v2.0 is released class HackedStreamingDistributionConfig(DistributionConfig): def __init__(self, connection=None, origin='', enabled=False, caller_reference='', cnames=None, comment='', trusted_signers=None): DistributionConfig.__init__(self, connection=connection, origin=origin, enabled=enabled, caller_reference=caller_reference, cnames=cnames, comment=comment, trusted_signers=trusted_signers) #override the to_xml() function def to_xml(self): s = '<?xml version="1.0" encoding="UTF-8"?>\n' s += '<StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">\n' s += ' <S3Origin>\n' s += ' <DNSName>%s</DNSName>\n' % self.origin if self.origin_access_identity: val = self.origin_access_identity s += ' <OriginAccessIdentity>origin-access-identity/cloudfront/%s</OriginAccessIdentity>\n' % val s += ' </S3Origin>\n' s += ' <CallerReference>%s</CallerReference>\n' % self.caller_reference for cname in self.cnames: s += ' <CNAME>%s</CNAME>\n' % cname if self.comment: s += ' <Comment>%s</Comment>\n' % self.comment s += ' <Enabled>' if self.enabled: s += 'true' else: s += 'false' s += '</Enabled>\n' if self.trusted_signers: s += '<TrustedSigners>\n' for signer in self.trusted_signers: if signer == 'Self': s += ' <Self/>\n' else: s += ' <AwsAccountNumber>%s</AwsAccountNumber>\n' % signer s += '</TrustedSigners>\n' if self.logging: s += '<Logging>\n' s += ' <Bucket>%s</Bucket>\n' % self.logging.bucket s += ' <Prefix>%s</Prefix>\n' % self.logging.prefix s += '</Logging>\n' s += '</StreamingDistributionConfig>\n' return s def create(self): response = self.connection.make_request('POST', '/%s/%s' % ("2010-11-01", "streaming-distribution"), {'Content-Type' : 'text/xml'}, data=self.to_xml()) body = response.read() if response.status == 201: return body else: raise CloudFrontServerError(response.status, response.reason, body) cf = boto.connect_cloudfront() s3_dns_name = "stream.example.com.s3.amazonaws.com" comment = "example streaming distribution" oai = "<OAI ID from step 2 above like E23KRHS6GDUF5L>" #Create a distribution that does NOT need signed URLS hsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True) hsd.origin_access_identity = oai basic_dist = hsd.create() print("Distribution with basic URLs: %s" % get_domain_from_xml(basic_dist)) #Create a distribution that DOES need signed URLS hsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True) hsd.origin_access_identity = oai #Add some required signers (Self means your own account) hsd.trusted_signers = ['Self'] signed_dist = hsd.create() print("Distribution with signed URLs: %s" % get_domain_from_xml(signed_dist)) 

5 – Проверьте, что вы можете загружать объекты из облачного, но не из s3

Теперь вы можете проверить:

  • stream.example.com.s3.amazonaws.com/video.mp4 – должен предоставить AccessDenied
  • signed_distribution.cloudfront.net/video.mp4 – должен указывать MissingKey (поскольку URL-адрес не подписан)
  • basic_distribution.cloudfront.net/video.mp4 – должен отлично работать

Тесты должны быть настроены для работы с вашим проигрывателем потока, но основная идея заключается в том, что должен работать только основной URL облачного интерфейса.

6 – Создайте ключевую пару для CloudFront

Я думаю, что единственный способ сделать это – через веб-сайт Amazon. Перейдите на страницу «Учетная запись AWS» и нажмите ссылку «Учетные данные безопасности». Перейдите на вкладку «Ключевые пары» и нажмите «Создать новую пару ключей». Это создаст новую пару ключей для вас и автоматически загрузит файл закрытого ключа (pk-xxxxxxxxx.pem). Храните файл ключа в безопасном и закрытом состоянии. Также обратите внимание на «Идентификатор пары ключей» от амазонки, поскольку мы будем нуждаться в этом на следующем шаге.

7 – Создание некоторых URL-адресов в Python

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

Для не потокового распространения вы должны использовать полный URL облачного облака в качестве ресурса, однако для потоковой передачи мы используем только имя объекта видеофайла. См. Приведенный ниже код для полного примера создания 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(str(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} #now base64 encode it (must be URL safe) encoded_policy = aws_url_base64_encode(canned_policy) #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 = "APKAIAZCZRKVIO4BQ" #from the AWS accounts page priv_key_file = "cloudfront-pk.pem" #your private keypair file 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) #Flash player doesn't like query params so encode them enc_url = encode_query_param(signed_url) print(enc_url) 

8 – Попробуйте URL-адреса

Надеюсь, теперь у вас должен быть рабочий URL-адрес, который выглядит примерно так:

 video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQ 

Поместите это в свой js, и у вас должно получиться что-то похожее на это (из примера PHP в документации Amazon CloudFront):

 var so_canned = new SWFObject('http://location.domname.com/~jvngkhow/player.swf','mpl','640','360','9'); so_canned.addParam('allowfullscreen','true'); so_canned.addParam('allowscriptaccess','always'); so_canned.addParam('wmode','opaque'); so_canned.addVariable('file','video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQ'); so_canned.addVariable('streamer','rtmp://s3nzpoyjpct.cloudfront.net/cfx/st'); so_canned.write('canned'); 

Резюме

Как вы можете видеть, не очень легко! boto v2 поможет значительно настроить дистрибутив. Я узнаю, можно ли получить код генерации URL-адресов, чтобы улучшить эту замечательную библиотеку!

В Python, что является самым простым способом генерации истекающего URL для файла. Я установил boto, но я не вижу, как получить файл из потокового дистрибутива.

Вы можете создать истекающий подписанный URL-адрес ресурса. В документации для Boto3 есть хорошее примерное решение для этого:

 import datetime from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding from botocore.signers import CloudFrontSigner def rsa_signer(message): with open('path/to/key.pem', 'rb') as key_file: private_key = serialization.load_pem_private_key( key_file.read(), password=None, backend=default_backend() ) signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) signer.update(message) return signer.finalize() key_id = 'AKIAIOSFODNN7EXAMPLE' url = 'http://d2949o5mkkp72v.cloudfront.net/hello.txt' expire_date = datetime.datetime(2017, 1, 1) cloudfront_signer = CloudFrontSigner(key_id, rsa_signer) # Create a signed url that will be valid until the specfic expiry date # provided using a canned policy. signed_url = cloudfront_signer.generate_presigned_url( url, date_less_than=expire_date) print(signed_url) 
Python - лучший язык программирования в мире.