CloudFront+OACによるS3のホームページホスト
Posted On 2024-12-08
Amazon S3の静的サイトホスティング機能を利用すると、ホームページをホストすることは可能ですが、HTTPS非対応やバケット全体の公開などのセキュリティリスクがあります。そこで、CloudFrontのOAC(Origin Access Control)を活用して、S3バケットを非公開にし、CloudFront経由のみで安全にコンテンツを配信する方法を試してみました。
目次
はじめに
- S3バケットには静的ページのホスト機能がついており、そのままでホームページをホストすることは可能
- しかし、S3単独だとHTTPSに対応していなかったり、バケット全体を公開してしまったりいろいろセキュリティリスクはある
- そこで(教科書的なパターンであるが)CloudFrontのOAC(Origin Access Control)を活用して、S3バケットを公開せず、CloudFront経由の接続のみ配信するという手法を試す
参考:Amazon CloudFront オリジンアクセスコントロール(OAC)のご紹介
関連:[Terraform]CloudFront+S3でホームページを作る
アーキテクチャー図
作るホームページ
以下の2ファイルからなるホームページ
- index.html
- images/sample_image.webp
index.htmlは以下の通り。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>静的サイトのサンプル</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
margin: 50px;
background-color: #f0f0f0;
}
img {
max-width: 100%;
height: auto;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
display: inline-block;
}
</style>
</head>
<body>
<div class="container">
<h1>ようこそ!</h1>
<p>これはTerraformを使用してホストされた静的サイトです。</p>
<img src="images/sample_image.webp" alt="サンプル画像">
</div>
</body>
</html>
images/sample_image.webp
は以下の通り。Dalle-3で作ったもの。かわいい
Terraformのコード
そこまで分量ないのでTerraformのコード全体を示す。仕組みはとても単純で、
- CloudFront側でOACを定義
- それをS3のバケットポリシーに入れて許可する
- CloudFrontのその他の設定は適当
# --- S3 バケット作成 ---
resource "aws_s3_bucket" "this" {
bucket = "private-static-site-bucket-test"
force_destroy = true
}
# index.html をS3にアップロード
resource "aws_s3_object" "index_html" {
bucket = aws_s3_bucket.this.bucket
key = "index.html"
content_type = "text/html"
source = "index.html"
acl = "private"
depends_on = [aws_s3_bucket_policy.this]
}
# 画像ファイルをS3にアップロード
resource "aws_s3_object" "sample_image" {
bucket = aws_s3_bucket.this.bucket
key = "images/sample_image.webp"
source = "images/sample_image.webp"
content_type = "image/webp"
acl = "private"
depends_on = [aws_s3_bucket_policy.this]
}
# --- Origin Access Control 作成 ---
resource "aws_cloudfront_origin_access_control" "this" {
name = "my-oac"
description = "OAC for S3 bucket"
signing_behavior = "always"
signing_protocol = "sigv4"
origin_access_control_origin_type = "s3"
}
# CloudFront Distribution
resource "aws_cloudfront_distribution" "this" {
enabled = true
default_root_object = "index.html"
price_class = "PriceClass_100"
origin {
domain_name = aws_s3_bucket.this.bucket_regional_domain_name
origin_id = "s3-origin"
origin_access_control_id = aws_cloudfront_origin_access_control.this.id
}
default_cache_behavior {
target_origin_id = "s3-origin"
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
wait_for_deployment = true
}
# S3バケットポリシー: CloudFront Distribution(OAC)からのみオブジェクト取得を許可
data "aws_caller_identity" "current" {}
resource "aws_s3_bucket_policy" "this" {
bucket = aws_s3_bucket.this.id
depends_on = [aws_cloudfront_distribution.this]
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCloudFrontServicePrincipal"
Effect = "Allow"
Principal = {
Service = "cloudfront.amazonaws.com"
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.this.arn}/*"
Condition = {
StringEquals = {
"aws:SourceArn" = "arn:aws:cloudfront::${data.aws_caller_identity.current.account_id}:distribution/${aws_cloudfront_distribution.this.id}"
}
}
}
]
})
}
# CloudFrontのドメインを出力
output "cloudfront_domain" {
value = aws_cloudfront_distribution.this.domain_name
}
ここでのバケットポリシーは明示的な許可を追加しただけなので、マネジメントコンソールからIAMユーザーはファイルにアクセスできる。ただ、S3単独のパブリックアクセスは許可しなくていいよというもの。
結果
CloudFront経由でアクセスした結果
マネジメントコンソールに表示されているS3のオブジェクトURLからアクセスした結果(Access Deniedで正常な挙動)
Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー