はじめに
2022/8/25 に Amazon CloudFront で Origin Access Control (OAC) が使用可能になりました。OAC は CloudFront で S3 バケットをオリジンとする際の新たなアクセス制御方法です。
これまでオリジンの S3 バケットへのアクセスを CloudFront のみに限定するために Origin Access Identity (OAI) という機能が提供されていました。OAC は OAI と比較し、以下の点が強化されています。
-
セキュリティ
より短期間のクレデンシャルおよびローテーション、リソースベースポリシーなどのベストプラクティスが実装されています。 -
SSE-KMS を使用したサーバー側の暗号化のサポート
SSE-KMS で暗号化された S3 オブジェクトのダウンロードとアップロードをサポートします。これにより IAM ポリシーやキーポリシーによる追加のアクセス制御や暗号化マテリアルのローテーション、CloudTrail による証跡取得などの多くのメリットを追加で得ることができます。 -
S3 への動的リクエスト
GET だけではなく、POST や PUT など包括的な HTTP メソッドをサポートしています。 -
すべてリージョンでサポート
既存のリージョンでは OAI も引き続きサポートされますが、2022 年 12 月以降に開設される新規リージョンについては OAC のみがサポートされる予定です。
現在 OAI は Legacy 扱いとなっており、セキュリティのベストプラクティスや最新リージョンへの対応の観点から OAI から OAC の移行が推奨されています。以降では OAI を使用している既存の CloudFormation テンプレートを OAC に移行する際の修正点を紹介します。
サンプルのテンプレートは GitHub にもあげていますのでご参考まで。
CloudFormation テンプレートの修正例
既存の CloudFormation テンプレートを OAI から OAC に移行するには、最低限以下の修正が必要です。
- OAC の作成
- バケットポリシーの修正
- Distribution Origin の修正
SSE-KMS によるサーバー側の暗号化に対応させるためには追加の手順が必要になりますが、本記事では触れません。
OAC の作成
AWS::CloudFront::OriginAccessControl
を作成します。OriginAccessControlConfig の Description は任意項目です。
Resources:
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: Default Origin Access Control
Name: !Ref AWS::StackName
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
移行後は AWS::CloudFront::CloudFrontOriginAccessIdentity
は不要になりますので削除します。
バケットポリシーの修正
# S3 bucket policy to allow access from CloudFront OAI
AssetsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub ${AssetsBucket.Arn}/*
Principal:
AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}
# S3 bucket policy to allow access from CloudFront OAC
AssetsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub ${AssetsBucket.Arn}/*
Principal:
Service: cloudfront.amazonaws.com
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${AssetsDistribution}
ここでは OAI 用のポリシーを削除していますが、OAI と OAC 両方のポリシーを記載することが推奨される移行手順です。これにより OAC への移行中に CloudFront が S3 バケットへのアクセスを失うこと防ぐことができます。必要に応じて対応してください。
その際は以下のような流れになります。
- OAI および OAC 両方のバケットポリシーを設定したテンプレートで更新
- Distribution の変更完了後、OAI 用のポリシーを削除したテンプレートで再度更新
Distribution Origin の修正
OAI は Distribution Origin の S3OriginConfig を指定しますが、OAC では OriginAccessControlId を指定します。
AssetsDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: S3Origin
DomainName: !GetAtt AssetsBucket.DomainName
S3OriginConfig:
OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}
AssetsDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: S3Origin
DomainName: !GetAtt AssetsBucket.DomainName
S3OriginConfig:
OriginAccessIdentity: ''
OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id
2022/9/14 時点では S3OriginConfig で空の OriginAccessIdentity を指定する必要がありました。
S3OriginConfig を削除すると、スタック更新時に以下のようなエラーとなります。
Resource handler returned message: "Invalid request provided: Exactly one of CustomOriginConfig and S3OriginConfig must be specified"
修正後のテンプレート例
クリックして全体を表示
AWSTemplateFormatVersion: "2010-09-09"
Description: Static contents distribution using S3 and CloudFront.
Parameters:
CachePolicy:
Description: Change this if you want to specify a cache policy.
Type: String
Default: CachingOptimized
AllowedValues:
- CachingOptimized
- CachingDisabled
- CachingOptimizedForUncompressedObjects
- Elemental-MediaPackage
- Amplify
Mappings:
CachePolicyIds:
CachingOptimized:
Id: 658327ea-f89d-4fab-a63d-7e88639e58f6
CachingDisabled:
Id: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
CachingOptimizedForUncompressedObjects:
Id: b2884449-e4de-46a7-ac36-70bc7f1ddd6d
Elemental-MediaPackage:
Id: 08627262-05a9-4f76-9ded-b50ca2e3a84f
Amplify:
Id: 2e54312d-136d-493c-8eb9-b001f22f67d2
Resources:
# S3 bucket contains static contents
AssetsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LoggingConfiguration:
DestinationBucketName: !Ref AccessLogBucket
LogFilePrefix: origin/
# S3 bucket policy to allow access from CloudFront OAI
AssetsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub ${AssetsBucket.Arn}/*
Principal:
Service: cloudfront.amazonaws.com
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${AssetsDistribution}
- Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !Sub ${AssetsBucket.Arn}/*
- !GetAtt AssetsBucket.Arn
Condition:
Bool:
aws:SecureTransport: false
AccessLogBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
AccessControl: LogDeliveryWrite
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: Retain2yrs
Status: Enabled
ExpirationInDays: 730
Transitions:
- StorageClass: STANDARD_IA
TransitionInDays: 30
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
AccessLogBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AccessLogBucket
PolicyDocument:
Statement:
- Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !Sub ${AccessLogBucket.Arn}/*
- !GetAtt AccessLogBucket.Arn
Condition:
Bool:
aws:SecureTransport: false
# CloudFront Distribution for contents delivery
AssetsDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: S3Origin
DomainName: !GetAtt AssetsBucket.DomainName
S3OriginConfig:
OriginAccessIdentity: ''
OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id
Enabled: true
DefaultRootObject: index.html
Comment: !Sub ${AWS::StackName} distribution
DefaultCacheBehavior:
CachePolicyId: !FindInMap [ CachePolicyIds, !Ref CachePolicy , Id ]
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
HttpVersion: http2
ViewerCertificate:
CloudFrontDefaultCertificate: true
IPV6Enabled: false
Logging:
Bucket: !GetAtt AccessLogBucket.DomainName
IncludeCookies: false
Prefix: cloudfront/
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: Default Origin Access Control
Name: DefaultOAC
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
Outputs:
URL:
Value: !Join [ "", [ "https://", !GetAtt AssetsDistribution.DomainName ]]
移行してみた例
修正後のテンプレートを使用して、既存の CloudFormation スタックを更新してみます。
変更セットの内容が想定通りであることを確認してスタックを更新します。
前述のとおり、OAI 用のバケットポリシーを削除して OAC に移行するとディストリビューションの更新中は AccessDenied が発生しますのでご注意ください。
ディストリビューションの更新後、S3 バケットアクセスの設定が OAC に更新されていることが確認できます。
S3 バケットポリシーも想定通り更新されています。
ブラウザからのアクセスも問題無さそうです。
以上です。
参考になれば幸いです。