The Problem
You need to move data from EC2 to an S3 bucket. Requirements:
- No API calls or data over the public internet
- Only this specific EC2 instance can upload to the bucket
The Solution
Create an interface VPC endpoint for S3 in the subnet where the EC2 instance lives. Attach a bucket policy that only allows the EC2 instance’s IAM role.
How It Works
Step 1: Create the Interface Endpoint
1
2
3
4
5
6
7
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--service-name com.amazonaws.us-east-1.s3 \
--vpc-endpoint-type Interface \
--subnet-ids subnet-app-1a \
--security-group-ids sg-s3-endpoint \
--private-dns-enabled
An interface endpoint creates an ENI (elastic network interface) in your subnet with a private IP. All S3 API calls resolve to this private IP via private DNS. Traffic stays on the AWS backbone.
Step 2: Security Group on the Endpoint
1
2
3
4
aws ec2 authorize-security-group-ingress \
--group-id sg-s3-endpoint \
--protocol tcp --port 443 \
--source-group sg-app-instance
Only the app instance’s security group can reach the endpoint. This is a network-level layer on top of IAM.
Step 3: Bucket Policy Restricted to the EC2 IAM Role
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowOnlyAppInstance",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/AppInstanceRole"
},
"Action": ["s3:PutObject", "s3:GetObject"],
"Resource": "arn:aws:s3:::app-uploads/*"
}]
}
Combined with an explicit deny for anyone else:
1
2
3
4
5
6
7
8
9
10
11
12
{
"Sid": "DenyAllOthers",
"Effect": "Deny",
"NotPrincipal": {
"AWS": "arn:aws:iam::123456789012:role/AppInstanceRole"
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::app-uploads",
"arn:aws:s3:::app-uploads/*"
]
}
Why Interface (Not Gateway) Here?
Gateway endpoints do not support security groups. If you need network-level restriction — like “only this specific security group can talk to S3” — you need an interface endpoint. Gateway endpoints only support route-level access.
Interface endpoints cost roughly $7.30 per endpoint per AZ per month plus $0.01/GB processed. Gateway endpoints are free but less restrictive.
Why Not the Alternatives?
Gateway VPC endpoint + security groups — Gateway endpoints cannot have security groups attached. Security groups only work on interface endpoints.
nslookup for S3’s “private IP” — S3 is a public service. It has no private IP that DNS can return. That is precisely why VPC endpoints exist.
Using ip-ranges.json and static routes — That file contains public IP ranges. Static routes to public IPs still send traffic out through the internet gateway.
Key Takeaways
- Interface endpoints support security groups for network-level restriction
- Gateway endpoints are free but only offer route-table-level access control
- Combine VPC endpoint (network layer) + bucket policy with
Principal/NotPrincipal(identity layer) for defense in depth - S3 has no discoverable private IP — always use a VPC endpoint for private access
- Use
NotPrincipalin an explicit deny to allowlist exactly one identity
Never miss a story from us, subscribe to our newsletter