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
}
}