2023-5-20
S3+CloudFrontで静的WEBサイトをホスティングする際のメモ
www.sd-milieu.net
をNetlifyからS3へ移行したのでやった作業をまとめておく。
実際に行った大まかな内容
- S3にバケットを作成し、静的WEBサイト用として設定。ファイルをアップロードする
- この時点で、S3のURLだがHTTPではアクセスできるようになっている
- Route53をDNSとして利用できるようにする
- Route53にて
sd-milieu.net
のホストゾーンを作成 - ドメインプロバイダ(今回はバリュードメイン)のネームサーバーに、Route53のネームサーバーを設定
- Route53にて
- 後工程で必要になるので、ACMにてSSL証明書の作成
- ACMにて証明書リクエストを行う。リージョンはバージニア北部(
us-east-1
)である必要があるので注意。 - 項目「ドメイン」の「Route53でレコードを作成」を押下し、DNSレコードに検証用のCNAMEレコードを追加する
- しばらく待ってステータスが「成功」となればOK
- ACMにて証明書リクエストを行う。リージョンはバージニア北部(
- 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への直接アクセスを防ぎたい場合
以下の要件を全て満たしたいケースを想定
https://example.com/
やhttps://example.com/about/
がリクエストされた際にindex.html
やabout/index.html
を返却する- S3バケットへの直接アクセスはブロックする
index.html
の返却をさせようとすると、S3の静的ウェブサイトホスティング機能を使う必要がある。これを利用すると、チェックボックスをオンにするだけで index.html
の返却をしてくれる。
ただ、CloudFrontと併用しようとすると、OAIを有効にすることが出来ず、公開されているS3バケットへ直接アクセスすることが可能になってしまう。
なので、対応方法としては大きく以下の3パターンに分かれる
- S3バケットへの直接アクセスを許容する
- そもそも静的WEBサイトとして公開しているものなので、直接アクセスされたところでという話
- 懸念点として、検索エンジンのクローラーにアクセスされた結果インデックスされてしまうという問題はある。なので基本的に避けたほうがいいかと思う
- バケットポリシーで、リファラに特定の文字列が設定されていない際にブロックする
- バケットポリシーのConditionにはリファラを指定することが出来る
- CloudFrontがS3にファイルをリクエストする際に、カスタムヘッダを指定することが出来る
- 上記2点より、CloudFrontがS3にファイルをリクエストする際のカスタムヘッダに
Referer: XXXXXX
(XXXXXは推測されづらいランダムな文字列)を指定し、バケットポリシーでもそのリファラを条件としておけば大体のアクセスは防げる - セキュリティとして完璧ではもちろんないが、今回のケースでは十分だと思う
- 参考: https://dev.classmethod.jp/articles/s3-cloudfront-static-site-design-patterns-2022/#toc-3
- CloudFront Functionsを利用し、特定URLパターンの際に処理を加えて
index.html
を返却するようにする- 正直ここまでする?という感じはあるが、まぁ一番王道ではあるかと思う
- AWS公式でサンプルコードを用意しているので、AWSとしても想定された用法っぽい
- 参考:https://dev.classmethod.jp/articles/cloudfront-functions-index-docum/
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
へのアクセスだったら弾く、とかしないといけない…?