Создание подписанных URL-адресов S3 и Cloudfront с помощью AWS SDK

Кто-нибудь успешно использовал AWS SDK для создания подписанных URL-адресов для объектов в ведре S3, которые также работают над CloudFront? Я использую JavaScript AWS SDK, и очень просто создать подписанные URL-адреса через ссылки S3. Я просто создал личное ведро и использовал следующий код для создания URL-адреса:

var AWS = require('aws-sdk')
  , s3 = new AWS.S3()
  , params = {Bucket: 'my-bucket', Key: 'path/to/key', Expiration: 20}

s3.getSignedUrl('getObject', params, function (err, url) {
  console.log('Signed URL: ' + url)
})

Это отлично работает, но я также хочу показать URL-адрес CloudFront для моих пользователей, чтобы они могли увеличить скорость загрузки с помощью CDN. Я установил дистрибутив CloudFront, который изменил политику ведра, чтобы разрешить доступ. Однако после этого можно было получить доступ к любому файлу через URL CloudFront, и Amazon, похоже, проигнорировала подпись в моей ссылке. Прочитав еще немного об этом, я увидел, что люди создают файл .pem, чтобы получить подписанные URL-адреса, работающие с CloudFront, но почему это не нужно для S3? Похоже, что метод getSignedUrl просто выполняет подписку с помощью секретного ключа AWS и ключа доступа AWS. Кто-нибудь получил настройку, подобную этой работе раньше?

Update: После дальнейших исследований выяснилось, что CloudFront обрабатывает сигнатуры URL, полностью отличные от S3 [link]. Тем не менее, я все еще не понимаю, как создать подписанный URL CloudFront с помощью Javascript.

Ответы

Ответ 1

Обновление: Я переместил функциональность подписи из приведенного ниже кода кода в aws-cloudfront-sign пакет на NPM, Таким образом, вы можете просто потребовать этот пакет и позвонить getSignedUrl().


После некоторого дальнейшего исследования я нашел решение, которое представляет собой комбинацию между этим ответом и методом, который я нашел в Библиотека Boto. Это правда, что подписи S3 URL обрабатываются иначе, чем подписи CloudFront URL. Если вам просто нужно подписать ссылку S3, тогда примерный код в моем первоначальном вопросе будет отлично работать для вас. Тем не менее, это становится немного сложнее, если вы хотите создать подписанные URL-адреса, которые используют ваш дистрибутив CloudFront. Это связано с тем, что подписи CloudFront в настоящее время не поддерживаются в SDK AWS, поэтому вам необходимо создать подпись самостоятельно. В случае, если вам также необходимо это сделать, вот основные шаги. Предположим, у вас уже есть настройка ведра S3:

Настройка CloudFront

  • Создайте дистрибутив CloudFront
  • Настройте исходный код со следующими настройками
    • Начало доменного имени: {your-s3-bucket}
    • Ограничить доступ к ведру: Да
    • Предоставление разрешений на чтение для ведра: да, обновить ведро.
  • Создать пару ключевых слов CloudFront. Должна быть в состоянии сделать это здесь.

Создать подписанный URL-адрес CloudFront

Чтобы отличить подписанный URL CloudFront, вам просто нужно подписать свою политику с помощью RSA-SHA1 и включить его в качестве параметра запроса. Подробнее о пользовательских политиках здесь, но я включил базовый пример в примерный код ниже, который должен вас запустить и запустить. Код примера для Node.js, но процесс может быть применен к любому языку.

var crypto = require('crypto')
  , fs = require('fs')
  , util = require('util')
  , moment = require('moment')
  , urlParse = require('url')
  , cloudfrontAccessKey = '<your-cloudfront-public-key>'
  , expiration = moment().add('seconds', 30)  // epoch-expiration-time

// Define your policy.
var policy = {
   'Statement': [{
      'Resource': 'http://<your-cloudfront-domain-name>/path/to/object',
      'Condition': {
         'DateLessThan': {'AWS:EpochTime': '<epoch-expiration-time>'},
      }
   }]
}

// Now that you have your policy defined you can sign it like this:
var sign = crypto.createSign('RSA-SHA1')
  , pem = fs.readFileSync('<path-to-cloudfront-private-key>') 
  , key = pem.toString('ascii')

sign.update(JSON.stringify(policy))
var signature = sign.sign(key, 'base64')

// Finally, you build the URL with all of the required query params:
var url = {
  host: '<your-cloudfront-domain-name>',
  protocol: 'http',
  pathname: '<path-to-s3-object>'
}    
var params = {
  'Key-Pair-Id=' + cloudfrontAccessKey,
  'Expires=' + expiration,
  'Signature=' + signature
}
var signedUrl = util.format('%s?%s', urlParse.format(url), params.join('&'))

return signedUrl

Ответ 2

Для моего кода для работы с кодом Jason Sims мне также пришлось преобразовать политику в base64 и добавить ее в окончательный signedUrl, например:

sign.update(JSON.stringify(policy))
var signature = sign.sign(key, 'base64')

var policy_64 = new Buffer(JSON.stringify(policy)).toString('base64'); // ADDED

// Finally, you build the URL with all of the required query params:
var url = {
  host: '<your-cloudfront-domain-name>',
  protocol: 'http',
  pathname: '<path-to-s3-object>'
}    
var params = {
  'Key-Pair-Id=' + cloudfrontAccessKey,
  'Expires=' + expiration,
  'Signature=' + signature,
  'Policy=' + policy_64  // ADDED 
}