Amazon EC2 Should Use VPC Endpoints
Overview
This check verifies that your VPCs have an interface VPC endpoint configured for the Amazon EC2 service. VPC endpoints allow your resources to communicate with EC2 APIs privately, without sending traffic over the public internet.
Risk
Without a VPC endpoint for EC2, API calls from your VPC to the EC2 service travel through the public internet. This creates several risks:
- Data exposure: Network traffic could be intercepted or monitored
- DNS hijacking: Attackers could redirect your API calls to malicious endpoints
- Availability issues: If your NAT gateway or internet gateway fails, EC2 API calls will stop working
- Compliance gaps: Many compliance frameworks require keeping sensitive traffic off the public internet
Remediation Steps
Prerequisites
You will need:
- Access to the AWS Console with permissions to create VPC endpoints
- A VPC with at least one private subnet
- A security group that allows HTTPS traffic (port 443)
Required IAM permissions
To create VPC endpoints, your IAM user or role needs these permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:CreateVpcEndpoint",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeVpcs",
"ec2:ModifyVpcEndpoint"
],
"Resource": "*"
}
]
}
AWS Console Method
- Open the VPC console at https://console.aws.amazon.com/vpc/
- In the left navigation, click Endpoints
- Click Create endpoint
- For Name tag, enter a descriptive name (e.g.,
ec2-endpoint) - Under Service category, select AWS services
- In the Services search box, type
ec2and selectcom.amazonaws.us-east-1.ec2 - For VPC, select the VPC that needs the endpoint
- Under Subnets, select at least one subnet in each Availability Zone where you have resources
- For Security groups, select a security group that allows inbound HTTPS (port 443) from your VPC CIDR
- Leave Policy set to Full access (you can restrict this later if needed)
- Click Create endpoint
The endpoint will show "Pending" status briefly, then change to "Available" within a few minutes.
AWS CLI
First, identify your VPC ID, subnet IDs, and security group ID:
# List your VPCs
aws ec2 describe-vpcs \
--region us-east-1 \
--query 'Vpcs[*].[VpcId,Tags[?Key==`Name`].Value|[0]]' \
--output table
# List subnets in a specific VPC
aws ec2 describe-subnets \
--region us-east-1 \
--filters "Name=vpc-id,Values=<your-vpc-id>" \
--query 'Subnets[*].[SubnetId,AvailabilityZone,Tags[?Key==`Name`].Value|[0]]' \
--output table
Create the VPC endpoint:
aws ec2 create-vpc-endpoint \
--region us-east-1 \
--vpc-id <your-vpc-id> \
--service-name com.amazonaws.us-east-1.ec2 \
--vpc-endpoint-type Interface \
--subnet-ids <subnet-id-1> <subnet-id-2> \
--security-group-ids <your-security-group-id> \
--private-dns-enabled
Example with real values:
aws ec2 create-vpc-endpoint \
--region us-east-1 \
--vpc-id vpc-0abc123def456789 \
--service-name com.amazonaws.us-east-1.ec2 \
--vpc-endpoint-type Interface \
--subnet-ids subnet-0123456789abcdef0 subnet-0fedcba9876543210 \
--security-group-ids sg-0123456789abcdef0 \
--private-dns-enabled
CloudFormation
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC Endpoint for Amazon EC2 Service
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: The ID of the VPC where the endpoint will be created
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: The subnet IDs where the endpoint network interfaces will be created
SecurityGroupIds:
Type: List<AWS::EC2::SecurityGroup::Id>
Description: Security group IDs to associate with the endpoint
Resources:
EC2VPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VpcId
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ec2'
VpcEndpointType: Interface
SubnetIds: !Ref SubnetIds
SecurityGroupIds: !Ref SecurityGroupIds
PrivateDnsEnabled: true
Outputs:
EndpointId:
Description: The ID of the VPC endpoint
Value: !Ref EC2VPCEndpoint
EndpointDnsEntries:
Description: DNS entries for the VPC endpoint
Value: !Join
- ', '
- !GetAtt EC2VPCEndpoint.DnsEntries
Deploy the stack:
aws cloudformation create-stack \
--region us-east-1 \
--stack-name ec2-vpc-endpoint \
--template-body file://template.yaml \
--parameters \
ParameterKey=VpcId,ParameterValue=<your-vpc-id> \
ParameterKey=SubnetIds,ParameterValue="<subnet-id-1>,<subnet-id-2>" \
ParameterKey=SecurityGroupIds,ParameterValue=<your-security-group-id>
Terraform
variable "vpc_id" {
description = "The ID of the VPC where the endpoint will be created"
type = string
}
variable "subnet_ids" {
description = "The subnet IDs where the endpoint network interfaces will be created"
type = list(string)
}
variable "security_group_ids" {
description = "Security group IDs to associate with the endpoint"
type = list(string)
}
resource "aws_vpc_endpoint" "ec2" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.us-east-1.ec2"
vpc_endpoint_type = "Interface"
subnet_ids = var.subnet_ids
security_group_ids = var.security_group_ids
private_dns_enabled = true
tags = {
Name = "ec2-vpc-endpoint"
}
}
output "endpoint_id" {
description = "The ID of the VPC endpoint"
value = aws_vpc_endpoint.ec2.id
}
output "dns_entry" {
description = "DNS entries for the VPC endpoint"
value = aws_vpc_endpoint.ec2.dns_entry
}
Apply the configuration:
terraform init
terraform apply -var="vpc_id=<your-vpc-id>" \
-var='subnet_ids=["<subnet-id-1>","<subnet-id-2>"]' \
-var='security_group_ids=["<your-security-group-id>"]'
Verification
After creating the endpoint, verify it is working:
- In the VPC console, go to Endpoints
- Find your new endpoint and confirm the Status shows Available
- Check that Private DNS names enabled shows Yes
CLI verification
# List EC2 VPC endpoints
aws ec2 describe-vpc-endpoints \
--region us-east-1 \
--filters "Name=service-name,Values=com.amazonaws.us-east-1.ec2" \
--query 'VpcEndpoints[*].[VpcEndpointId,VpcId,State,PrivateDnsEnabled]' \
--output table
Expected output shows available state and True for private DNS:
------------------------------------------------------------------
| DescribeVpcEndpoints |
+---------------------------+---------------+-----------+---------+
| vpce-0abc123def456789 | vpc-0xyz... | available| True |
+---------------------------+---------------+-----------+---------+
Additional Resources
- AWS PrivateLink User Guide
- Access AWS services through AWS PrivateLink
- VPC endpoint pricing
- AWS services that integrate with AWS PrivateLink
Notes
- Pricing: Interface VPC endpoints are charged hourly plus data processing fees. Check current pricing before deploying to multiple AZs.
- Private DNS: When private DNS is enabled, EC2 API calls from your VPC automatically route through the endpoint. No application changes are needed.
- Security groups: The security group must allow inbound HTTPS (port 443) from the IP ranges that will use the endpoint.
- Multiple AZs: For high availability, create endpoint network interfaces in multiple Availability Zones.
- Endpoint policies: You can add an endpoint policy to restrict which EC2 actions are allowed through the endpoint, adding another layer of access control.