はじめに
2024年11月20日にVPC Originという機能が追加されました。
ざっくり説明すると、CloudFrontのオリジンにプライベートサブネットに配置されたEC2やALB,NLBをオリジンにすることができるという機能です。
https://aws.amazon.com/jp/blogs/news/introducing-amazon-cloudfront-vpc-origins-enhanced-security-and-streamlined-operations-for-your-applications/
今回はこのVPC Originという機能を使って、パブリックIPを持たないEC2を外部公開してみようと思います。
VPC Originのメリット
CloudFrontのVPCオリジンを活用してプライベートサブネット内のリソースを公開するメリットは3点ほどあります。
・EC2用のElasticIPが必要なくなるため、コスト削減できる
・VPCオリジン配下を外部(インターネット)から隔離できアクセスされる心配が無いため、セキュリティ向上が期待できる
・今までパブリックサブネットに配置されていたEC2やALB等のオリジンに、IP制限やセキュリティヘッダを使ってCloudFrontのみアクセスできるようにする必要があったが不要になる
VPCオリジンを使ったパブリックIPの無いEC2を公開するまでの流れ
- VPC周りとEC2を作成する
- VPCオリジンを作成する
- セキュリティグループにVPCオリジンを追加する
- CloudFrontを作成する
- CloudFrontのドメインからWEBページを確認
完成イメージ図
1. VPC周りとEC2を作成する
手動で作成してもいいが、面倒なのでTerraformで作成する。(似たような構成が作れるなら手動でも可)
設定内容
・VPC作成(プライベートサブネット,VPCエンドポイント,セキュリティグループ)
・EC2作成(SSMFullAccessロール作成&EC2にアタッチ,Nginx・SSMAgentインストール&起動,htmlコンテンツ追加)
main.tf
terraform {
required_version = ">=1.9.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.44.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
# ===============================================================================
# リソースの命名に yasuda-kensyo を付ける
# ===============================================================================
locals {
env = "yasuda-kensyo"
}
aws_ec2.tf
# ===============================================================================
# WEBサーバー作成
# ===============================================================================
resource "aws_instance" "web-ec2" {
ami = "ami-023ff3d4ab11b2525"
instance_type = "t3.micro"
subnet_id = aws_subnet.private_1a.id
vpc_security_group_ids = [aws_security_group.ec2.id]
iam_instance_profile = aws_iam_instance_profile.ec2_instance_profile.name
user_data = <<-EOF
#!/bin/bash
sudo dnf update -y
sudo setenforce 0
sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
sudo systemctl enable amazon-ssm-agent
sudo systemctl start amazon-ssm-agent
sudo dnf install -y nginx
sudo systemctl start nginx
sudo systemctl enable nginx
sudo echo "<html>hello</html>" > /usr/share/nginx/html/index.html
EOF
tags = {
"Name" = "${local.env}-web-ec2"
}
}
# ===============================================================================
# IAMロール作成
# ===============================================================================
resource "aws_iam_role" "ec2_ssm_role" {
name = "ec2-ssm-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}
# ===============================================================================
# IAMポリシーアタッチ
# ===============================================================================
resource "aws_iam_role_policy_attachment" "ssm_full_access" {
role = aws_iam_role.ec2_ssm_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMFullAccess"
}
# ===============================================================================
# EC2インスタンスプロファイル作成
# ===============================================================================
resource "aws_iam_instance_profile" "ec2_instance_profile" {
name = "ec2-ssm-instance-profile"
role = aws_iam_role.ec2_ssm_role.name
}
aws_vpc.tf
# ===============================================================================
# main vpc
# ===============================================================================
resource "aws_vpc" "main" {
assign_generated_ipv6_cidr_block = false
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
"Name" = "${local.env}-vpc"
}
}
# ===============================================================================
# インターネットゲートウェイ作成
# ===============================================================================
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${local.env}-igw"
}
}
# ===============================================================================
# プライべートサブネットを作成
# ===============================================================================
resource "aws_subnet" "private_1a" {
vpc_id = aws_vpc.main.id
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.1.0/24"
tags = {
Name = "${local.env}-private-a"
}
}
# ===============================================================================
# プライベートサブネット用のルートテーブル作成
# ===============================================================================
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${local.env}-private-route"
}
}
# ===============================================================================
# プライベートルートにプライベートサブネット関連付け
# ===============================================================================
resource "aws_route_table_association" "private_subnet_A" {
subnet_id = aws_subnet.private_1a.id
route_table_id = aws_route_table.private.id
}
# ===============================================================================
# プライベートルートにS3エンドポイントゲートウェイ関連付け
# ===============================================================================
resource "aws_vpc_endpoint_route_table_association" "s3" {
vpc_endpoint_id = aws_vpc_endpoint.s3.id
route_table_id = aws_route_table.private.id
}
# ===============================================================================
# EC2用のセキュリティグループ作成
# ===============================================================================
resource "aws_security_group" "ec2" {
vpc_id= "${aws_vpc.main.id}"
name = "${local.env}-ec2"
description = "${local.env}-ec2"
egress{
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks=["0.0.0.0/0"]
}
tags = {
"Name" = "${local.env}-ec2"
}
}
# ===============================================================================
# VPCエンドポイント用のセキュリティグループ作成
# ===============================================================================
resource "aws_security_group" "ssm" {
vpc_id= "${aws_vpc.main.id}"
name = "${local.env}-ssm"
description = "${local.env}-ssm"
ingress{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks=["10.0.0.0/16"]
}
tags = {
"Name" = "${local.env}-ssm"
}
}
# ===============================================================================
# VPC Endpoint 作成
# ===============================================================================
data "aws_iam_policy_document" "vpc_endpoint" {
statement {
effect = "Allow"
actions = [ "*" ]
resources = [ "*" ]
principals {
type = "AWS"
identifiers = [ "*" ]
}
}
}
resource "aws_vpc_endpoint" "ssm" {
vpc_endpoint_type = "Interface"
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.ssm"
policy = data.aws_iam_policy_document.vpc_endpoint.json
subnet_ids = [
aws_subnet.private_1a.id
]
private_dns_enabled = true
security_group_ids = [
aws_security_group.ssm.id
]
}
resource "aws_vpc_endpoint" "ssmmessages" {
vpc_endpoint_type = "Interface"
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.ssmmessages"
policy = data.aws_iam_policy_document.vpc_endpoint.json
subnet_ids = [
aws_subnet.private_1a.id
]
private_dns_enabled = true
security_group_ids = [
aws_security_group.ssm.id
]
}
resource "aws_vpc_endpoint" "ec2messages" {
vpc_endpoint_type = "Interface"
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.ec2messages"
policy = data.aws_iam_policy_document.vpc_endpoint.json
subnet_ids = [
aws_subnet.private_1a.id
]
private_dns_enabled = true
security_group_ids = [
aws_security_group.ssm.id
]
}
resource "aws_vpc_endpoint" "s3" {
vpc_endpoint_type = "Gateway"
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.s3"
policy = data.aws_iam_policy_document.vpc_endpoint.json
}
2. VPCオリジンを作成する
CloudFrontの画面から「VPCオリジン」を選択し、下記の画面に入る
「Name」を適当に入力、「オリジンARN」にEC2のARNを入力し、「VPCオリジンを作成」を選択する
「Status」が「Deployed」になったら作成完了
3. セキュリティグループにVPCオリジンを追加する
VPCオリジンがEC2に対してHTTP通信可能にするために、EC2のセキュリティグループにVPCオリジンのセキュリティグループをHTTP通信許可を設定する
(Terraform通り作成した場合は、EC2のセキュリティグループが「yasuda-kensyo-ec2」となる)
4. CloudFrontを作成する
「Origin domain」に2. で作成したVPCOriginを選択する
「VPC origin domain」にEC2のプライベートDNS名を入力する
「ウェブアプリケーションファイアウォール(WAF)」は使わないので有効にしない
5. CloudFrontのドメインからWEBページを確認
確認できればOK
※今回は証明書を適用していないので、httpでアクセスすること
最後に
今回はCloudFrontのVPCオリジンを活用してパブリックIPを持たないEC2を外部公開しました。
VPCオリジンは、EC2のほかにもプライベートサブネットに配置されているALBやNLBに対しても使用できます。
本記事には掲載していませんが、InternalALB + ECSの構成でもデプロイできましたのでInternalALB越しであればなんでもOKみたいです。