One Istio mesh, two gateways: public traffic through the edge, internal traffic never leaving the VPC.
Private by default
internal APIs never leave the VPC
Wildcard TLS
issued from AWS Private CA
Public / internal
two gateways on one mesh
Pod Identity
no node wide AWS credentials
A platform usually starts out with one way in: a public load balancer per service. That is fine until some of those services are only ever meant to be called by other services, or by the people running the platform. At that point a public endpoint with an authentication check in front of it is doing more work than it should.
I separated the two. Anything public keeps its route through Cloudflare and an Istio public gateway. Anything internal moves behind a second gateway that only answers inside the VPC, on a private Network Load Balancer with no public IP, using names that only resolve on a Route 53 private hosted zone. Certificates come from AWS Private CA with ACM wildcards, so adding an internal service does not mean issuing another certificate by hand.
Internal services were exposed the same way public ones were, which made the security story depend entirely on what sat in front of each endpoint.
Treat public and internal as two separate paths through one mesh. Exposure becomes a property of the route, not an accident of how the service was deployed.
Public edge
Cloudflare with WAF, bot controls and rate limits in front of the public gateway
Public gateway
Istio gateway carrying only the routes meant to be internet facing
Private DNS
Route 53 private hosted zone, so internal names resolve only inside the VPC
Private entry
Internal NLB with no public IP, reachable only from inside the network
Internal gateway
A second Istio gateway terminating TLS for internal routes
TLS
AWS Private CA with ACM wildcards, so new internal hosts need no manual certificate
Identity
EKS Pod Identity and Pod Security Admission instead of node wide credentials
Secrets
Vault and External Secrets Operator syncing into the cluster
Delivery
Argo CD and FluxCD, so routing and policy changes land through git

mesh.txt
Internet
→ Cloudflare (WAF, bot, rate limits)
→ Istio public gateway
└── public VirtualServices only
Internal client
→ Route 53 private hosted zone
→ private NLB (no public IP)
→ Istio internal gateway (AWS Private CA + ACM wildcard)
└── internal VirtualServicesBefore
After
Have a project in mind?
Let's build something together.