Pointing an app connector at a public S3 endpoint does not work well. S3 regional endpoints resolve to a large pool of addresses that rotates. The connector keeps discovering new addresses, so the route set grows and never settles. Clients see the churn as ERR_NETWORK_CHANGED in Chrome.
People hit this because the guidance points both ways. Tailscale ships an aws-s3-<region> preset app, while the best-practices guide recommends serving multi-tenant content outside app connectors. S3 regional endpoints are shared multi-tenant address space, but nothing connects those two pages.
The fix is to give the bucket a stable private address first, then advertise that. Three pieces have to line up:
- An S3 interface VPC endpoint with
private_dns_only_for_inbound_resolver_endpoint = false. Resolving the bucket domain inside the VPC now returns the endpoint ENI.
- A split DNS entry on the exact bucket FQDN. Split DNS does not match siblings, so other buckets stay public. This is what scopes the change to one bucket.
- The connector advertises the VPC resolver and the endpoint ENI. The resolver is the only one that knows the private DNS mapping, and a client outside the VPC cannot reach it directly.
The same shape applies to any interface-endpoint service, not just S3.
I have this built as terraform/aws/aws-app-connector-s3-privatelink. It passes tflint, fmt, and validate, and I have applied it and confirmed the private path end to end.
Opened as a draft PR in #51.
Pointing an app connector at a public S3 endpoint does not work well. S3 regional endpoints resolve to a large pool of addresses that rotates. The connector keeps discovering new addresses, so the route set grows and never settles. Clients see the churn as
ERR_NETWORK_CHANGEDin Chrome.People hit this because the guidance points both ways. Tailscale ships an
aws-s3-<region>preset app, while the best-practices guide recommends serving multi-tenant content outside app connectors. S3 regional endpoints are shared multi-tenant address space, but nothing connects those two pages.The fix is to give the bucket a stable private address first, then advertise that. Three pieces have to line up:
private_dns_only_for_inbound_resolver_endpoint = false. Resolving the bucket domain inside the VPC now returns the endpoint ENI.The same shape applies to any interface-endpoint service, not just S3.
I have this built as
terraform/aws/aws-app-connector-s3-privatelink. It passes tflint, fmt, and validate, and I have applied it and confirmed the private path end to end.Opened as a draft PR in #51.