Private DNS on Interface VPC Endpoints

When I first enabled Private DNS on an interface VPC endpoint, I thought AWS would give me another DNS name for the endpoint. The endpoint already had one. The setting changed the answer for the AWS service hostname my application was using.

Let’s say an EC2 instance in a private subnet calls Secrets Manager at https://secretsmanager.us-east-1.amazonaws.com. We create an interface endpoint for Secrets Manager in the same VPC. The instance and the URL in our code stay where they are. What happens to the DNS lookup?

Before enabling Private DNS

An interface endpoint creates an elastic network interface (ENI) in each subnet you select. Each ENI gets a private IP address. In our example, let’s give them 10.0.2.14 and 10.0.3.21.

AWS also gives the endpoint its own regional DNS name. An example looks like this:

vpce-0123456789abcdef0-abc123.secretsmanager.us-east-1.vpce.amazonaws.com

That name resolves to the endpoint ENIs, so we can use it even with Private DNS turned off. But our EC2 instance is still calling secretsmanager.us-east-1.amazonaws.com. The usual hostname continues to resolve to the public Secrets Manager endpoint. Simply creating the interface endpoint hasn’t changed the name used by the application.

After enabling Private DNS

With Private DNS enabled, AWS creates a hidden, AWS-managed private hosted zone for the VPC. The record for the service’s usual hostname resolves to the private IPs of the endpoint ENIs when queried through the VPC’s Route 53 Resolver. Both DNS resolution and DNS hostnames must be enabled on the VPC.

The two private IPs in the diagram are examples, not the result of a live lookup.

The EC2 instance resolves the same Secrets Manager hostname to a public service address with Private DNS off, and to two endpoint ENI private IPs with it on.

The SDK can keep using the normal Secrets Manager URL. Its request now reaches the interface endpoint because the DNS answer changed.

What happens with API Gateway?

Suppose the same EC2 instance calls two API Gateway APIs in us-east-1:

The private API has an endpoint type of PRIVATE. Only REST APIs support that endpoint type; an HTTP API cannot be made private this way.

To invoke the private REST API, we add an interface endpoint for the execute-api service. With Private DNS on, the VPC Resolver answers queries for default *.execute-api.us-east-1.amazonaws.com hostnames with the endpoint ENI IPs. Both API IDs share this DNS namespace, so requests to either URL reach those ENIs.

The private REST API can be invoked through the endpoint, provided its resource policy allows the VPC or VPC endpoint. The public API’s default URL reaches the same endpoint, which cannot invoke public APIs. A request to that URL from this VPC fails, even if the public API works elsewhere. The same issue applies to an edge-optimized API’s default execute-api URL.

With Private DNS enabled, both the private REST API and public API default execute-api hostnames resolve to the VPC endpoint. The private API can be invoked if its policy allows access; the public API default URL fails.

Calling private and public APIs from the same VPC

If we keep Private DNS enabled, we can call the public API through a custom domain name instead of its default execute-api URL. That domain won’t match the endpoint’s private DNS records. For a Regional custom domain, the DNS record should be an A alias rather than a CNAME to an execute-api name. An EC2 instance in a private subnet still needs an outbound route, such as a NAT gateway, to reach the public API.

Another option is to turn off Private DNS for the execute-api endpoint. The public API’s default hostname then resolves normally. The private REST API needs a different way to reach the endpoint: its endpoint-specific DNS name, along with a header that identifies the API.

For example, using x-apigw-api-id:

curl -v \
  'https://vpce-0fedcba9876543210-abc123.execute-api.us-east-1.vpce.amazonaws.com/prod/orders' \
  -H 'x-apigw-api-id: a1b2c3d4e5'

Or using the Host header:

curl -v \
  'https://vpce-0fedcba9876543210-abc123.execute-api.us-east-1.vpce.amazonaws.com/prod/orders' \
  -H 'Host: a1b2c3d4e5.execute-api.us-east-1.amazonaws.com'

The endpoint-specific hostname gets the request to the execute-api endpoint. The header tells API Gateway which private REST API should receive it. Both forms are documented ways to invoke a private API.

If we associate the VPC endpoint with the private API, API Gateway generates an alias URL that doesn’t require either header:

https://a1b2c3d4e5-vpce-0fedcba9876543210.execute-api.us-east-1.amazonaws.com/prod/orders

We can also leave Private DNS off and create a private hosted zone for each private API ID that should resolve through the endpoint. The other default execute-api hostnames will continue to resolve publicly. Either way, the public API still needs normal outbound connectivity from our private subnet.

Thanks for reading.