S3 + CloudFront + Route53 でウェブサイトを公開するパターン
構成のパターン
S3をウェブサイトのホスティング先にする場合、S3バケットを静的ウェブサイトとして設定するか否かでパターンが分かれる。S3バケットを静的ウェブサイトとして設定すると、S3内のコンテンツにWebエンドポイントからアクセスできるようになる。
あえて静的ウェブサイト設定を用いるモチベーションとなるのは、以下の2つ。
リダイレクトを扱いたい場合
Amazon S3 の静的ウェブサイト設定では、オブジェクトレベル/バケットレベルのリダイレクトをサポートする
https://example.com/hoge, https://example.com/hoge/, https://example.com/hoge/index.html で全て同じページにアクセスさせることが可能
Amazon S3 の静的ウェブサイト設定を利用しない場合、リダイレクトには AWS Lambda@Edge を利用しないと厳しい
エラーページの扱いを楽にしたい場合
AWS CloudFront のオリジンとして Amazon S3 を利用した場合、存在しないページへのアクセスは 403 になる。Custom Error Response の機能で 404 に変更することも可能ではある
AWS CloudFront のオリジンとして Amazon S3 の静的ウェブサイトエンドポイントを利用した場合、Amazon S3 は存在しないページへのアクセスに対し 404 を返し、エラーレスポンス内容も編集できる
ウェブサイトエンドポイントと REST API エンドポイントの主な違い
Amazon S3 + Amazon CloudFrontでWebサイトを構築する際にS3静的Webサイトホスティングを採用する理由
S3の静的ウェブサイト設定
リダイレクトのことを考えると、S3 の静的ウェブサイト機能を利用したい。
静的ウェブサイト設定をONにする
バケットの「プロパティ」から設定を編集できる。ホスティングタイプは「静的ウェブサイトをホストする」にする。
https://gyazo.com/b91137bb425f6801d88e3ef08c6f447f
ウェブサイトのホスティングの有効化
バケットポリシーを設定する
バケットを静的ウェブサイトとして設定する場合、バケットをパブリック読み取りアクセス可能にする必要がある。以下の2つの手順が必要。
パブリックアクセスブロック設定を無効にする
パブリック読み取りアクセス可能にするバケットポリシーを記述する
これらはどちらもバケットの「アクセス許可」から設定を編集できる。前者は単に「パブリックアクセスを全てブロック」からチェックを外して保存すれば良い。後者は、以下のようなバケットポリシーを設定する。
code:json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::自分のバケット名/*",
}
]
}
ただし、このままだと S3 のWebエンドポイントに直アクセスが可能となってしまう。
AWS CloudFront から AWS S3 へのアクセス時に Referer を付与し、Referer 付与時のみアクセスできるようにすることで、アクセス元を制限させることもできる。
code:json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::自分のバケット名/*",
"Condition": {
"StringEquals": {
"aws:Referer": "推測されにくい文字列"
}
}
}
]
}
ウェブサイトアクセスのアクセス許可の設定
CloudFrontとS3で作成する静的サイト構成の私的まとめ
DNSの設定
HTTPS 通信のために、AWS Certificate Manager で証明書を発行する。パブリック SSL/TLS 証明書は無料で発行できる。ただし、CloudFront で利用するためには、米国東部 (バージニア北部) リージョン (us-east-1) で証明書をリクエストする必要がある。
CloudFront で SSL/TLS 証明書を使用するための要件
Route53 の場合
TODO
Cloudflare の場合
Google Domains や Cloudflare 等の外部のレジストラを利用している場合には、CNAME 検証が利用できる。
サードパーティの DNS プロバイダーが管理するカスタムドメインの追加
発行されたCNAMEの名前と値を確認し、
https://gyazo.com/051214faf2aa841edc1fdf3ce8a25aa5
Clouflare 側で CNAME レコードを登録する。この時、プロキシはオフにしたほうが良さそうだった。
https://gyazo.com/f78fbb7bc3544c681d586a4a94bbf7fe
Cloudflare の DNS の基本:プロキシ化って何?
Cloudflare CDN のオリジンとして CloudFront を設定してみた
Cloudflare DNS Proxy モード と CNAME Flattening (ALIASレコード)について
CloudFront の設定
新しいディストリビューションを、以下のような設定で作成する。
オリジンドメイン: Webエンドポイントを設定する。S3バケットを指定しないよう注意。
カスタムヘッダーを追加: Referer が必要なら追加する
ビューワープロトコルポリシー: Redirect HTTP to HTTPS
HTTPでアクセスされても、HTTPSへリダイレクトするようになる。
ビューワーと CloudFront 間の通信で HTTPS を必須にする
代替ドメイン名: アクセスさせるドメイン名を入力する (example.com, www.example.com など)
カスタムSSL証明書: ACMで発行した証明書を利用する
Route53 の場合
TODO
Cloudfrare の場合
CloudFront への疎通のために、CNAME を設定する。
Type: CNAME
Name: ドメイン名
Target: CloudFront のディストリビューションドメイン
AWSコンソール上の「一般」「詳細」「ディストリビューションドメイン名」から確認できる
example.com だけでなく、www.example.com のようなサブドメインからもアクセスさせたい場合は、Name を www.example.com にして、もう一つ登録する。
これで、登録したドメイン名で S3 へ疎通できるようになる。
Cloudflare CDN のオリジンとして CloudFront を設定してみた
デプロイ自動化
GitHub Actions を利用して、S3 へのリソースのアップロードを自動化したい。
GitHub Actions は OpenID Connect をサポートしている。パスワードやアクセスキーのようなシークレットを直接 GitHub 側に管理させると、シークレットの更新や漏洩リスクの考慮が必要になる。OIDC ではシークレットをハードコードするのではなく、必要に応じて都度短時間利用可能なアクセストークンを発行する。
https://gyazo.com/d91faa7c6ecce288baf16265924eb50c
GitHub Actions の OpenID Connect サポートについて
About security hardening with OpenID Connect
AWS で ID プロバイダを作成する
まずは、GitHub の OIDC プロバイダを IAM 上で設定する。設定項目は以下の通り。
プロバイダのタイプ: OpenID Connect
プロバイダのURL: https://token.actions.githubusercontent.com
対象者: sts.amazonaws.com
Configuring OpenID Connect in Amazon Web Services
IAM ポリシーの作成
OIDC プロバイダに最終的に割り当てることになる IAM ポリシーを作成する。以下のような感じ。ListAllMyBuckets と GetBucketLocation は、CLI からの利用でない場合不要かもしれない。
適当な名前をつけて保存しておく。
code:json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "s3:ListAllMyBuckets",
"Effect": "Allow",
"Resource": "arn:aws:s3:::*"
},
{
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::バケット名"
},
{
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::バケット名/*"
}
]
}
IAM ロールの作成と割り当て
OIDC プロバイダに IAM ロールを割り当てる。
信頼されたエンティティタイプ: ウェブアイデンティティ
ウェブアイデンティティ:
アイデンティティプロバイダー: token.actions.githubusercontent.com
Audience: sts.amazonaws.com
GitHub 組織: GitHubユーザー名
GitHub リポジトリ: リポジトリ名
許可ポリシー: 先ほど作成したポリシー
GitHub Actions を使ってS3 に自動デプロイ
GitHub ActionsにAWSクレデンシャルを直接設定したくないのでIAMロールを利用したい
GitHub Actions Workflow の作成
以下の三つは GitHub Actions の secret として設定する
AWS_ACCOUNT_ID
AWS_IAM_ROLE
AWS_BUCKET_NAME
以下のような yaml を記述する。
code:yaml
name: Deploy
on:
push:
branches:
- main
env:
AWS_REGION: ap-northeast-1
AWS_ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_IAM_ROLE }}
permissions:
id-token: write # JWTのリクエストのためにwriteにする
contents: read # acionts/checkoutのためにreadにする
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Git clone the repository
uses: actions/checkout@v4
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@v3
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
role-session-name: セッション名
aws-region: ${{ env.AWS_REGION }}
- name: Deploy to S3
run:
aws s3 sync --delete ./src/ s3://${{ secrets.AWS_BUCKET_NAME }}/
Configuring OpenID Connect in Amazon Web Services
AWS CLIがAssumeRoleする際のセッション名を指定する
補足: CloudFront のキャッシュを都度削除する
以下を参考にすると良い
https://nshmura.com/posts/automate-cache-cleaning-of-cloudfront/