23
6

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

CloudFrontとS3で公開期間を指定したWebサイトを構築する

Last updated at Posted at 2023-07-13

はじめに

AWSで静的Webサイトを構築する際は、CloudFront + S3の構成が一般的かと思います。

S3のバケットポリシーとCloudFrontのカスタムエラーレスポンスを組み合わせて、
特定の期間内のみアクセスを受け付け、
期間外はエラーページを表示するサイトを構築しました。

AWS環境はTerraformで作成します。

参考:

実行環境

  • macOS Ventura 13.2.1
  • Terraform v1.5.2
  • aws-cli 2.9.12

環境構築

構成はCloudFrontディストリビューションが1つに、S3バケットが2つです。
1つのS3バケット(公開用バケット)に公開用ページを格納し、
もう1つ(エラー用バケット)にエラーページを格納します。

公開用ページは公開用バケットのapp_page/index.htmlに配置し、
エラーページはエラー用バケットのerror/error.htmlに配置します。

CloudFrontのオリジンには両方を指定します。

image.png

CloudFrontをマルチオリジンにする際の注意点はこちらを参考に

S3

まずはS3バケットの作成です。2つ用意します。

# 公開用
resource "aws_s3_bucket" "s3_bucket" {
  bucket = "open-bucket"
}

# エラー用
resource "aws_s3_bucket" "s3_standby_bucket" {
  bucket = "error-bucket"
}

次にS3のバケットポリシーを作成します。
ここで公開用バケットのポリシーにアクセスを許可する日時を指定することで、
期間外にアクセスした場合は403 forbiddenが返るようになります。

# 公開用
resource "aws_s3_bucket_policy" "s3_bucket" {
  bucket = aws_s3_bucket.s3_bucket.id
  policy = data.aws_iam_policy_document.s3_policy.json
}

data "aws_iam_policy_document" "s3_policy" {
  statement {
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.s3_bucket.arn}/*"]

    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn]
    }

    # アクセスを許可する期間を指定
    condition {
      test = "DateGreaterThan"
      variable = "aws:CurrentTime"
      # 標準時なので公開開始の時間-9時間にする
      values = ["2023-7-11T15:00:00Z"] # 公開開始 7/12 0:00
    }

    condition {
      test = "DateLessThan"
      variable = "aws:CurrentTime"
      values = ["2023-7-12T06:00:00Z"] # 公開終了 7/12 15:00
    }
  }
}

# エラー用
resource "aws_s3_bucket_policy" "s3_standby_bucket" {
  bucket = aws_s3_bucket.s3_standby_bucket.id
  policy = data.aws_iam_policy_document.s3_standby_policy.json
}

data "aws_iam_policy_document" "s3_standby_policy" {
  statement {
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.s3_standby_bucket.arn}/*"]

    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn]
    }
  }
}

CloudFront

OriginAccessIdentity

resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  comment = "origin access identity for s3"
}

ディストリビューション

まずは公開用の設定

公開ファイルのパスがapp_page/index.htmlなので、
default_root_objectindex.htmlに、
originブロックのorigin_path/app_pageにします。

その他の設定はご自身の環境に合わせてください。

resource "aws_cloudfront_distribution" "cf_dist" {
    comment = "cf-comment"
    default_root_object = "index.html"
    enabled             = "true"
    http_version        = "http2"
    is_ipv6_enabled     = "true"
    retain_on_delete    = "false"

    default_cache_behavior {
        allowed_methods = ["GET", "HEAD"]
        cached_methods  = ["GET", "HEAD"]
        compress        = "false"
        default_ttl     = "86400"
        
        forwarded_values {
          cookies {
            forward = "none"
          }
        
          query_string = "false"
        }
        
        max_ttl                = "31536000"
        min_ttl                = "0"
        smooth_streaming       = "false"
        target_origin_id       = aws_s3_bucket.s3_bucket.id
        viewer_protocol_policy = "redirect-to-https"
    }

    origin {
        domain_name = aws_s3_bucket.s3_bucket.bucket_regional_domain_name
        origin_id   = aws_s3_bucket.s3_bucket.id
        origin_path = "/app_page"
    
        s3_origin_config {
          origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path
        }
      }
    }

    restrictions {
        geo_restriction {
            restriction_type = "none"
        }
    }

    viewer_certificate {
        cloudfront_default_certificate = true
    }

    # 以下にエラーページ表示用の設定を追加

次にエラーページ表示用の設定

custom_error_responseブロックを追加し、
エラーコード403をキャッチしたら/error/error.htmlを返すようにします。
また、ordered_cache_behaviorブロックを追加します。
path_pattern/error/*を設定し、
/errorへのアクセスはエラー用バケットを見に行くようにします。

resource "aws_cloudfront_distribution" "cf_dist" {
    # 省略
    custom_error_response {
        error_caching_min_ttl = "0"
        error_code            = "403"
        response_code         = "200"
        response_page_path    = "/error/error.html"
    }

    origin {
        domain_name = aws_s3_bucket.s3_standby_bucket.bucket_regional_domain_name
        origin_id   = aws_s3_bucket.s3_standby_bucket.id
    
        s3_origin_config {
          origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path
        }
    }

    ordered_cache_behavior {
        allowed_methods = [
          "GET",
          "HEAD",
        ]
        cached_methods = [
          "GET",
          "HEAD",
        ]
        compress               = false
        default_ttl            = 86400
        max_ttl                = 86400
        min_ttl                = 0
        path_pattern           = "/error/*"
        smooth_streaming       = false
        target_origin_id       = aws_s3_bucket.s3_standby_bucket.id
        trusted_signers        = []
        viewer_protocol_policy = "https-only"
    
        forwarded_values {
          cookies {
            forward = "none"
          }
          query_string = "false"
        }
    }
}

AWSの設定は以上です。

S3にコンテンツを配置

公開用バケットの/app_page/index.html
エラーバケットのerror/error.htmlにそれぞれコンテンツを配置しましょう。

動作確認

CloudFrontのURLにアクセスすると、公開期間内には公開用バケットのコンテンツが表示され、
期間外はエラーページが表示されるはずです。

つまづきポイント

何回か失敗して試していると、CloudFrontのキャッシュが残っていて、
設定はあっているのに正常に表示されないことがあったので、
設定変更する度にキャッシュを削除することをおすすめします。

なお、公開開始前→開始後でキャッシュが残ってエラーページがずっと表示されてしまう、
といったことは心配しなくてよさそうです。
正常なレスポンスとエラーレスポンスのキャッシュは分けられているようなので、
デフォルトキャッシュのTTLに長い時間を設定していても問題ありません。
公開停止前→停止後の場合も同様です。

おわりに

イベントの期間だけアクセスできるようにしたい、
深夜の公開作業を自動で行いたい、など、
期間指定で公開したい場合って結構あるのではと思います。
参考になれば幸いです。

23
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
6