Theme
SD MILIEU

2023-5-20

S3+CloudFrontで静的WEBサイトをホスティングする際のメモ

www.sd-milieu.net をNetlifyからS3へ移行したのでやった作業をまとめておく。

実際に行った大まかな内容

  • S3にバケットを作成し、静的WEBサイト用として設定。ファイルをアップロードする
    • この時点で、S3のURLだがHTTPではアクセスできるようになっている
  • Route53をDNSとして利用できるようにする
    • Route53にて sd-milieu.net のホストゾーンを作成
    • ドメインプロバイダ(今回はバリュードメイン)のネームサーバーに、Route53のネームサーバーを設定
  • 後工程で必要になるので、ACMにてSSL証明書の作成
    • ACMにて証明書リクエストを行う。リージョンはバージニア北部(us-east-1)である必要があるので注意。
    • 項目「ドメイン」の「Route53でレコードを作成」を押下し、DNSレコードに検証用のCNAMEレコードを追加する
    • しばらく待ってステータスが「成功」となればOK
  • CloudFront提供のURL XXX.cloudfront.net でアクセスできるようにし、CDNを経由するようにする
    • ディストリビューションの作成。オリジンはS3をWebサイトURLを指定
    • 「代替ドメイン名 (CNAME)」に www.sd-milieu.net を指定
    • カスタムSSL証明書に先程ACMにて作成した証明書を指定
  • Route53にて、エイリアスレコードを作成し www.sd-milieu.net がCloudFrontへ向くようにする
  • 他、後述の細かい作業
    • S3のURLからアクセスされないよう小細工をしたり
    • 自動デプロイのためにGitHubActionsの設定をしたり
    • ルートドメインにアクセスされた際に、リダイレクトするようにする

S3への直接アクセスを防ぎたい場合

以下の要件を全て満たしたいケースを想定

  1. https://example.com/https://example.com/about/ がリクエストされた際に index.htmlabout/index.html を返却する
  2. S3バケットへの直接アクセスはブロックする

index.html の返却をさせようとすると、S3の静的ウェブサイトホスティング機能を使う必要がある。これを利用すると、チェックボックスをオンにするだけで index.html の返却をしてくれる。 ただ、CloudFrontと併用しようとすると、OAIを有効にすることが出来ず、公開されているS3バケットへ直接アクセスすることが可能になってしまう。

なので、対応方法としては大きく以下の3パターンに分かれる

  1. S3バケットへの直接アクセスを許容する
    • そもそも静的WEBサイトとして公開しているものなので、直接アクセスされたところでという話
    • 懸念点として、検索エンジンのクローラーにアクセスされた結果インデックスされてしまうという問題はある。なので基本的に避けたほうがいいかと思う
  2. バケットポリシーで、リファラに特定の文字列が設定されていない際にブロックする
    • バケットポリシーのConditionにはリファラを指定することが出来る
    • CloudFrontがS3にファイルをリクエストする際に、カスタムヘッダを指定することが出来る
    • 上記2点より、CloudFrontがS3にファイルをリクエストする際のカスタムヘッダに Referer: XXXXXX (XXXXXは推測されづらいランダムな文字列)を指定し、バケットポリシーでもそのリファラを条件としておけば大体のアクセスは防げる
    • セキュリティとして完璧ではもちろんないが、今回のケースでは十分だと思う
    • 参考: https://dev.classmethod.jp/articles/s3-cloudfront-static-site-design-patterns-2022/#toc-3
  3. CloudFront Functionsを利用し、特定URLパターンの際に処理を加えて index.html を返却するようにする

GitHub Actionsで自動デプロイする

Yamlに以下のように書けばいい

- name: Deploy
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  run: |
    aws s3 sync --delete --region [バケットのリージョン] [デプロイ対象ディレクトリ] s3://[対象バケット]

run の部分は、例えば aws s3 sync --delete --region ap-northeast-1 dist s3://sample-website みたいな感じ。記事によっては sync ではなく cp を使用しているケースがあるが、それだと不要になったファイルが削除されず残り続けてしまう。sync --delete すると存在しないファイルを消してくれる。

また、デプロイ用のIAMユーザーを作成する必要がある。IAMポリシーは以下のような物を与えれば最小権限に絞れる。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::[バケット名]"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::[バケット名]/*"
        }
    ]
}

ルートドメインにアクセスされた際にリダイレクトするようにする

S3の静的WEBサイトホスティング機能にリダイレクト機能があるので、それを利用するのが一番手軽かと思われる。

  • 作業内容
    • sd-milieu.net 用のS3バケットを作成。静的WEBサイトホスティング機能を有効にし、「オブジェクトのリクエストをリダイレクトする」を選択。「ホスト名」に www.sd-milieu.net を指定。
    • sd-milieu.net 用のCloudFrontディストリビューションを作成し、代替ドメイン名に sd-milieu.net を指定
    • Route53で、sd-milieu.net へのエイリアスレコードを作成

今回知った細かいテクニック

  • dig [url] でDNSに関する情報が見れる
    • ネットワーク上でDNSの設定が反映されているか確認する際に使用。この方法で設定の反映が確認できたのに思ったとおり動作しない場合は後述のOSのDNSキャッシュを削除するといい。
  • sudo killall -HUP mDNSResponder でOSのDNSキャッシュを消せる。ネットワーク上にはDNSの設定が反映されているがOSのキャッシュのせいで確認できないことがあったのでその際に使用

わからないこと

  • CloudFrontのURL XXX.cloudfront.net の無効化方法
    • S3のURLを無効化しておいてこっちを無効化しないのは片手落ち感がある
    • AmazonでもCloudFrontのURLがインデックスされてトラブルになった過去がある
    • CloudFront FunctionsでURLをみて cloudfront.net へのアクセスだったら弾く、とかしないといけない…?