この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
静的コンテンツを配信するためにCloudFrontとS3を利用した際、OACを利用したのでブログに残しておきます。
OACとは
CloudFrontのオリジンにS3を指定する際、S3へのアクセスをCloudFrontからだけに制限するための設定です。
S3へのアクセス制限はOAIという機能でも可能ですがOACでは以下のものに対応しています。
Amazon S3 オリジンへのアクセスの制限
AWS KMS による Amazon S3 サーバー側の暗号化 (SSE-KMS)
Amazon S3 に対する動的なリクエスト (POST、PUT など)
詳しくは以下のブログとドキュメントをご確認ください。
作成したCloudFormationテンプレート
以下のテンプレートをデプロイするとCloudFrontとS3が作成されます。
今回はS3とCloudFrontでテンプレートを分割しました。
デプロイをする際は順番があるのですぐに試したい場合はデプロイに飛んでください。
S3のテンプレート
S3バケットはKMS暗号化を使用して作成しました。
AWS::KMS::Key
AWS::S3::Bucket
AWSTemplateFormatVersion: "2010-09-09"
Description: S3&KMS Stack
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
BucketName:
Type: String
IAMUserARN:
Type: String
Resources:
# ------------------------------------------------------------#
# KMS
# ------------------------------------------------------------#
KMS:
Type: AWS::KMS::Key
Properties:
Description: OAC Test
Enabled: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action: 'kms:*'
Resource: '*'
- Sid: 'Allow access for Key Administrators'
Effect: Allow
Principal:
AWS: !Ref IAMUserARN
Action:
- 'kms:Create*'
- 'kms:Describe*'
- 'kms:Enable*'
- 'kms:List*'
- 'kms:Put*'
- 'kms:Update*'
- 'kms:Revoke*'
- 'kms:Disable*'
- 'kms:Get*'
- 'kms:Delete*'
- 'kms:TagResource'
- 'kms:UntagResource'
- 'kms:ScheduleKeyDeletion'
- 'kms:CancelKeyDeletion'
- 'kms:CreateGrant'
- 'kms:ListGrants'
- 'kms:RevokeGrant'
Resource: '*'
- Sid: 'Allow use of the key'
Effect: Allow
Principal:
AWS: !Ref IAMUserARN
Action:
- 'kms:Encrypt'
- 'kms:Decrypt'
- 'kms:ReEncrypt*'
- 'kms:GenerateDataKey*'
- 'kms:DescribeKey'
Resource: '*'
- Sid: 'Allow use of the key'
Effect: Allow
Principal:
Service:
- cloudfront.amazonaws.com
Action:
- 'kms:Decrypt'
- 'kms:Encrypt'
- 'kms:GenerateDataKey*'
Resource: '*'
Condition:
StringEquals:
aws:SourceArn:
- !Join
- ''
- - !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/'
- !ImportValue CloudFrontID
PendingWindowInDays: 7
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
S3:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- BucketKeyEnabled: true
ServerSideEncryptionByDefault:
KMSMasterKeyID: !Ref KMS
SSEAlgorithm: aws:kms
BucketName: !Ref BucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3
PolicyDocument:
Version: "2008-10-17"
Statement:
- Sid: "AllowCloudFrontServicePrincipal"
Effect: "Allow"
Principal:
Service:
- "cloudfront.amazonaws.com"
Action:
- "s3:GetObject"
Resource:
- !Sub ${S3.Arn}/*
Condition:
StringEquals:
AWS:SourceArn:
- !Join
- ''
- - !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/'
- !ImportValue CloudFrontID
Outputs:
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
S3:
Value: !GetAtt S3.DomainName
Export:
Name: S3
CloudFrontのテンプレート
CloudFrontでS3をオリジンにする際は「CustomOriginConfig」(静的Webサイトホスティングを設定している場合) もしくは「S3OriginConfig」の設定が必要です。
今回は静的Webサイトホスティングではないので「S3OriginConfig」を設定しています。
また「S3OriginConfig」の中にある「OriginAccessIdentity」は空の要素を指定してオリジン アクセスIDを利用しない状態を作っています。
OAIは使用しませんが、記載しないとエラーになります。
AWS::CloudFront::Distribution
AWS::CloudFront::OriginAccessControl
AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFront Stack
Resources:
# ------------------------------------------------------------#
# CloudFront
# ------------------------------------------------------------#
OAC:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: Access Control
Name: OAC
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
CloudFront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
TargetOriginId: S3
ViewerProtocolPolicy: allow-all
Enabled: true
Origins:
- DomainName: !ImportValue S3
Id: S3
OriginAccessControlId: !GetAtt OAC.Id
S3OriginConfig:
OriginAccessIdentity: ''
PriceClass: PriceClass_200
Outputs:
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
CloudFrontID:
Value: !Ref CloudFront
Export:
Name: CloudFrontID
デプロイ
デプロイはAWS CLIを使用して行いました。
まずはS3をデプロイします。
S3を最初にデプロイする際は以下のようにKMSキーポリシーのCloudFrontに関係する部分とS3バケットポリシーをコメントへ変更しておきます。
理由としてはCloudFrontのARNを指定しているためコメントにしていない場合、作成時にエラーが発生するためです。
AWSTemplateFormatVersion: "2010-09-09"
Description: S3&KMS Stack
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
BucketName:
Type: String
IAMUserARN:
Type: String
Resources:
# ------------------------------------------------------------#
# KMS
# ------------------------------------------------------------#
KMS:
Type: AWS::KMS::Key
Properties:
Description: OAC Test
Enabled: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action: 'kms:*'
Resource: '*'
- Sid: 'Allow access for Key Administrators'
Effect: Allow
Principal:
AWS: !Ref IAMUserARN
Action:
- 'kms:Create*'
- 'kms:Describe*'
- 'kms:Enable*'
- 'kms:List*'
- 'kms:Put*'
- 'kms:Update*'
- 'kms:Revoke*'
- 'kms:Disable*'
- 'kms:Get*'
- 'kms:Delete*'
- 'kms:TagResource'
- 'kms:UntagResource'
- 'kms:ScheduleKeyDeletion'
- 'kms:CancelKeyDeletion'
- 'kms:CreateGrant'
- 'kms:ListGrants'
- 'kms:RevokeGrant'
Resource: '*'
- Sid: 'Allow use of the key'
Effect: Allow
Principal:
AWS: !Ref IAMUserARN
Action:
- 'kms:Encrypt'
- 'kms:Decrypt'
- 'kms:ReEncrypt*'
- 'kms:GenerateDataKey*'
- 'kms:DescribeKey'
Resource: '*'
# - Sid: 'Allow use of the key'
# Effect: Allow
# Principal:
# Service:
# - cloudfront.amazonaws.com
# Action:
# - 'kms:Decrypt'
# - 'kms:Encrypt'
# - 'kms:GenerateDataKey*'
# Resource: '*'
# Condition:
# StringEquals:
# aws:SourceArn:
# - !Join
# - ''
# - - !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/'
# - !ImportValue CloudFrontID
PendingWindowInDays: 7
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
S3:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- BucketKeyEnabled: true
ServerSideEncryptionByDefault:
KMSMasterKeyID: !Ref KMS
SSEAlgorithm: aws:kms
BucketName: !Ref BucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
# S3BucketPolicy:
# Type: AWS::S3::BucketPolicy
# Properties:
# Bucket: !Ref S3
# PolicyDocument:
# Version: "2008-10-17"
# Statement:
# - Sid: "AllowCloudFrontServicePrincipal"
# Effect: "Allow"
# Principal:
# Service:
# - "cloudfront.amazonaws.com"
# Action:
# - "s3:GetObject"
# Resource:
# - !Sub ${S3.Arn}/*
# Condition:
# StringEquals:
# AWS:SourceArn:
# - !Join
# - ''
# - - !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/'
# - !ImportValue CloudFrontID
Outputs:
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
S3:
Value: !GetAtt S3.DomainName
Export:
Name: S3
1. S3作成
以下のコマンドを実行します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=BucketName,ParameterValue=S3バケット名 ParameterKey=IAMUserARN,ParameterValue=IAMユーザのARN
2. CloudFront作成
S3のスタックが完成したらCloudFrontを作成していきます。
CloudFrontのテンプレートは編集せずに以下のコマンドを実行します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名
3. S3バケットポリシーとKMSポリシー追加
CloudFrontの作成が完了したらコメントに変更していたS3バケットポリシーとKMSポリシーの部分を戻して以下のコマンドを実行します。
aws cloudformation create-change-set --change-set-name 変更セット名 --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=BucketName,ParameterValue=S3バケット名 ParameterKey=IAMUserARN,ParameterValue=IAMユーザのARN
aws cloudformation execute-change-set --change-set-name 変更セットのARN
4. 動作確認
以下のHTMLファイルをS3にアップロードします。
<html>
<head>
<meta charset="UTF-8">
<title>テストページ</title>
</head>
<body bgcolor="#10100E" text="#cccccc">
小林 陸<br/>
</body>
</html>
S3へのアップロードは以下のコマンドを実行します。
test.htmlはアップロードするHTMLファイル名になります。
aws s3 cp ./test.html s3://バケット名
ファイルアップロード後CloudFrontのドメイン名でブラウザからアクセスすると以下のようにページが表示されます。
オブジェクトURLに直接アクセスしても表示されないことも確認できます。
テンプレート説明
今回作成したCloudFormationテンプレートで重要な部分を説明します。
CloudFrontのテンプレートでは9行目から記載しているOACを作成している部分と36行目のOACを指定している部分が今回メインになります。
OACの作成は「AWS::CloudFront::OriginAccessControl」というものを使用して作成します。
各パラメータでOACの名前、オリジンのタイプ、オリジンへのリクエストに署名をする設定、署名方法を設定しています。
少し内容がずれますが、普段AWSのAPIを利用するときは裏側でSigV4というもので署名を行いAPIリクエスト実行者の認証、承認を行っています。
この機能を使ってCloudFrontからS3オリジンへのアクセスに認証、承認、拒否を行いアクセスを制御しています。
署名についての詳細は以下のドキュメントをご確認ください。
署名バージョン 4 の署名プロセス
Authenticating Requests (AWS Signature Version 4)
# OAC作成部分
OAC:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: Access Control
Name: OAC
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
# OAC指定部分
OriginAccessControlId: !GetAtt OAC.Id
S3のテンプレートではKMSキーポリシーとS3バケットポリシーが重要な部分になっています。
キーポリシーではCloudFrontのプリンシパルを指定してKMSにアクセスできるようにしています。
S3バケットポリシーも同様にCloudFrontからアクセスできるようにプリンシパルで指定しています。
# キーポリシー抜粋
- Sid: 'Allow use of the key'
Effect: Allow
Principal:
Service:
- cloudfront.amazonaws.com
Action:
- 'kms:Decrypt'
- 'kms:Encrypt'
- 'kms:GenerateDataKey*'
Resource: '*'
Condition:
StringEquals:
aws:SourceArn:
- !Join
- ''
- - !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/'
- !ImportValue CloudFrontID
# S3バケットポリシー
- Sid: "AllowCloudFrontServicePrincipal"
Effect: "Allow"
Principal:
Service:
- "cloudfront.amazonaws.com"
Action:
- "s3:GetObject"
Resource:
- !Sub ${S3.Arn}/*
Condition:
StringEquals:
AWS:SourceArn:
- !Join
- ''
- - !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/'
- !ImportValue CloudFrontID
さいごに
今回はCloudFrontのOACをCloudFormationで作成してみました。
CloudFormationではなくマネジメントコンソールからの作成の場合、バケットポリシーをコピーすることが可能なので簡単に設定することができます。
以下のドキュメントにOAIからOACへ移行する際のサンプルバケットポリシーが記載されていますので移行を検討の際はご確認ください。