T Techclick ← All lessons
Amazon · AWS security · Lesson 6 of 10

Endpoints: gateway for S3, PrivateLink for the rest, not NAT

App VPC 10.51.0.0/16 has no internet and still must write logs to tc-lab-app-logs. Someone added a NAT Gateway “so S3 works.” That is the expensive, wrong default when a gateway endpoint exists for S3. Interface endpoints (PrivateLink) are the ENI path for most other AWS APIs.

Updated 2026-08-18·18 min read·L2 primary·Quiz at end

After this page you can

The ticket

Finance: NAT data processing is huge. Flow logs: app subnets talking to S3 public prefixes via NAT. There is no gateway endpoint on the app route table. Private instances were never supposed to need a NAT just to reach S3.

Quick interview answer

Two VPC endpoint families. Gateway endpoint: Amazon S3 and DynamoDB only. You add a prefix-list route to the VPC route table; traffic to those services stays on the AWS network. Official docs: no additional charge for using gateway endpoints. Interface endpoint (AWS PrivateLink): an ENI in your subnet, security group, optional private DNS, used for most other AWS APIs (KMS, Secrets Manager, CloudTrail, EC2 API, …). You pay for interface endpoints (hours + data processing — do not invent a price). NAT to the public S3 endpoint from a private subnet is the expensive/wrong default when a gateway endpoint exists.

Hero · stay on the AWS network
S3 gateway endpoint on the route table versus NAT to public S3
S3 does not need a NAT. Your route table does need a prefix list.
Lab data · dummy only

Account 111122223333 · OUs Security / Workloads · hub VPC 10.50.0.0/16 · app VPC 10.51.0.0/16 · SG sg-web · NACL acl-app · bucket tc-lab-app-logs · role AppEC2Role · TGW tgw-lab · VGW public 203.0.113.60 · on-prem FortiGate WAN 203.0.113.10. Dummy account ID. Not yours.

Mental model

Gateway endpointInterface endpoint
ServicesS3, DynamoDBPrivateLink — most other AWS services
How it attachesPrefix list in the route tableENI in a subnet + SG
This labApp RT: pl-s3vpce-s3 for tc-lab-app-logsKMS / Secrets Manager / CloudTrail ENIs in 10.51.0.0/16
Wrong default0.0.0.0/0 → NAT just so S3 worksLeaving private DNS off and still calling the public API
Two paths to S3
app 10.51/16 vpce gateway · S3 NAT → public S3 tc-lab-app-logs PrivateLink ENI

Top path is the design. Bottom path is the bill and the extra public exposure.

https://111122223333.signin.aws.amazon.com/console
Training mock · not live
VPCIAMS3CloudTrail
VPC → Endpoints

vpce-s3 (Gateway) + interface endpoints

com.amazonaws.<region>.s3 · route table = app VPC 10.51.0.0/16
pl-xxxx → vpce-s3 (not 0.0.0.0/0 via nat-xxx)
KMS, Secrets Manager · ENI + SG · private DNS on
Optional least privilege to tc-lab-app-logs
Remove S3-via-NAT once the gateway route is proven
Gateway for S3/Dynamo. Interface for PrivateLink. Training mock.

How to choose

DestinationUseDo not
S3 / DynamoDB from a private subnetGateway endpoint + RT prefix listNAT “because it needs internet”
KMS, Secrets Manager, STS, EC2 APIInterface endpoint + SGOpen the app SG to 0.0.0.0/0 so the public API works
True internet (patches from the public web)NAT or a pull-through that you meantPretend S3 is that problem

Runbook

  1. Side A — gateway

    Create the S3 gateway endpoint in app VPC 10.51.0.0/16. Associate the app private route tables. Confirm a prefix-list route, not a default route via NAT.

  2. Side B — interface

    For KMS/Secrets: interface endpoints in the private subnets, SG allowing 443 from sg-app, private DNS enabled.

  3. Side C — prove

    From the instance (no public IP): write an object to tc-lab-app-logs. aws ec2 describe-route-tables shows vpce-. NAT bytes to S3 should fall.

aws cli · endpoint proof
aws ec2 describe-vpc-endpoints --filters Name=vpc-id,Values=vpc-app \
  --query 'VpcEndpoints[].{Id:VpcEndpointId,Type:VpcEndpointType,Service:ServiceName,State:State}'
# vpce-s3     Gateway    com.amazonaws.ap-south-1.s3
# vpce-kms    Interface  com.amazonaws.ap-south-1.kms

aws ec2 describe-route-tables --filters Name=vpc-id,Values=vpc-app \
  --query 'RouteTables[].Routes'
# DestinationPrefixListId: pl-xxxx   GatewayId: vpce-s3

aws s3 cp /etc/os-release s3://tc-lab-app-logs/probe/os-release
# from the private instance — no public IP required

Four failures

1 · NAT for S3 by habit

Works, costs, and teaches the wrong default. Add the gateway endpoint; then drop S3 from the NAT path.

2 · Gateway endpoint, wrong route table

You attached it to the public RT. The private app RT still points at NAT or has no S3 route.

3 · Interface endpoint, private DNS off

SDK still calls the public hostname and fails in a private subnet.

4 · Endpoint policy deny + IAM allow

Both must allow. The endpoint policy is another ceiling, like an SCP for that path.

How to prove it

Close only when

1) describe-vpc-endpoints shows Gateway for S3. 2) The app RT has the prefix-list route. 3) A private instance wrote to tc-lab-app-logs. 4) You did not open 0.0.0.0/0 to “make S3 work.”

Traps

BeliefFact
S3 needs a NATS3 has a gateway endpoint
Gateway endpoints exist for KMSKMS is interface / PrivateLink
Endpoint policy grants IAMIt only filters, like an SCP

Knowledge check

Judgment items. One best answer. Reasons send you back to the matching section.

Q1

Gateway endpoints exist for…

Correct: b. Official PrivateLink / endpoint concepts.
Q2

Private subnet talking to S3 via NAT is…

Correct: b. Ticket.
Q3

An interface endpoint is…

Correct: b. PrivateLink.
Q4

Proof the S3 gateway is in path?

Correct: a. Route table.
Q5

KMS from a private subnet should use…

Correct: b. KMS is not a gateway service.
Q6

Official docs on gateway endpoint extra charge?

Correct: b. Do not invent prices; official: no additional charge.

AWS security class series: Shared + IAM · Org / OU · SG vs NACL · Role not key · KMS + Secrets · VPC endpoint · Trail + GuardDuty · TGW hub · S3 public · Interview

Sources

Related: AWS session factory · Migration series.