この記事は個人ブログと同じ内容です
AWS の S3 にホスティングした静的 WEB サイトを CloudFront を用いて CDN での配信を行うと共に、SSL 証明書等の整備も行って独自ドメインを HTTPS でアクセスできるようにしていきます。
なお、S3 にホスティングした WEB サイトを独自ドメイン& HTTPS でアクセスできるようにするにも同じで、今回のように CloudFront を用いて構築する必要があり、手段手順としては同じになります。
S3 にホスティングした静的 WEB サイト
今回は CloudFront や SSL/TLS 証明書回りがメインです。
S3 に静的 WEB サイトをホスティングする部分については前回の記事にまとめています。
今回は前回の記事の続きになるので、環境変数周りや Route53 や S3 の設定等もこちらを確認してください。
実行環境
今回の実行環境は以下になります。
- Terraform v0.15.1
コードはモジュール化を行わずに一つの tf ファイルに書いていきます。
AWS プロバイダの追加
まずは aws プロバイダを追加します。
既に前回で東京リージョンの aws プロバイダを作成済みですが、ACM で作成した SSL/TLS 証明書を CloudFront にアタッチすためには証明書をバージニア北部リージョンで作成する必要があるため(2021/05/04現在)、別途こちらの aws プロバイダを作成しておきます。
main.tf
provider "aws" { access_key = var.aws_access_key secret_key = var.aws_secret_key region = "us-east-1" alias = "virginia" }
リージョンをバージニア北部にし、エイリアスを付けてデフォルトとは別にこちらを指定して使用できるようにしています。
ログ用の S3 バケット作成
CloudFront のアクセスログを格納するバケットを S3 に作成します。
main.tf
## S3 for cloudfront logs resource "aws_s3_bucket" "cloudfront_logs" { bucket = "${local.fqdn.name}-cloudfront-logs" acl = "private" tags = { Name = var.project_tag_name Environment = var.project_environment_name } }
CloudFront ディストリビューション作成
CloudFront のディストリビューションを作成します。
main.tf
## キャッシュポリシー data "aws_cloudfront_cache_policy" "managed_caching_optimized" { name = "Managed-CachingOptimized" } ## ディストリビューション resource "aws_cloudfront_distribution" "main" { origin { domain_name = "${local.bucket.name}.s3-${var.aws_region}.amazonaws.com" origin_id = "S3-${local.fqdn.name}" } enabled = true is_ipv6_enabled = true default_root_object = "index.html" # Alternate Domain Names (CNAMEs) aliases = [local.fqdn.name] # 証明書の設定 viewer_certificate { cloudfront_default_certificate = false acm_certificate_arn = aws_acm_certificate.main.arn minimum_protocol_version = "TLSv1.2_2019" ssl_support_method = "sni-only" } retain_on_delete = false logging_config { include_cookies = true bucket = "${aws_s3_bucket.cloudfront_logs.id}.s3.amazonaws.com" prefix = "log/" } default_cache_behavior { allowed_methods = ["GET", "HEAD"] cached_methods = ["GET", "HEAD"] target_origin_id = "S3-${local.fqdn.name}" viewer_protocol_policy = "allow-all" compress = true cache_policy_id = data.aws_cloudfront_cache_policy.managed_caching_optimized.id } restrictions { geo_restriction { restriction_type = "none" } } }
- キャッシュポリシー
- 今回は特に細かく設定する必要がないためマネージドのものを使用しています。
- 管理キャッシュポリシーの使用
- ディストリビューション
SSL/TLS 証明書 作成
ACM(AWS Certificate Manager) にて SSL/TLS 証明書を作成し、独自ドメインに紐づけていきます。
証明書のリクエスト
SSL/TLS 証明書の発行を ACM にリクエストします。
main.tf
resource "aws_acm_certificate" "main" { provider = aws.virginia domain_name = local.fqdn.name validation_method = "DNS" }
CNAME レコード 作成
証明書リクエストに対して DNS で検証を行なうため、CNAME レコードを追加します。
main.tf
## CNAME レコード resource "aws_route53_record" "main_acm_c" { for_each = { for d in aws_acm_certificate.main.domain_validation_options : d.domain_name => { name = d.resource_record_name record = d.resource_record_value type = d.resource_record_type } } zone_id = data.aws_route53_zone.naked.id name = each.value.name type = each.value.type ttl = 172800 records = [each.value.record] allow_overwrite = true }
証明書の検証
証明書のリクエストに CNAME レコードを連携させ、DNS 検証の完了を確認します。
main.tf
## ACM 証明書 / CNAME レコード 連携 resource "aws_acm_certificate_validation" "main" { provider = aws.virginia certificate_arn = aws_acm_certificate.main.arn validation_record_fqdns = [for record in aws_route53_record.main_acm_c : record.fqdn] }
独自ドメインの向き先を変更する
最後に、前回までは S3 に向いていた独自ドメインの向き先を CloudFront に変更します。
main.tf
## A レコード(to CloudFront) resource "aws_route53_record" "main_cdn_a" { zone_id = data.aws_route53_zone.naked.zone_id name = local.fqdn.name type = "A" alias { evaluate_target_health = true name = aws_cloudfront_distribution.main.domain_name zone_id = aws_cloudfront_distribution.main.hosted_zone_id } }
CloudFront へ A レコードを設定しました。
前回の記事で S3 に向けた A レコードは削除します。
## 削除 ## A レコード(to S3 Bucket) //resource "aws_route53_record" "main_a" { // zone_id = data.aws_route53_zone.naked.zone_id // name = local.fqdn.name // type = "A" // alias { // evaluate_target_health = true // name = "s3-website-${var.aws_region}.amazonaws.com" // zone_id = aws_s3_bucket.app.hosted_zone_id // } //}
動作確認
ここまでで terraform から環境を構築すると、CloudFront 及び HTTPS でのアクセスが可能となっている事が確認できます。
HTTPS でのみのアクセスにする
現時点では http と https どちらでもアクセス可能な状態なので、http でアクセスがきたものは https にリダイレクトするようにします。
main.tf
# allow-all を redirect-to-https に変更する # viewer_protocol_policy = "allow-all" viewer_protocol_policy = "redirect-to-https"
aws_cloudfront_distribution リソースの default_cache_behavior にある viewer_protocol_policy の値を allow-all から redirect-to-https へ変更します。
これで http でアクセスがきたものは https にリダイレクトされるようになります。
S3 へのアクセスを制限する
訪問者が S3 のバケットへ直接アクセスしないように制限を掛け、CloudFront ディストリビューションを介してのみアクセスできるようにします。
Origin Access Identity(OAI)作成
Origin Access Identity(以下、OAI)を作成します。
main.tf
## CloudFront OAI 作成 resource "aws_cloudfront_origin_access_identity" "main" { comment = "Origin Access Identity for s3 ${local.bucket.name} bucket" }
OAI をディストーションに関連付ける
OAI で作成した CloudFront ユーザーをディストリビューションに関連付けます。
main.tf
resource "aws_cloudfront_distribution" "main" { origin { # 追加 s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.main.cloudfront_access_identity_path } } . . .
aws_cloudfront_distribution リソースの origin に s3_origin_config として origin_access_identity を設定します。
IAMポリシードキュメント作成
アクセスを制御するポリシードキュメントを作成します。(ここで作成したポリシーを json としてアタッチする)
main.tf
# IAMポリシードキュメント作成 data "aws_iam_policy_document" "s3_policy" { statement { actions = ["s3:GetObject"] resources = ["${aws_s3_bucket.app.arn}/*"] principals { identifiers = [aws_cloudfront_origin_access_identity.main.iam_arn] type = "AWS" } } }
ポリシーをバケットにアタッチ
作成したポリシーをアプリケーションのバケットに紐付けます。
main.tf
# ポリシーをバケットに紐付け resource "aws_s3_bucket_policy" "main" { bucket = aws_s3_bucket.app.id policy = data.aws_iam_policy_document.s3_policy.json }
アプリケーションのバケットへのアクセス制御変更
前回の記事で作成したアプリケーションを配置するバケットでは、PublicRead が ON になっている状態です。これをプライベートに変更します。
main.tf
## S3 for Static Website Hosting resource "aws_s3_bucket" "app" { bucket = local.bucket.name acl = "private" website { index_document = "index.html" error_document = "error.html" } tags = { Name = var.project_tag_name Environment = var.project_environment_name } }
これで、作成した OAI での CloudFront からのアクセスのみ S3 バケットへのアクセスが可能になりました。
S3 アプリケーションログ用のバケット削除
この時点で S3 へのアクセスは全て CloudFront 経由になり、S3 への訪問者のアクセスログのバケットは不要になるので、前回の記事で作成したバケットは削除します。
main.tf
# 削除 //data "aws_canonical_user_id" "current_user" {} # 削除 ## S3 for app logs ## https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket //resource "aws_s3_bucket" "app_logs" { // bucket = "${local.bucket.name}-logs" // // grant { // id = data.aws_canonical_user_id.current_user.id // permissions = ["FULL_CONTROL"] // type = "CanonicalUser" // } // grant { // permissions = ["READ_ACP", "WRITE"] // type = "Group" // uri = "http://acs.amazonaws.com/groups/s3/LogDelivery" // } // // tags = { // Name = var.project_tag_name // Environment = var.project_environment_name // } //}
動作確認
ここまでの設定を適用させると、ブラウザから S3 の URL でコンテンツへアクセスできなくなった事が確認できました。
まとめ
静的な WEB を公開するのであればコスト面で優れている S3 へのホスティングはおすすめです。
また、CloudFront も使うと高速にコンテンツを配信する、独自ドメインを HTTPS 化するなど、できる幅も広がるのでおすすめです。