CloudFrontの署名付きURLを使用してプライベートなファイルを配信する

CloudFront 署名付きURLの利用

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html

CloudFrontの署名付きURLを利用することで、プライベートなファイルの配信ができる
署名付きURLを利用する場合は、URLにクエリ文字列として署名が付与され、その署名が検証できない限りは 403 Forbidden となる
URLを知っている人のみがアクセスでき、また署名付きURLには有効期限がある

署名付きURLの利用手順

  • 署名、検証のためのキーペアを作る

RSAキーペアを作る

openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem

キーペアは以下の制約がある

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html

  • SSH-2RSA キーペアである必要があります。
  • base64 エンコードされた PEM 形式である必要があります。
  • 2048 ビットのキーペアである必要があります。

最初4096ビットで作っていて署名に失敗してハマっていた
4096ビットの鍵で署名しようとすると以下のようなエラーになる

Your request contains empty/invalid/out of limits RSA Encoded Key
  • CloudFrontにパブリックキーを登録する

  • パブリックキーをCloudFrontキーグループに登録する

キーグループを作成しておくと、キーの更新が簡単になる
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#choosing-key-groups-or-AWS-accounts

  • CloudFrontでの配信URLを署名する
aws cloudfront sign \
  --url https://<distribution url>/<path> \
  --key-pair-id <key pair id> \
  --private-key file://private_key.pem \
  --date-less-than YYYYMMDDTHH:mm:dd+09:00

署名付きURLが生成される
このURLにブラウザなどでアクセスするとファイルが見えるはず
--date-less-than で署名の有効期限を設定でき、この日時を超えるとアクセスが無効になる
署名をする際は cloudfront:GetPublicKey ポリシーが必要

  • (おまけ)boto3で署名する
from botocore.signers import CloudFrontSigner
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding


def rsa_signer(message):
    private_key = serialization.load_pem_private_key(private_key_pem.encode(), password=None)
    return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1())


cloudfront_signer = CloudFrontSigner(cloudfront_key_id, rsa_signer)
url = f'https://{domain_name}/{path}'
expire_date = datetime.now() + timedelta(minutes=30)
signed_url = cloudfront_signer.generate_presigned_url(url, date_less_than=expire_date)

署名付きURLを利用するCloudFront環境をcdkで作る

リポジトリ: https://github.com/kefi550/private_s3_media_distributor/blob/main/lib/signing-s3-distribution-stack.ts

    // parameter store に 公開鍵を登録している
    const publicKey = ssm.StringParameter.fromStringParameterAttributes(this, 'PublicKey', {
      parameterName: props.signingPublicKeySsmParameterName,
    });

    // parameter storeの公開鍵からCloudFrontパブリックキーを作成
    const cloudfrontPublicKey = new cloudfront.PublicKey(this, 'CloudfrontPublicKey', {
      encodedKey: publicKey.stringValue,
    });

    // CloudFrontパブリックキーからキーグループを作成
    const keyGroup = new cloudfront.KeyGroup(this, 'KeyGroup', {
      items: [ cloudfrontPublicKey ],
    });

    // trusterdKeyGroups としてキーグループを指定してCloudFront distribution を作成
    // 署名されたURL以外でのアクセスを拒否するようになる
    const distribution = new cloudfront.Distribution(this, 'Distribution', {
      defaultBehavior: {
        origin: new cloudfrontOrigins.S3Origin(bucket),
        trustedKeyGroups: [ keyGroup ],
      },
      domainNames,
      certificate: cloudfrontCertificate,
    });