CloudFront 署名付き URL で S3 にアップロードする方法
CloudFront は、署名付き URL の生成をサポートしています。 これを利用して、 S3 バケットにファイルをアップロードできます。
S3 も同様の機能を提供していますが、 CloudFront で利用しているカスタムドメインでファイルをアップロードできることが、重要な違いです(この投稿では触れません)。 これは、ドメインが制限された環境で特に役立ちます。
概要
署名者の指定
最初に、署名者として信頼されたキーグループを作成する必要があります。
キーペア作成
キーペアは、以下の要件を満たす必要があります。
- SSH-2 RSA key pair
- base64-encoded PEM format
- 2048-bit key pair
以下のコマンドで、新規キーペアを作成できます。
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
AWS リソース作成
以下の内容で CloudFormation テンプレートを作成してください。
重要なポイントは以下のとおりです。
- 公開鍵は
PublicKey
パラメータに渡される必要があります。(5行目、38行目) - S3 バケットポリシーは、
s3:PutObject
アクションを許可する必要があります。(27行目) - S3 バケットがクエリーストリングを必要とするため、 CloudFront origin request policy は、 AllViewerExceptHostHeader を指定する必要があります。(85行目)
AWSTemplateFormatVersion: 2010-09-09
Description: Example of CloudFront pre-signed URLs to upload files to S3 Bucket
Parameters:
PublicKey:
Type: String
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub uploaded-files-${AWS::AccountId}-${AWS::Region}
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Version: 2008-10-17
Id: PolicyForCloudFrontPrivateContent
Statement:
- Sid: AllowCloudFrontServicePrincipal
Effect: Allow
Principal:
Service: cloudfront.amazonaws.com
Action:
- s3:PutObject
Resource: !Sub ${S3Bucket.Arn}/*
Condition:
StringEquals:
"AWS:SourceArn": !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
CloudFrontPublicKey:
Type: AWS::CloudFront::PublicKey
Properties:
PublicKeyConfig:
Name: signer1
EncodedKey: !Ref PublicKey
CallerReference: cloudfront-caller-reference-example
CloudFrontKeyGroup:
Type: AWS::CloudFront::KeyGroup
Properties:
KeyGroupConfig:
Name: cloudfront-key-group-1
Items:
- !Ref CloudFrontPublicKey
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Name: !Ref S3Bucket
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
HttpVersion: http2and3
Origins:
- Id: !GetAtt S3Bucket.DomainName
DomainName: !GetAtt S3Bucket.DomainName
OriginAccessControlId: !Ref CloudFrontOriginAccessControl
S3OriginConfig:
OriginAccessIdentity: ''
DefaultCacheBehavior:
AllowedMethods:
- HEAD
- DELETE
- POST
- GET
- OPTIONS
- PUT
- PATCH
Compress: true
# CachingDisabled
# See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html#managed-cache-policy-caching-disabled
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
# AllViewerExceptHostHeader
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html#managed-origin-request-policy-all-viewer-except-host-header
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac
TargetOriginId: !GetAtt S3Bucket.DomainName
TrustedKeyGroups:
- !Ref CloudFrontKeyGroup
ViewerProtocolPolicy: https-only
Outputs:
CloudFrontDistributionDomainName:
Value: !GetAtt CloudFrontDistribution.DomainName
CloudFrontPublicKeyId:
Value: !Ref CloudFrontPublicKey
S3BucketName:
Value: !Ref S3Bucket
以下のコマンドで CloudFormation スタックをデプロイしてください。
公開鍵が、 PublicKey
パラメータに渡されていることにご注意ください。
PUBLIC_KEY=$(cat public_key.pem)
aws cloudformation deploy \
--template-file template.yaml \
--stack-name example-of-cloudfront-presigned-urls-to-upload-files-to-s3-bucket \
--parameter-overrides PublicKey=$PUBLIC_KEY
テスト用の値を確認するために、以下のコマンドを実行してください。
$ aws cloudformation describe-stacks \
--stack-name example-of-cloudfront-presigned-urls-to-upload-files-to-s3-bucket \
| jq ".Stacks[0].Outputs"
[
{
"OutputKey": "CloudFrontPublicKeyId",
"OutputValue": "<id>"
},
{
"OutputKey": "CloudFrontDistributionDomainName",
"OutputValue": "<id>.cloudfront.net"
},
{
"OutputKey": "S3BucketName",
"OutputValue": "uploaded-files-<AWS::AccountId>-<AWS::Region>"
}
]
テスト
S3 バケットにファイルをアップロードするための署名付き URL を生成するために、 CLOUDFRONT_DOMAIN
, KEYPAIR_ID
, UTC_OFFSET
変数に実際の値をセットしてください。
CLOUDFRONT_DOMAIN=
KEYPAIR_ID=
UTC_OFFSET=+9
次に、以下のコマンドを実行してください。署名付き URL が生成されます。
PRESIGNED_URL=$(aws cloudfront sign \
--url https://$CLOUDFRONT_DOMAIN/upload-test.txt \
--key-pair-id $KEYPAIR_ID \
--private-key file://private_key.pem \
--date-less-than $(date -v +5M "+%Y-%m-%dT%H:%M:%S$UTC_OFFSET"))
echo $PRESIGNED_URL
# https://<distribution-id>.cloudfront.net/upload-test.txt?Expires=...&Signature=...Key-Pair-Id=...
署名付き URL を利用してファイルをアップロードするために、以下のコマンドを実行してください。
PUT
でなければならない点にご注意ください。 echo 'Hello World' > example.txt
curl -X PUT -d "$(cat example.txt)" $PRESIGNED_URL
最後に、 S3 バケットにアップロードされたファイルを確認してください。
aws s3 cp s3://uploaded-files-<AWS::AccountId>-<AWS::Region>/upload-test.txt ./
cat ./upload-test.txt
# Hello World
rm ./upload-test.txt
クリーンアップ
以下のコマンドを使用して、プロビジョニングされた AWS リソースを削除してください。
aws s3 rm s3://uploaded-files-<AWS::AccountId>-<AWS::Region>/upload-test.txt
aws cloudformation delete-stack --stack-name example-of-cloudfront-presigned-urls-to-upload-files-to-s3-bucket
まとめ
ドメインが制限された環境で S3 バケットにファイルをアップロードしたい場合、 CloudFront 署名付き URL が有用です。
この投稿が、お役に立てば幸いです。