CloudFront署名付きURL経由でS3にアップロードする
CloudFrontの署名付きURLを使えば、独自ドメイン経由でS3にアップロードできます。S3の直接の署名付きURLが使えない場合に有用です。
CloudFrontには署名付きURLを生成する機能があります。S3にも同様の機能がありますが、CloudFrontを使うと独自ドメイン経由でもアップロードできるため、ドメイン制限のある環境では特に有用です。
Serve private content with signed URLs and signed cookies

信頼された署名者の指定
信頼された署名者として使用するキーグループを作成します。
Specify signers that can create signed URLs and signed cookies
AWSアカウントを信頼された署名者として使用することも可能ですが、AWSはキーグループの使用を推奨しています。詳細はChoose between trusted key groups (recommended) and AWS accountsを参照してください。
キーペアは次の要件を満たす必要があります。
- タイプ: SSH-2 RSAキーペア
- フォーマット: Base64エンコードされたPEM
- キーサイズ: 2048ビット
次のコマンドでキーペアを作成します。
openssl genrsa -out private_key.pem 2048openssl rsa -pubout -in private_key.pem -out public_key.pem構築
- 公開鍵を
PublicKeyパラメータ(5行目)に渡し、それを使用します(38行目)。 - S3バケットポリシーが
s3:PutObjectアクションを許可していることを確認します(27行目)。 - AllViewerExceptHostHeaderオリジンリクエストポリシーを使用します(85行目)。
AWSTemplateFormatVersion: 2010-09-09Description: 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スタックをデプロイします。
PUBLIC_KEY=$(cat public_key.pem)aws cloudformation deploy \ --template-file template.yaml \ --stack-name cloudfront-presigned-urls-example \ --parameter-overrides PublicKey=$PUBLIC_KEYデプロイされたリソースを確認します。
aws cloudformation describe-stacks \ --stack-name cloudfront-presigned-urls-example \| jq ".Stacks[0].Outputs"テスト
次の変数を設定します。
CLOUDFRONT_DOMAIN=<CloudFront domain>KEYPAIR_ID=<Key pair ID>UTC_OFFSET=+9URLを生成します。
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=...ファイルをアップロードします。
echo 'Hello World' > example.txtcurl -X PUT -d "$(cat example.txt)" $PRESIGNED_URLファイルがアップロードされたことを確認します。
aws s3 cp s3://uploaded-files-<AWS::AccountId>-<AWS::Region>/upload-test.txt ./cat ./upload-test.txtクリーンアップ
次のコマンドで、この例でプロビジョニングしたすべてのAWSリソースをクリーンアップします。
CloudFrontディストリビューションの無効化には数分かかる場合があります。
aws s3 rm s3://uploaded-files-<AWS::AccountId>-<AWS::Region>/upload-test.txtaws cloudformation delete-stack --stack-name cloudfront-presigned-urls-exampleまとめ
信頼されたキーグループでCloudFrontのURLに署名し、それを使ってファイルをPUTしたところ、S3への直接の署名付きURLではなく、独自ドメイン経由でアップロードがS3に到達することがわかりました。これらのURLの署名に使うキーペアはAWSに一切触れません。CloudFrontが受け取るのはCloudFrontPublicKey経由の公開鍵だけで、秘密鍵はaws cloudfront signを実行する側の環境にとどまります。これが、AWSアカウントを署名者として使うよりも信頼されたキーグループのモデルが推奨される理由です。秘密鍵が漏洩しても、IAMの設定に触れることなくキーグループから外してローテーションできます。オリジンリクエストポリシー(AllViewerExceptHostHeader)とバケットポリシーのs3:PutObject条件は、どちらもディストリビューションのARNと一致していないとアップロードが成功しません。そのため、CloudFrontがURLを返したからといってそれが有効だと決めつけず、上で行ったように署名、PUT、S3からの読み戻しという一連の流れを通しでテストしておく価値があります。
Related posts
AWS S3 通知における s3:TestEvent のよくある落とし穴を避ける
S3 バケットにイベント通知を設定すると、S3 から s3:TestEvent メッセージが自動的に送信されます。このテストメッセージを適切に扱わないと、予期しない問題を引き起こすことがあります。
Cognito User PoolsとOIDCでSlackサインインを実装する
Cognito user poolをOIDC経由でSlackと連携させ、"Sign in with Slack"をAmplifyでNext.jsアプリに組み込みます。
Lambda Web AdapterでFastAPIをAWS Lambdaにデプロイする
Lambda Web Adapterを使うと、FastAPIで書いたAPIバックエンドをコンテナのまま単一のLambda関数にデプロイできます。
API Gateway WebSocket:モック統合の実装
バックエンドのLambdaを一切使わず、モック統合のみでAPI Gateway WebSocket APIを構築し、あらかじめ用意されたレスポンスを返します。
AWS EventBridge Scheduler:スケジュールに沿ってEC2を起動・停止する
Lambdaを介さずEventBridge SchedulerがEC2 APIを直接呼び出すことで、cronスケジュールに従ってEC2インスタンスを起動・停止します。
