AWS VPC Security Concepts
Security Groups, Private Subnets, and VPC Endpoints
Whenever you need to onboard a new Amazon Web Services (AWS) account or deploy a new isolated network, simply deploying the VPC is not enough. It’s important that you understand some of the security concepts around deploying VPCs (Virtual Private Clouds). A well-architected, secure VPC can protect your resources, control traffic flow and reduce your attack surface.
In this article, I want to focus on three particular concepts. They are core networking components that I believe adds a significant layer of security:
- Security Groups
- Private Subnets
- VPC Endpoints
Security Groups: Defense for Resources
You can think of Security Groups as virtual firewalls. You can place them on most AWS resources like load balancers, EC2, or RDS instances. They control inbound and outbound traffic at the resource level.
Before you deploy your VPC you want to focus on some of the secure concepts, as it’s important to understand the type of resources that’ll be deployed in your subnets and what security control layers are protecting them and managing communication. Thinking about this will give you the opportunity to pre-plan the traffic flow and the type of ports that need to be open against these resources.
Security groups should be least privileged, meaning you want to only allow the lowest access and communication needed and all other communication protocols are denied.
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_security_group" "postgres_sg" {
name = "postgres-db-sg"
description = "Allow Postgres traffic"
vpc_id = aws_vpc.main.id
ingress {
description = "Postgres from specific CIDR"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"] # Replace with allowed CIDR (e.g., on-prem or app subnet)
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "postgres-db-sg"
}
}In this example, we’re looking at deploying a PostgreSQL RDS instance. In the security group, only ports listed are the ones required for it to communicate with clients. There’s no need for it to communicate with the internet unsecured, so port 80 is not listed making it a deny.
There are a few important key concepts to remember when discussing security groups.
Key Concepts:
- Stateful: If an incoming request is allowed, the response is automatically allowed, even if no outbound rule exists.
- Resources/ Instance-specific: They’re applied to resources and instances, not subnets.
- Default Deny: All traffic is denied unless explicitly allowed. This saves time on management as you only have to think about the type of traffic that you want coming through your firewall to your resource.
Best Practices:
- Create least privilege rules: Only open ports necessary for the application (e.g., port 22 for SSH from known IPs).
- Use separate security groups for different layers of your app (e.g., web, app, and DB tiers), but try to look at areas where you can use the same security groups in your infrastructure’s code, for example, if you have to deploy multiple databases.
- Regularly audit and clean up unused rules.
Private Subnets: Shielding Critical Resources
Private subnets are subnets that do not have a route to the internet via an internet gateway. Generally, this makes them ideal for resources that don’t need to be accessed directly from outside the VPC, such as databases or backend services.
Get Christopher B. London’s stories in your inbox
Join Medium for free to get updates from this writer.
I used to think that private subnets only had a few use cases. As time went on, I’ve come to prefer using private subnets almost as a default. Most resources deployed in cloud environments don’t need full open access to the internet and if they need any, you can use a more advanced networking technique to allow outbound traffic to go out through an AWS Transit Gateway that sends traffic to an egress designed VPC that has access to the internet. This hub and spoke type method protects your subnets and ultimately your resources, allowing outbound traffic leaving your cloud environment to a centrally managed outbound VPC.
Why Use Private Subnets?
- Reduce exposure: By keeping critical systems off the internet.
- Enforce tighter controls: Use NAT gateways to control what traffic leaves the subnet. Enhance VPC security by introducing transit gateways.
- Improve compliance: Many security and compliance standards recommend limiting public access to sensitive components.
Best Practices:
- Place databases, internal services, and batch jobs in private subnets.
- Use NAT Gateways only when these systems need to access the internet (e.g., for package updates). Try to keep it minimized to egress VPCs.
- Consider enhancing outbound routes by allowing them to communicate directly to Transit Gateways that route to egress VPCs and use AWS Marketplace firewalls.
VPC Endpoints: Secure and Private Access to AWS Services
VPC Endpoints allow you to privately connect your VPC to supported AWS services without requiring an internet gateway, NAT device, VPN, or AWS Direct Connect.
VPC endpoints are great. Essentially, you pretty much have an AWS ENI in your private subnet and a VPC Endpoint Interface attached to your VPC, and when you’re looking for a supported AWS service the traffic is routed on the internal AWS Network. This setup comes from AWS Private Link and communicates over the AWS managed Network by routing traffic to the internal DNS name of the service that you’re trying to reach, like S3 or Cloudwatch.
For example, if you were looking to pull a file from S3 while on an ec2 instance, your command would make its call, and a DNS resolution process would check for the created internal DNS record against an Amazon provided DNS server (AWS Route53 Resolver), because of the service’s endpoint association with AWS Private Link, and it would then send its requests directly to the S3 service internal address without ever leaving the AWS network.
You’ll need to enable DNS Hostnames and DNS Resolution inside of your VPC settings. This is a great configuration to further eliminate outbound traffic to the internet and keep your deployed resources secure and isolated.
As far as endpoint interfaces are concerned there are two types:
- Interface Endpoints: Elastic network interface collections (ENIs) with private IPs in your VPC.
- Gateway Endpoints: Routes traffic primarily to S3 or DynamoDB using route table entries.
Benefits:
- Improved security: No traffic goes over the public internet.
- Reduced latency and costs: Localized access within AWS’s private network.
- Enhanced control: Combine with endpoint policies and IAM for fine-grained access.
Best Practices:
- Use Interface Endpoints for services especially when considering data transfer.
- Apply resource-based policies to restrict access via endpoints only.
- Use security groups around the EINs that are deployed in your subnet.
Final Thoughts
VPC security is not a single switch but a layered set of practices. By properly designing private subnets, applying least privilege to security groups, and leveraging VPC endpoints, you can create a strong and secure foundation for your cloud infrastructure.
Regular audits, automation, and continuous monitoring through tools like AWS Config and CloudWatch can further help enforce and refine your VPC security posture.









