Understanding Public vs. Private Subnets in AWS VPC: Why a Private Subnet Server Shows “Open to the World” Warning


2 views

In AWS VPC architecture, the fundamental difference lies in route table configurations:

# Public subnet route table example
Destination: 0.0.0.0/0
Target: igw-xxxxxx (Internet Gateway)

# Private subnet route table example  
Destination: 0.0.0.0/0
Target: nat-xxxxxx (NAT Gateway)

Public subnets have direct internet accessibility via an Internet Gateway (IGW), while private subnets route outbound traffic through NAT devices.

When you see the "may be open to the world" warning on a private subnet instance, it's because:

# Example problematic security group (SG)
aws ec2 authorize-security-group-ingress \
    --group-id sg-xxxxxx \
    --protocol all \
    --cidr 0.0.0.0/0

Although the subnet is private, any resource with this SG allows internal access from:

  • Other instances in the VPC
  • VPN-connected networks
  • Direct Connect connections
  • Peered VPCs

Consider this common web application setup:

# Web tier (public subnet)
- Route: 0.0.0.0/0 → igw
- SG: Allow HTTP/HTTPS from 0.0.0.0/0

# Application tier (private subnet)
- Route: 0.0.0.0/0 → nat
- SG: Allow TCP/8080 from web tier SG
# NOT 0.0.0.0/0!

Best practice security group configuration:

aws ec2 authorize-security-group-ingress \
    --group-id sg-privateserver \
    --protocol tcp \
    --port 22 \
    --source-group sg-bastion

Key principles:

  • Reference security groups rather than CIDR blocks when possible
  • Implement the principle of least privilege
  • Use network ACLs for additional subnet-level protection

When troubleshooting private subnet access:

# Check effective routes
aws ec2 describe-route-tables --filters "Name=vpc-id,Values=vpc-xxxxxx"

# Verify security group associations
aws ec2 describe-instances --instance-ids i-xxxxxx | jq '.Reservations[].Instances[].SecurityGroups'

Remember that VPC Flow Logs are invaluable for diagnosing actual traffic patterns.

Be aware of these scenarios where private subnets can receive external traffic:

  • VPC peering connections from other accounts
  • Transit Gateway attachments
  • ClassicLink instances
  • Improperly configured Site-to-Site VPNs

In AWS VPC architecture, the key difference lies in route table configurations:

# Public subnet route table example
Destination: 0.0.0.0/0
Target: igw-123456 # Internet Gateway

# Private subnet route table example 
Destination: 0.0.0.0/0
Target: nat-123456 # NAT Gateway

Even in private subnets, security group warnings appear because:

  • Private subnets can still access the internet via NAT Gateway
  • Intra-VPC communication isn't blocked by subnet privacy
  • Misconfigured NACLs could override subnet isolation

Consider this Terraform configuration that creates potential exposure:

resource "aws_security_group" "private_sg" {
  vpc_id = aws_vpc.main.id
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # Warning trigger
  }
}

For proper private subnet security:

# Correct security group for private instances
resource "aws_security_group" "restricted_sg" {
  vpc_id = aws_vpc.main.id
  
  # Allow internal VPC traffic only
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.main.cidr_block]
  }
  
  # Outbound via NAT
  egress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Supplement with NACL rules at subnet level:

resource "aws_network_acl" "private_nacl" {
  vpc_id = aws_vpc.main.id
  subnet_ids = [aws_subnet.private.id]

  egress {
    protocol   = "-1"
    rule_no    = 100
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  ingress {
    protocol   = "-1"
    rule_no    = 100
    action     = "allow"
    cidr_block = aws_vpc.main.cidr_block
    from_port  = 0
    to_port    = 0
  }
}