How to Configure VPC Endpoints and AWS PrivateLink for EC2?

This article explains how to configure AWS VPC Endpoints and AWS PrivateLink so isolated Amazon EC2 instances in private subnets can securely access AWS APIs without needing an Internet Gateway or NAT Gateway. By setting up Gateway Endpoints for Amazon S3 and DynamoDB alongside Interface Endpoints powered by AWS PrivateLink for other AWS services, you keep all network traffic entirely within the AWS backbone network. You will learn the architectural differences between endpoint types, how to configure security groups and route tables, how to enable private DNS resolution, and how to apply endpoint policies to enforce least-privilege access.

When Amazon EC2 instances are deployed in isolated private subnets without internet routing (no Internet Gateway and no NAT Gateway), standard requests to public AWS service endpoints fail because traffic has no path to reach public IP addresses. VPC Endpoints solve this by providing private connectivity between your Virtual Private Cloud (VPC) and supported AWS services.

There are two primary types of VPC Endpoints used to reach AWS APIs:

  1. Interface VPC Endpoints (AWS PrivateLink): Powered by AWS PrivateLink, Interface Endpoints provision an Elastic Network Interface (ENI) with a private IP address directly inside your subnet. Traffic destined for an AWS API (such as Amazon SQS, AWS KMS, Amazon CloudWatch, or AWS Systems Manager) is directed to this private IP address via DNS resolution.
  2. Gateway VPC Endpoints: Used exclusively for Amazon S3 and Amazon DynamoDB. Instead of creating an ENI inside your subnet, a Gateway Endpoint acts as a target in your VPC route table. Traffic destined for S3 or DynamoDB is routed using an AWS-managed prefix list directly over the AWS private network.

Prerequisites for Isolated EC2 Connectivity

Before creating VPC Endpoints, ensure your VPC and EC2 environment meet the following network and identity requirements:

  • VPC DNS Settings: For Interface Endpoints to seamlessly override public service hostnames (like sqs.us-east-1.amazonaws.com) with private IP addresses, your VPC must have both enableDnsHostnames and enableDnsSupport set to true.
  • IAM Instance Profile: Isolated EC2 instances still require AWS Identity and Access Management (IAM) permissions to authenticate API calls. Attach an IAM role with the necessary policies to your EC2 instance.
  • Network Capacity: Ensure your target private subnets have available IPv4 addresses so AWS PrivateLink can allocate an ENI per Availability Zone.

To verify and enable DNS settings on your VPC using the AWS CLI, run the following commands:

aws ec2 modify-vpc-attribute \
    --vpc-id vpc-0a1b2c3d4e5f6g7h8 \
    --enable-dns-support "{\"Value\":true}"

aws ec2 modify-vpc-attribute \
    --vpc-id vpc-0a1b2c3d4e5f6g7h8 \
    --enable-dns-hostnames "{\"Value\":true}"

Step 1: Configure Security Groups for Interface Endpoints

Because Interface Endpoints deploy Elastic Network Interfaces into your subnets, they are protected by VPC Security Groups. You must create a Security Group for the VPC Endpoint that allows inbound HTTPS (port 443) traffic from your isolated EC2 instances.

  1. Create a Security Group specifically for your VPC Endpoints (e.g., vpce-security-group).
  2. Add an inbound rule permitting TCP port 443 from the Security Group attached to your isolated EC2 instances, or from the CIDR block of your private subnets (such as 10.0.1.0/24).
  3. Ensure your EC2 instance Security Group allows outbound TCP port 443 traffic to the endpoint Security Group or VPC CIDR.

Using the AWS CLI, you can create the endpoint Security Group and authorize ingress traffic from a VPC CIDR of 10.0.0.0/16:

aws ec2 create-security-group \
    --group-name vpce-tls-sg \
    --description "Allow HTTPS from VPC to Interface Endpoints" \
    --vpc-id vpc-0a1b2c3d4e5f6g7h8

aws ec2 authorize-security-group-ingress \
    --group-id sg-0123456789abcdef0 \
    --protocol tcp \
    --port 443 \
    --cidr 10.0.0.0/16

To allow an isolated EC2 instance to communicate with most AWS APIs (for example, AWS KMS, Amazon EC2 API, or AWS Systems Manager), create an Interface VPC Endpoint.

Using the AWS Management Console

  1. Open the Amazon VPC Console and navigate to Endpoints in the left navigation pane.
  2. Choose Create endpoint.
  3. Enter a name tag (for example, vpce-kms-private).
  4. Under Service category, keep AWS services selected.
  5. In the Services list, filter for your desired service (e.g., com.amazonaws.us-east-1.kms) and ensure the Type is Interface.
  6. Select your target VPC.
  7. Under Additional settings, ensure Enable DNS name (Private DNS) is checked. This creates a Route 53 private hosted zone managed by AWS so existing SDKs and CLI commands automatically resolve standard regional endpoints to the private ENI IP addresses without code changes.
  8. Select the Subnets (one per Availability Zone where your isolated EC2 instances reside).
  9. Select the Security group created in Step 1 (vpce-tls-sg).
  10. Under Policy, choose Full access or define a custom VPC Endpoint Policy to restrict which principals and resources can be accessed through this endpoint.
  11. Click Create endpoint.

Using the AWS CLI

You can create the same Interface Endpoint with Private DNS enabled via the AWS CLI:

aws ec2 create-vpc-endpoint \
    --vpc-id vpc-0a1b2c3d4e5f6g7h8 \
    --vpc-endpoint-type Interface \
    --service-name com.amazonaws.us-east-1.kms \
    --subnet-ids subnet-01111111111111111 subnet-02222222222222222 \
    --security-group-ids sg-0123456789abcdef0 \
    --private-dns-enabled

Required Endpoints for AWS Systems Manager Session Manager

If your EC2 instances are completely isolated and you want shell access without SSH keys, bastions, or public IP addresses, configure the following Interface Endpoints for AWS Systems Manager (SSM):

  • com.amazonaws.<region>.ssm (Systems Manager core service)
  • com.amazonaws.<region>.ssmmessages (Session Manager channel communication)
  • com.amazonaws.<region>.ec2messages (Run Command and SSM Agent messaging)

Step 3: Create a Gateway VPC Endpoint for S3 and DynamoDB

For Amazon S3 and Amazon DynamoDB, Gateway Endpoints are generally preferred for general API access because they incur no hourly or data-processing charges and scale automatically via your VPC route tables (note that S3 also supports Interface Endpoints if you require access from on-premises networks via VPN or Direct Connect).

Using the AWS Management Console

  1. In the Amazon VPC Console, navigate to Endpoints and click Create endpoint.
  2. Select AWS services and search for com.amazonaws.<region>.s3 with the Gateway type.
  3. Select your VPC.
  4. Select the Route tables associated with your isolated private subnets. AWS will automatically inject a route linking the S3 prefix list (e.g., pl-63a5400a) to the Gateway Endpoint ID (vpce-xxxx).
  5. Configure your endpoint policy and click Create endpoint.

Using the AWS CLI

Run the following command to provision an S3 Gateway Endpoint and attach it to your private route table:

aws ec2 create-vpc-endpoint \
    --vpc-id vpc-0a1b2c3d4e5f6g7h8 \
    --vpc-endpoint-type Gateway \
    --service-name com.amazonaws.us-east-1.s3 \
    --route-table-ids rtb-0987654321fedcba0

Step 4: Harden Access with VPC Endpoint Policies

By default, VPC Endpoints allow full access to the target service for any IAM principal in the VPC that has valid IAM credentials. To implement defense-in-depth and prevent data exfiltration from isolated instances to unauthorized external AWS accounts, attach a restrictive VPC Endpoint Policy.

For example, the following policy restricts an S3 Gateway Endpoint so EC2 instances can only read and write objects within your organization's specific bucket (my-isolated-app-bucket), blocking access to any external S3 buckets:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RestrictToSpecificBucket",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-isolated-app-bucket",
        "arn:aws:s3:::my-isolated-app-bucket/*"
      ]
    }
  ]
}

Note that if your isolated EC2 instances use yum, dnf, or apt to install packages from Amazon Linux or Ubuntu repositories hosted on S3, or pull container images from Amazon ECR (which stores image layers in S3), you must also permit s3:GetObject to the respective AWS-owned regional S3 buckets in your S3 Gateway Endpoint policy.

Step 5: Verify Private Connectivity from the EC2 Instance

Once your endpoints transition to the Available state, connect to your isolated EC2 instance (for example, via AWS Systems Manager Session Manager) and verify that traffic is resolving and routing privately.

1. Verify DNS Resolution for Interface Endpoints

Use dig or nslookup to verify that the standard AWS service hostname resolves to a private IP address inside your VPC CIDR range rather than a public IP address:

dig +short kms.us-east-1.amazonaws.com

If Private DNS is working properly, the output will show the private IPv4 addresses of the endpoint ENIs inside your subnets (such as 10.0.1.45 and 10.0.2.88).

2. Test TCP Connectivity and API Execution

Test port 443 connectivity and run an AWS CLI command against the service:

aws kms list-keys --region us-east-1
aws s3 ls s3://my-isolated-app-bucket --region us-east-1

If both commands return results without hanging or timing out, your isolated EC2 instance is successfully communicating with AWS APIs over AWS PrivateLink and Gateway Endpoints without traversing the public internet.

Troubleshooting Common Issues

  • Connection Timeouts (Connect timeout on endpoint URL):
    • For Interface Endpoints, verify that Private DNS is enabled on the endpoint and that enableDnsSupport and enableDnsHostnames are enabled on the VPC. Check that the endpoint's Security Group allows inbound TCP port 443 from the EC2 instance's private IP or Security Group.
    • For Gateway Endpoints, verify that the route table associated with the EC2 instance's subnet contains a route targeting the vpce-... ID for the service's prefix list, and ensure Network ACLs allow ephemeral return traffic.
  • Region Mismatch: VPC Endpoints are regional constructs. If your EC2 instance and VPC Endpoints are in us-east-1, ensure your AWS CLI or SDK explicitly targets us-east-1 (e.g., AWS_DEFAULT_REGION=us-east-1). Cross-region API calls will attempt to route to the internet unless inter-region VPC peering and custom DNS routing are configured.
  • Access Denied (AccessDenied or 403 Forbidden): When a call fails immediately with an authorization error rather than a network timeout, PrivateLink connectivity is functioning properly. Check three places: the EC2 instance's IAM Role policy, the VPC Endpoint Policy, and any resource-based policy on the target service (such as an S3 bucket policy or KMS key policy, especially if it uses an aws:SourceVpce or aws:SourceVpc condition).