generate_presigned_url boto3 генерирует один и тот же URL-адрес до истечения срока действия при вызове с помощью cloudfront

#amazon-web-services #aws-lambda #boto3 #amazon-cloudfront #aws-lambda-edge

Вопрос:

Я пытаюсь сгенерировать URL-адрес под председательством s3. Логика для этого написана в Lambda@Edge, python 3.7

 def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    s3 = boto3.client('s3',config=Config(signature_version='s3v4'))
    url = s3.generate_presigned_url(ClientMethod='get_object',
                                    Params={'Bucket': 'BUCKET_NAME',
                                            'Key':'abc.jpeg'
                                           },
                                    ExpiresIn=3600)
    response = {
     'status': '302',
     'statusDescription': 'Found',
     'headers': {
         'location': [{
             'key': 'Location',
             'value': url
         }]
     }
     }
    return response
 

Когда я тестирую этот код в простой Лямбде, я получаю разные URL-адреса для каждого вызова. Но если я добавлю cloudfront и вызову доменное имя cloudfront, то тот же URL-адрес будет сгенерирован до истечения срока действия URL-адреса.
Почему я не получаю разные URL-адреса для каждого вызова, как в случае 1?

Комментарии:

1. Не могли бы вы показать свою процедуру генерации URL-адресов с предварительной подписью CloudFront? Я получаю разные URL-адреса просто отлично, используя Boto3.

Ответ №1:

Вам нужно создать подписывающее лицо для CloudFront и использовать generate_presigned_url его от подписывающего лица. Этот фрагмент кода генерирует разные URL-адреса каждый раз, когда я пытаюсь.

 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
from datetime import datetime, timedelta

from botocore.signers import CloudFrontSigner
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding

from cf_url_gen.config import Bucket, Operation, config


class CFClient(object):
    """CloudFront client class

    Creates a CloudFront client object for generting S3 resource URLs.
    """

    def __init__(
        self,
        cf_key_id: str = os.getenv("CF_KEY_ID"),
        cf_key_path: str = os.getenv("CF_KEY_PATH"),
        cf_ttl_sec: int = 300,
    ):
        """The __init__ method for CFClient class

        Args:
            cf_key_id (str, optional): CloudFront key id, can be set via environment variable `CF_KEY_ID`
            cf_key_path (str, optional): CloudFront key path, can be set via environment variable `CF_KEY_PATH`
            cf_ttl_sec (int, optional): URL validity duration from UTC timestamp, default value 300 seconds
        """
        with open(cf_key_path, "rb") as key_file:
            _key = serialization.load_pem_private_key(key_file.read(), password=None, backend=default_backend())
            self.signer = CloudFrontSigner(cf_key_id, lambda m: _key.sign(m, padding.PKCS1v15(), hashes.SHA1()))
        self.ttl = cf_ttl_sec

    def get_url_with_prefix(self, file_path: str, url_prefix: str, is_signed: bool = True) -> str:
        """Method for generating CloudFront URLs for file paths with given url prefix

        Args:
            file_path (str): Full file path in S3 bucket excluding bucket name
            url_prefix (str): URL prefix to use for signing or generating S3 resource URL
            is_signed (bool): Flag indicating whether URL should be signed or not

        Returns:
            CloudFront url string based on provided signing options
        """
        if not is_signed:
            return os.path.join(url_prefix, file_path)
        else:
            end_date = datetime.utcnow()   timedelta(seconds=self.ttl)
            file_url = os.path.join(url_prefix, file_path)
            return self.signer.generate_presigned_url(file_url, date_less_than=end_date)
 

Примечание. Префиксом URL-адреса должен быть URL-адрес домена CloudFront для вашей корзины S3, обычно что-то вроде https://bucketname.yourdomain.com или что-то подобное.