August 13, 2020
S3で静的ウェブサイトを公開するための方法を解説します。
※公式ドキュメントでは、「静的ウェブサイトをホスティングする」という文言で表現されています。本記事では、分かりやすさを優先して「ウェブサイトをインターネットに公開する」という表現に変えています。
※静的ウェブサイト: サーバ側スクリプト(PHP等)を使用しないサイト。
目次
Terraformを使用します。ソースコードの構成は以下の通り。
public-s3
|-- main.tf ←モジュールを読み込むトップのファイル。
`-- modules
|-- iam.tf ←バケットポリシーを設定するファイル。
`-- s3.tf ←S3バケットを設定するファイル。
また、Terraformのトップファイルは以下の通りです。共通の設定のため解説は割愛します。
./main.tf
provider "aws" {
region = "ap-northeast-1"
assume_role {
role_arn = "arn:aws:iam::XXXXXXXXXX:role/SystemAdmin"
}
}
terraform {
required_version = "0.12.24"
backend "s3" {
bucket = "tfstate.mini-schna.com"
region = "ap-northeast-1"
key = "blog/public-s3.tfstate"
encrypt = true
role_arn = "arn:aws:iam::XXXXXXXXXX:role/SystemAdmin"
}
}
module "aws" {
source = "./modules"
}
下記の図は、S3でウェブサイトを公開した時の流れになります。
大きな流れとしては、Webサーバで公開する時と変わりません。ただ、いくつかS3特有の情報があるので少し補足したいと思います。
簡単に表現すると、外部からウェブサイトとしてアクセスできるURLのことです。ぱっとイメージが付きづらいかと思うので、index.html
にアクセスしたい場合を例に取ってみます。
(例) mini-schna.comバケット
index.html
を表示するURLは下記のように2種類存在します。
・ウェブサイトエンドポイント: http://mini-schna.com.s3-website-ap-northeast-1.amazonaws.com
・オブジェクトURL: https://s3-ap-northeast-1.amazonaws.com/mini-schna.com/index.html
オブジェクトURLでアクセスする場合は、ファイル名まで指定しないとアクセスすることができません。ウェブサイトにアクセスしたいクライアントは、ファイル名まで知らないことが多いため、これだとウェブサイトとしてなかなか機能しません。
そこでこの問題を解決するのが、ウェブサイトエンドポイント。 ウェブサイトエンドポイントにアクセスすると、デフォルトで設定されたコンテンツが表示され、このURLをベースに画面遷移していくことができます。
S3はデフォルトで外部からアクセスすることができません。そのため、外部からのアクセスを許可する必要があるのですが、その設定を行うのがバケットポリシーになります。
外部からS3バケットへのアクセスを許可します。
./modules/iam.tf
# S3 Bucket Policy
data "aws_iam_policy_document" "s3_bucket_policy" {
statement {
sid = ""
effect = "Allow"
## アクセス元の設定。
principals {
identifiers = ["*"] ## 誰でもアクセスできるように設定。
type = "*"
}
## バケットに対して制御するアクションを設定する。
actions = [
"s3:GetObject" ## オブジェクトの読み取りアクション。
]
## アクセス先の設定。
resources = [
"arn:aws:s3:::mini-schna.com", ## mini-schna.comバケットへのアクセス。
"arn:aws:s3:::mini-schna.com/*" ## mini-schna.comバケット配下へのアクセス。
]
}
}
それでは、解説していきます。
## アクセス元の設定。
principals {
identifiers = ["*"] ## 誰でもアクセスできるように設定。
type = "*"
}
どこからでもアクセスできるように identifiers
& type
に「*(ワイルドカード)」を指定します。
## バケットに対して制御するアクションを設定する。
actions = [
"s3:GetObject" ## オブジェクトの読み取りアクション。
]
ウェブサイトとして公開するため、クライアントはオブジェクト(HTMLファイル等)を表示することができればよいので s3:GetObject
アクションを指定します。
## アクセス先の設定。
resources = [
"arn:aws:s3:::mini-schna.com", ## mini-schna.comバケットへのアクセス。
"arn:aws:s3:::mini-schna.com/*" ## mini-schna.comバケット配下へのアクセス。
]
mini-schna.comバケット配下だけアクセスできるようにします。
Webサイトを公開するためのS3バケットを作成していきます。
./modules/s3.tf
# 変数
variable "bucket_name" {
default = "mini-schna.com"
description = "s3 bucket name."
}
# S3 Bucket
resource "aws_s3_bucket" "mini_schna_com" {
bucket = var.bucket_name
policy = data.aws_iam_policy_document.s3_bucket_policy.json ## iam.tfで設定したポリシーを使用。
## バケットの削除設定。
force_destroy = false ## バケットの中にオブジェクトが入っている場合にTerraformからバケットを削除できないようにする。
## Webサイト設定。
website {
## バケットにアクセスした時にデフォルトで表示されるコンテンツを設定。
index_document = "index.html"
}
## オブジェクトのバージョン管理設定。
versioning {
enabled = true
mfa_delete = false ## オブジェクトへのアクセスにMFA(多段階認証)を使用しない。
}
## バケットの料金を誰が支払うか設定。
request_payer = "BucketOwner" ## 通常通り所有者が支払う。
}
# S3 Public Access Block
resource "aws_s3_bucket_public_access_block" "mini_schna_com" {
bucket = aws_s3_bucket.mini_schna_com.bucket
block_public_acls = true
block_public_policy = false ## バケットポリシーで制御したいため無効にする。
ignore_public_acls = true
restrict_public_buckets = false ## バケットポリシーで制御したいため無効にする。
}
それでは、解説していきます。
# 変数
variable "bucket_name" {
default = "mini-schna.com"
description = "s3 bucket name."
}
後で変更しやすいようにバケット名を変数に設定しています。(特別必要な設定ではないため、直接S3の設定に記載する方法でも大丈夫です。)
resource "aws_s3_bucket" "mini_schna_com" {
bucket = var.bucket_name
policy = data.aws_iam_policy_document.s3_bucket_policy.json ## iam.tfで設定したポリシーを使用。
## バケットの削除設定。
force_destroy = false ## バケットの中にオブジェクトが入っている場合にTerraformからバケットを削除できないようにする。
## Webサイト設定。
website {
## バケットにアクセスした時にデフォルトで表示されるコンテンツを設定。
index_document = "index.html"
}
## オブジェクトのバージョン管理設定。
versioning {
enabled = true
mfa_delete = false ## オブジェクトへのアクセスにMFA(多段階認証)を使用しない。
}
## バケットの料金を誰が支払うか設定。
request_payer = "BucketOwner" ## 通常通り所有者が支払う。
}
bucketでバケット名を設定します。今回は、変数で設定したバケット名を使用しています。
poloicyでバケットポリシーを設定します。今回は、iam.tf
で事前に設定したポリシーをJSONにシリアライズして値を設定します。
force_destroyは、バケットの中にオブジェクトが入っている状態でもバケットを削除できるようにするかの設定。オブジェクトが入っている状態で間違えて削除しないように無効にします。
website { index_document = ~ }は、ウェブエンドポイントにアクセスがあった時にデフォルトで返すファイルを設定します。
versioning { enabled = ~ }は、オブジェクトのバージョン管理を行うかどうか設定します。間違えてファイルを削除した時にすぐに戻せるようにするために有効にします。
versioning { mfa_delete = ~ }は、オブジェクトへのアクセスにMFA(多段階認証)が必要かどうか設定します。今回は、必要ないため無効にします。
request_payerは、バケットの料金を支払う対象を設定します。今回は、一般的なバケット所有者が支払うように設定します。
※バケット所有者以外にも、リクエストを実行したユーザーが支払うように設定することも可能です。
# S3 Public Access Block
resource "aws_s3_bucket_public_access_block" "mini_schna_com" {
bucket = aws_s3_bucket.mini_schna_com.bucket
block_public_acls = true
block_public_policy = false ## バケットポリシーで制御したいため無効にする。
ignore_public_acls = true
restrict_public_buckets = false ## バケットポリシーで制御したいため無効にする。
}
パブリックアクセスブロック設定は、ポリシーやACL設定を上書きしてパブリックアクセスを無効にする設定です。今回は、バケットポリシーでアクセス許可を行うため、ポリシーに関する機能は無効にします。
$ cd /infra/terraform/public-s3
$ terraform init
$ terraform apply
※Dockerコンテナ内での実行を前提にしています。
0.公開するためのHTMLファイル( index.html
)を作成する。
index.html
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>My Website Home Page</title>
</head>
<body>
<h1>Welcome to my website</h1>
<p>Now hosted on Amazon S3!</p>
</body>
</html>
3.アップロードしたファイルを確認して、「次へ」をクリックする。
5.ストレージクラスから「スタンダード」を選択して、「次へ」をクリックする。
8.ウェブエンドポイント http://[バケット名].s3-website-[リージョン].amazonaws.com
にアクセスするとWebページが表示されます。
以上で、S3でウェブサイトを公開できるようになりました。
最後までご覧頂きありがとうございました。