はじめに
Amazon Web Services(AWS)が提供する、Amazon CloudFront
や Amazon S3
と呼ばれるサービスを組み合わせることで、 HTMLやJavaScript、画像、ビデオなどで構成される静的Webサイトの配信基盤 を安価に構築することができます。本記事では、リソースのセットアップを自動で行うことのできる、AWS CloudFormation
を用いることで、これらの配信基盤を ミスなく迅速に構築 する手順をご説明します。なお、今回使用する CloudFormation テンプレートは、以下の GitHub リポジトリで公開しています。
TL;DR
以下の CloudFormation
テンプレートを実行することで、 静的Webサイトのホスティング基盤 を 迅速かつお手軽に実現 します。下にあるボタンをクリックすると、自身のAWSアカウント(Asia Pacific Tokyo - ap-northeast-1)で、この CloudFormation
テンプレートを実行することが可能となります。
作成されるAWSリソースとそのアーキテクチャ図はこちら。
この CloudFormation
テンプレートは、 Nested Stack
の構成となっており、下記のAWSサービスを単体で作成することも可能です。
作成されるAWSサービス | 個別のCloudFormationテンプレート | 作成されるサービス |
---|---|---|
Synthetics | 作成した運用基盤の 外形監視 を行います | |
Real-time Dashboard | CloudFront の アクセスログ を リアルタイムに解析 します | |
WAF | 作成した配信基盤に WAF を導入 します |
なお、 上記の Nested Stack のリソースの作成は必須ではなく 、これらが無くても正常に動作します。 静的Webサイトのホスティングに必要となるのは、 Amazon CloudFront と Amazon S3 のみ であり、本記事ではこの部分に焦点を当ててご説明します。
アーキテクチャ
このテンプレートの設定内容は以下の通りです。なお、以下のYAMLコードは、AWSCloudFormationTemplates/static-website-hosting-with-ssl で公開しているCloudFormationテンプレートから、本記事用に一部抜粋したものです。本記事作成用に一部改変を行なっていること、またリポジトリの最新のコードを常に反映している訳ではないことをご了承ください。詳細は、GitHubの当該リポジトリをご覧ください。
- README(EN) - aws-cloudformation-templates/static-website-hosting-with-ssl
- README(JP) - aws-cloudformation-templates/static-website-hosting-with-ssl
Amazon S3
Amazon S3は、ユーザがデータを安全にかつ容量無制限に保存可能な オブジェクトストレージ で、本アーキテクチャでは、 オリジンWebサーバ としての役割を担います。このS3の中に、HTMLやJavaScript、画像、ビデオなどの 静的ファイルが保存 されており、ユーザからのHTTPSリクエストに従って、これらのコンテンツが配信されます。
なお、S3自体に備わっている 静的Webサイトホスティング機能
を有効化することで、S3のみでWebサイトのホスティングを実現することも可能です。しかし、 S3に以下のような制限がある ことから、本テンプレートでは CloudFront と組み合わせて静的Webサイトのホスティングを実現 しています。
- S3単独では秒間数千リクエストを超える 大規模なトラフィックに耐えられない
- S3単独では カスタムドメインを用いたHTTPS通信が実現できない
- S3の権限設定ミスを悪用した Ghostwriter攻撃 を受ける可能性がある
S3バケットに関するCloudFormationテンプレートは以下の通りです。
Resource:
S3ForWebHosting:
Type: 'AWS::S3::Bucket'
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Properties:
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: example.com
LifecycleConfiguration:
Rules:
- Id: NonCurrentVersionExpiration
NoncurrentVersionExpirationInDays: 90
Status: Enabled
LoggingConfiguration:
DestinationBucketName: !Ref S3ForAccessLog
LogFilePrefix: S3-example.com/
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
バケット内が空である場合のみS3バケットの削除が可能となるため、S3バケットには、 UpdateReplacePolicy
および DeletionPolicy
を指定することで、 リソース置換時およびリソース削除時は、リソースを保持する 設定にしています。また、上述のように CloudFront と組み合わせて静的Webサイトホスティングを実現するため、 規定ACLを private
にした上で、 パブリックアクセスを全て禁止 にしています。これにより、 外部から直接S3バケットに対してアクセスされることはなく、ファイルの取得は全てCloudFrontを経由 します。
なお、のちほどご説明する Origin Access Identity
が、 AES256によるバケット暗号化のみをサポート していること、AWS の基本的なセキュリティのベストプラクティス標準 にて S3バケットのサーバサイド暗号化を求められている ことから、 AES256によるサーバサイド暗号化を有効化 しています。
Parameters:
DomainName:
Type: String
AllowedPattern: .+
Description: The CNAME attached to CloudFront [required]
Resources:
S3BucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref S3ForWebHosting
PolicyDocument:
Version: 2012-10-17
Id: !Ref S3ForWebHosting
Statement:
- Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}
Action:
- 's3:GetObject'
Resource: !Sub arn:aws:s3:::${S3ForWebHosting}/*
- Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !GetAtt S3ForWebHosting.Arn
- !Join
- ''
- - !GetAtt S3ForWebHosting.Arn
- /*
Condition:
Bool:
aws:SecureTransport: false
CloudFrontOriginAccessIdentity:
Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub access-identity-${DomainName}
S3のバケットポリシーでは、 Origin Access Identity を用いて CloudFrontユーザからの GetObject 操作のみを許可 しています。また、AWS の基本的なセキュリティのベストプラクティス標準 にて Secure Socket Layer を使用するためのリクエストが必要 とされているため、 SSLを用いないS3バケットへのアクセスを明示的に禁止 しています。
また、上述のS3バケットとは別に、 ログを蓄積するためのS3バケットを作成 します。このバケットの規定ACLには、 LogDeliveryWrite
を指定しています。
Resources:
S3ForAccessLog:
Type: 'AWS::S3::Bucket'
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Properties:
AccessControl: LogDeliveryWrite
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: !Sub logs-${AWS::Region}-${AWS::AccountId}
LifecycleConfiguration:
Rules:
- Id: ExpirationInDays
ExpirationInDays: 60
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
S3BucketPolicyForAccessLog:
Condition: CustomLogBucketName
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref S3ForAccessLog
PolicyDocument:
Version: 2012-10-17
Id: !Ref S3ForAccessLog
Statement:
- Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !GetAtt S3ForAccessLog.Arn
- !Join
- ''
- - !GetAtt S3ForAccessLog.Arn
- /*
Condition:
Bool:
aws:SecureTransport: false
本テンプレートで作成するS3バケットは、AWS の基本的なセキュリティのベストプラクティス標準 の以下の項目に準拠しています。
No. | ルール | 実行内容 |
---|---|---|
S3.1 | S3 バケットでは、パブリックアクセスブロック設定を有効にする必要があります | パブリックアクセスを禁止します |
S3.2 | S3 バケットでは、パブリック読み取りアクセスを禁止する必要があります | パブリックアクセスを禁止します |
S3.3 | S3 バケットでは、パブリック書き込みアクセスを禁止する必要があります | パブリックアクセスを禁止します |
S3.4 | S3 バケットでは、サーバー側の暗号化を有効にする必要があります | AES256によるサーバ側暗号化を行います |
S3.5 | S3 バケットでは、Secure Socket Layer を使用するためのリクエストが必要です | バケットボリシーを用いて、SSL通信以外のリクエストを拒否します |
Amazon CloudFront
CloudFrontは、コンテンツを低遅延、高速かつ安全に配信することが可能な **コンテンツ配信ネットワーク(CDN)**で、AWS Certificate Manager(ACM)で作成/インポートされた SSL/TLS証明書を簡単に紐付ける ことができます。
ユーザからのHTTPリクエストはまず最初にこのCloudFrontが受信し、 CloudFrontがキャッシュしていないコンテンツのみをオリジンWebサーバの役割を担うS3バケットに問い合わせるため、 S3バケットへのトラフィックを大幅に低減することが可能 です。
また、 GET/HEAD リクエストのみ許可 され、 かつS3バケットへは Origin Access Identity (OAI)と呼ばれる、特別なCloudFrontユーザからのリクエストのみが許可 されるため、 AWSリソースおよびS3バケット上のコンテンツは安全に保護 されます。
Parameters:
CertificateManagerARN:
Default: ''
Type: String
Description: The ARN of an SSL Certifiation attached to CloudFront
DomainName:
Type: String
AllowedPattern: .+
Description: The CNAME attached to CloudFront [required]
CloudFrontDefaultTTL:
Default: 86400
MinValue: 0
Type: Number
Description: CloudFront Default TTL [required]
CloudFrontMinimumTTL:
Default: 0
MinValue: 0
Type: Number
Description: CloudFront Minimum TTL [required]
CloudFrontMaximumTTL:
Default: 31536000
MinValue: 0
Type: Number
Description: CloudFront Maximum TTL [required]
Resources:
CloudFront:
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Aliases:
- !Ref DomainName
Comment: !Sub CDN for ${DomainName}
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
DefaultTTL: !Ref CloudFrontDefaultTTL
ForwardedValues:
QueryString: false
MaxTTL: !Ref CloudFrontMaximumTTL
MinTTL: !Ref CloudFrontMinimumTTL
SmoothStreaming: false
TargetOriginId: !Ref S3ForWebHosting
ViewerProtocolPolicy: redirect-to-https
DefaultRootObject : index.html
Enabled: true
HttpVersion: http2
IPV6Enabled: false
Logging:
Bucket: !GetAtt S3ForAccessLog.DomainName
Prefix: !Sub CloudFront-${DomainName}/
Origins:
- DomainName: !GetAtt S3ForWebHosting.DomainName
Id: !Ref S3ForWebHosting
S3OriginConfig:
OriginAccessIdentity: !Join
- ''
- - origin-access-identity/cloudfront/
- !Ref CloudFrontOriginAccessIdentity
ViewerCertificate:
AcmCertificateArn: !Ref CertificateManagerARN
MinimumProtocolVersion: TLSv1.1_2016
SslSupportMethod: sni-only
本テンプレートでは、 任意のカスタムドメイン とこれに 関連するSSL/TLS証明書 をCloudFrontディストリビューションに紐づけます。また、ディストリビューションが コンテンツをキャッシュする時間(TTL)を自由に指定 することが可能です。
以上で、Amazon S3 バケットと Amazon CloudFront ディストリビューションを用いた、 静的Webサイトの配信基盤を構築 することができました。 トラフィックの大部分はCloudFrontで処理 され、またCloudFrontとS3との間の通信に Origin Access Identity
が使用されるために、 オリジンWebサーバの役割を担うS3バケットと、その中に保存されている静的コンテンツは安全に保護 されます。また、任意のカスタムドメインを紐付けて、このドメインに対して HTTPS通信 を行うこともできます。これらのトラフィックの ログは、指定したS3バケットに一定期間保存 されます。
関連リンク
- ワンクリックで配信基盤を構築 - CloudFormation を用いて簡単Webサイトホスティング
- CloudFrontにWAFをアタッチ - CloudFormation を用いて簡単Webサイトホスティング
- 特定のURLを定期的にモニタリングする - CloudFormation を用いて簡単Webサイトホスティング
- CloudFrontのリアルタイムログをKibanaで可視化する - CloudFormation を用いて簡単Webサイトホスティング