🐙

AWS-VPCプライベートサブネットとは何かを考える ~ Terraformを使ったハンズオン有り ~

2024/09/29に公開

はじめに

VPC・サブネットをある程度は利用したことがある方を前提にしております。
サブネット、ルートテーブルについて簡単に解説しつつ、パブリックサブネットとプライベートサブネットを隔てているものについて考えていきます。
Terraformでリソースを作成しながら体感していく記事となっています。

サブネットとは

サブネット(Subnet)は、**仮想プライベートクラウド(VPC)**内でIPアドレス空間を細分化したネットワークの単位です。VPC全体のCIDRブロックを複数のサブネットに分割することで、異なる用途やセキュリティ要件に応じたネットワーク設計が可能になります。サブネットは、以下のような特徴と役割を持ちます:

  • IPアドレスの範囲:各サブネットは一意のCIDRブロックを持ち、VPC内でのIPアドレスの重複を防ぎます。
  • アベイラビリティゾーン(AZ)の選択:サブネットは特定のアベイラビリティゾーンに属し、可用性と耐障害性を向上させます。
  • 公開・非公開の区分:サブネットは「パブリックサブネット」と「プライベートサブネット」に分類され、アクセスの制御やセキュリティの設定が異なります。

パブリックサブネット

インターネットゲートウェイ(IGW)へのルート:パブリックサブネットはインターネットゲートウェイへのルートが設定されており、インターネットとの双方向通信が可能です。
パブリックIPの自動割り当て:サブネット内のインスタンスにパブリックIPが自動的に割り当てられる設定にすることで、直接インターネットからアクセス可能になります。

プライベートサブネット

インターネットアクセスの制限:プライベートサブネットはインターネットゲートウェイへの直接ルートを持たず、外部からの直接アクセスができません。
NATゲートウェイを介してプライベートサブネットからインターネットへアクセスすることは可能です。ただし、NATゲートウェイはアウトバウンド通信を行うことができますが、インバウンドの新規接続は受け付けません。

ルートテーブルとは

ルートテーブルは、VPC内のトラフィックがどの経路を通って目的地に到達するかを定義するルールの集合です。各ルートは「宛先(Destination)」と「ターゲット(Target)」のペアで構成されます。

宛先(Destination):

  • トラフィックが向かう先のIPアドレス範囲。

ターゲット(Target):

  • その宛先に向かうトラフィックを転送する先(例:IGW、NATGW、ローカルなど)。

※補足

  • サブネットに対して1つのみルートテーブルを紐づけられる
  • 1つのルートテーブルを、幾つかのサブネットで利用することは可能
  • サブネットに明示的にルートテーブルを紐づけない場合は、メインルートテーブル(VPC作成時に作成)が紐づく

https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/subnet-route-tables.html

結局、プライベートサブネットとは

IGWをDestinationに設定されていないルートテーブルが紐付いたサブネットのことです。
簡単にいうと、インターネットとの直接的な通信が遮断されたサブネットのことです。

ルートテーブルはサブネットから外向きの通信制御なのに、IGWを設定することでインターネット通信が可能?

私自身、1~3が疑問でした。

  1. ルートテーブルはサブネットから外向きの通信制御
  2. パブリックサブネット(ルートテーブル)では、0.0.0.0/0 -->> IGWを設定することでインターネット接続可能
  3. プライベートサブネット(ルートテーブル)0.0.0.0/0 -->> NATGWを設定してもインターネット接続不可

回答は

  1. 通信する際には、内向き・外向きの通信が複数回実行されることで正式にネットワークがつながる
    https://qiita.com/c60evaporator/items/2f24d4796202e8b06a77#外向き通信と内向き通信

  2. IGWは外向き・内向き双方の通信を許可するのに対して、NATGWは内向きの新規の接続は受け付けない
    https://qiita.com/c60evaporator/items/2f24d4796202e8b06a77#インターネットゲートウェイとnatゲートウェイ

パブリックサブネットのパブリックIPに対して外部(インターネットから)リクエストが合った際には、IGW の宛先に対して通信することができる。 なので、「0.0.0.0/0 -->> IGW」が紐付いたルートテーブルはパブリックサブネットとしてインターネットと通信することができるというこでです。

Terraformでリソース作成し、通信を確認

目的

サブネットのルートテーブルによる、通信の違いを確認したい。

パブリックサブネット、プライベートサブネット内部にそれぞれEC2を配置しています。
サブネットのルートテーブルによる、通信の違いを明確にしたかったので、同じセキュリティグループをアタッチしています。また、両方のEC2インスタンスにパブリックIPを付与しています。

AWS構成図(簡略)

下記のような構成のリソースを作成しました。

結論

パブリックサブネットのEC2のパブリックIPからは接続出来た。(プライベートサブネットのEC2へは不可)

Terraformソースコード

# VPCの作成
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "main-vpc"
  }
}

# インターネットゲートウェイの作成
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "main-igw"
  }
}

# パブリックサブネットの作成
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
  availability_zone       = "ap-northeast-1a" # AZは適宜変更してください

  tags = {
    Name = "public-subnet"
  }
}

# プライベートサブネットの作成
resource "aws_subnet" "private" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.2.0/24"
  map_public_ip_on_launch = false
  availability_zone       = "ap-northeast-1a" # AZは適宜変更してください

  tags = {
    Name = "private-subnet"
  }
}

# Elastic IPの作成(NATゲートウェイ用)
resource "aws_eip" "nat" {
  vpc = true
}

# NATゲートウェイの作成
resource "aws_nat_gateway" "natgw" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public.id

  tags = {
    Name = "main-natgw"
  }
}

# パブリックルートテーブルの作成
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "public-route-table"
  }
}

# プライベートルートテーブルの作成
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.natgw.id
  }

  tags = {
    Name = "private-route-table"
  }
}

# パブリックルートテーブルとサブネットの関連付け
resource "aws_route_table_association" "public_assoc" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

# プライベートルートテーブルとサブネットの関連付け
resource "aws_route_table_association" "private_assoc" {
  subnet_id      = aws_subnet.private.id
  route_table_id = aws_route_table.private.id
}

# セキュリティグループの作成(パブリックセキュリティグループ)
resource "aws_security_group" "public_sg" {
  name        = "public-sg"
  description = "Allow HTTP, HTTPS, and SSH inbound traffic"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # HTTPアクセスを全世界から許可
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # HTTPSアクセスを全世界から許可
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1" # 全てのアウトバウンドトラフィックを許可
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "public-sg"
  }
}

# EC2インスタンスの作成(パブリックサブネット)
resource "aws_instance" "public_ec2" {
  ami                         = "ami-03d25459ad01ac2b9"
  instance_type               = "t2.micro"
  subnet_id                   = aws_subnet.public.id
  security_groups             = [aws_security_group.public_sg.id] # パブリックSGを適用
  associate_public_ip_address = true

  user_data = <<-EOF
              #!/bin/bash
              sudo yum update -y
              sudo yum install -y httpd
              sudo systemctl start httpd
              sudo systemctl enable httpd
              echo "<h1>Welcome to Public EC2 Instance</h1>" | sudo tee /var/www/html/index.html
              EOF

  tags = {
    Name = "public-ec2"
  }
}

# EC2インスタンスの作成(プライベートサブネット)
resource "aws_instance" "private_ec2" {
  ami                         = "ami-03d25459ad01ac2b9"
  instance_type               = "t2.micro"
  subnet_id                   = aws_subnet.private.id
  security_groups             = [aws_security_group.public_sg.id] # パブリックSGを適用
  associate_public_ip_address = true // プライベートサブネット内部でもパブリックIPをもたせる

  user_data = <<-EOF
              #!/bin/bash
              sudo yum update -y
              sudo yum install -y httpd
              sudo systemctl start httpd
              sudo systemctl enable httpd
              echo "<h1>Welcome to Private EC2 Instance</h1>" | sudo tee /var/www/html/index.html
              EOF

  tags = {
    Name = "private-ec2"
  }
}

参考

https://qiita.com/c60evaporator/items/2f24d4796202e8b06a77

Discussion