AWS VPC security: security groups, network ACLs, subnets and NAT
In short: An Amazon VPC is secured in layers. Security groups are stateful allow-lists on each network interface, network ACLs are stateless subnet filters, private subnets keep servers off the internet, NAT gateways allow outbound-only access, and flow logs record what actually happened.
Layers of VPC defence
A Virtual Private Cloud (VPC) gives you a private network in AWS; the basics are in cloud networking basics. Securing it means controlling which subnets are reachable, which ports are open on each resource, and how traffic leaves. AWS gives you two packet filters that work at different points.
Security groups and network ACLs
A security group (SG) is attached to an elastic network interface (ENI), so it protects an EC2 instance, database or load balancer directly. It is stateful: if inbound traffic is allowed, the reply is automatically allowed. It has only allow rules, and by default allows all outbound traffic.
A network ACL (NACL) is attached to a subnet. It is stateless, so you must allow return traffic explicitly, usually on ephemeral ports 1024-65535. Rules are checked in number order and the first match wins, so you can write explicit deny rules, for example to block a known bad address range.
A powerful SG feature is referencing another security group instead of an IP range. The database SG can say "allow TCP 5432 from the app SG", so any app server gets access and nothing else does, even as IPs change.
aws ec2 authorize-security-group-ingress --group-id <db-sg-id> --protocol tcp --port 5432 --source-group <app-sg-id>
Tip: use SGs as your main control and keep NACLs simple. Complex NACLs are a common cause of hard-to-find outages because people forget they are stateless.
Public and private subnet design
- Only the Application Load Balancer (ALB) sits in public subnets and accepts HTTPS from the internet.
- App servers in private subnets accept traffic only from the ALB's security group.
- Databases accept traffic only from the app SG and have no route to the internet.
- A NAT gateway in each zone lets private servers fetch updates without being reachable. One per zone avoids a single point of failure.
- Avoid open SSH or RDP. Use AWS Systems Manager Session Manager for admin access, so no inbound port is needed.
Private access and visibility
| Feature | Security benefit |
|---|---|
| Gateway endpoints (S3, DynamoDB) | Reach these services without the internet; endpoint policies limit which buckets |
| Interface endpoints (PrivateLink) | Private IPs for many AWS and partner services |
| VPC Flow Logs | Record accepted and rejected connections for investigation |
| AWS Network Firewall | Stateful inspection, domain filtering and IPS for whole VPCs |
| Route 53 Resolver DNS Firewall | Block lookups of known malicious domains |
Common mistakes include security groups open to 0.0.0.0/0 on port 22 or 3389, databases with public IPs, a single NAT gateway for all zones, and flow logs never turned on. GuardDuty and AWS Config rules can flag many of these automatically.
Practice
You allow inbound HTTPS in a NACL, but replies never reach clients. Why?
How can a database security group allow only the app servers, even when their IPs change?
How can admins reach private EC2 instances without opening port 22?
Key takeaways
- Security groups are stateful, allow-only and attached to interfaces.
- NACLs are stateless, ordered, subnet-level and can deny.
- Keep only load balancers public; app and data tiers stay private.
- Use one NAT gateway per zone and VPC endpoints for AWS services.
- Turn on flow logs and avoid open SSH or RDP.
Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.