Teams that run both an API gateway and a service mesh often cannot say which layer is responsible for authenticating callers and authorising requests, so controls end up duplicated in some paths and missing in others. The gateway governs north-south traffic: external clients calling your APIs. The mesh governs east-west traffic: services calling each other inside the platform. They overlap in features (TLS, routing, rate limits, JWT checks), but they prove different things about different callers. This guide covers the threat model and architecture of each, where authentication and authorisation belong, workload identity with mTLS and SPIFFE, how to avoid redundant token validation without leaving gaps, failure modes and a reference architecture. It is for platform engineers, API owners and security architects designing or reviewing cloud-native platforms.
Key takeaways
- Different callers, different proofs. The gateway authenticates external users and client applications with OAuth tokens or API keys; the mesh authenticates workloads to each other with certificates.
- Coarse at the edge, fine in the service. Put token validity, client identity, quotas and coarse scope checks at the gateway; put service-to-service allow rules in the mesh; keep object-level decisions in the service.
- Mesh defaults are usually permissive. Istio accepts plain text in PERMISSIVE mode and allows all requests when no authorisation policy exists; Linkerd's default inbound policy allows all requests. Enforcement is something you switch on.
- Do not forward the edge token everywhere. Exchange it for a narrower internal token or pass verified claims over mTLS, so an internal compromise does not yield a token valid at the edge.
- Duplication is fine when deliberate. Validating a token twice costs little; the failure is two layers that each assume the other checked.
The short answer
You usually need both once you have more than a handful of services exposed externally. An API gateway alone leaves internal calls trusted by network location, which NIST SP 800-207A moves away from: it argues that access control should rely on application and service identities, not only network parameters, enforced by "API gateways, sidecar proxies" and identity infrastructure such as SPIFFE. A mesh alone gives strong workload identity but is a poor place to manage external developer onboarding, API keys, usage plans and public API contracts. Small platforms with a few services may run a gateway plus Kubernetes network policies and in-service token validation, and add a mesh when the number of internal callers makes per-service mTLS impractical.
How each architecture works
API gateway: the north-south policy point
A gateway terminates client TLS at the edge, authenticates the caller (OAuth 2.0 access token, mTLS client certificate or API key), applies rate limits and quotas, validates request shape against an API schema, routes to a backend and often transforms headers. Its threat model is the internet: credential stuffing against token endpoints, stolen or replayed tokens, scraping, resource exhaustion and attacks on inventory gaps such as shadow or deprecated API versions. The OWASP API Security Top 10 2023 risks it helps with most directly are API2:2023 Broken Authentication, API4:2023 Unrestricted Resource Consumption and API9:2023 Improper Inventory Management. Hardening the gateway itself is covered in the API Gateway Security Guide.
Service mesh: the east-west policy point
A mesh places a proxy beside each workload (sidecar) or on each node (sidecarless or ambient designs) and a control plane that issues identities and pushes policy. NIST SP 800-204A describes the benefit as enforcing security and resiliency consistently "without making changes to individual microservice code". Mesh products are listed alphabetically. Istio provisions "strong identities to every workload with X.509 certificates" and rotates them automatically; Linkerd authenticates clients through automatic mTLS and its MeshTLSAuthentication resource. The threat model is the inside: a compromised pod calling services it never should, lateral movement, interception of plain text traffic, and stolen tokens replayed internally. The mesh's own security, including control plane hardening, is in the Service Mesh Security Guide.
Side by side
| Dimension | API gateway | Service mesh |
|---|---|---|
| Traffic | North-south: external clients to services | East-west: service to service; mesh ingress and egress gateways at the boundary |
| Caller identity | End users and client applications: OAuth tokens, API keys, client certificates | Workloads: X.509 certificates bound to service accounts or SPIFFE IDs |
| Typical authorisation | Scopes, client entitlements, plans and quotas, route-level allow | Which source workload may call which service, method and path |
| Transport security | TLS to clients; optional mTLS for partner clients | mTLS between every meshed workload, with automatic rotation |
| Consumer management | Developer portal, keys, usage plans, versioning | None; consumers are internal workloads |
| Owner | API platform or product team | Platform or SRE team |
| Coverage gap if used alone | Internal calls trusted by network position | External consumer onboarding, abuse controls and API contracts |
Where authentication and authorisation belong
A useful rule is to place each decision at the layer that holds the facts needed to make it, and no higher.
| Decision | Best layer | Why |
|---|---|---|
| Is the external token valid, unexpired, from our issuer, for this audience? | Gateway (and again in sensitive services) | Rejects junk before it reaches the platform |
| Is this client application allowed this API and within its quota? | Gateway | Client registrations and plans live there |
| Does the token carry the scope for this route? | Gateway for coarse scopes; service for anything business specific | Route-level scope is stable; business rules change with code |
| Is the calling workload allowed to call this service, method and path? | Mesh | Only the mesh knows the authenticated workload identity |
| May this user read or change this specific object? | Service | Needs data the proxies do not have; this is where API1:2023 Broken Object Level Authorization is prevented |
| Is this administrative function allowed for this user? | Service, with a coarse deny at gateway for admin routes | Prevents API5:2023 Broken Function Level Authorization |
Externalised policy engines can be called from either proxy (Istio's CUSTOM action delegates to an external authoriser), which helps keep rules consistent; see the Policy-as-Code for Authorisation Guide. Whatever engine you use, the object-level check still needs the object's data.
Workload identity, mTLS and SPIFFE
The mesh's core contribution is a workload identity that does not depend on IP addresses. SPIFFE defines it as a URI, spiffe://trust-domain/workload-identifier, carried in a SPIFFE Verifiable Identity Document (SVID) that is valid only if "signed by an authority within the SPIFFE ID's trust domain". Workloads obtain short-lived keys, SVIDs and trust bundles from the Workload API, which rotates them automatically. Istio exposes the peer identity from mTLS as source.principal, so policy is written against identities, not addresses.
Two choices matter for the comparison:
- X.509-SVID or JWT-SVID: SPIFFE warns that tokens "are susceptible to replay attacks" and recommends X.509-SVIDs "whenever possible", accepting JWT-SVIDs where an L7 proxy or load balancer sits between workloads. This is exactly the case when traffic passes through a gateway that terminates TLS, so design the hop after the gateway with care.
- Trust domains: SPIFFE advises separate trust domains for environments with different security practices, such as staging and production. A gateway that routes into several trust domains must present an identity each accepts.
Deeper coverage: SPIFFE and SPIRE Guide and mTLS vs Signed Tokens for Service Authentication.
Token validation duplication and propagation
The question of whether to validate a JWT at the gateway, in every service or both is covered in depth in API Gateway vs In-Service Token Validation. What changes when a mesh is added is that there are now three places a token can be checked (gateway, mesh proxy, service code) and two identities on every internal request (the workload from mTLS, the user from the token).
Patterns, from weakest to strongest:
- Trust the header: the gateway validates and passes a user ID header; services trust it because traffic came from inside. Any compromised workload can forge the header. Avoid.
- Header plus mesh source check: the mesh only accepts the user header from the gateway's workload identity (an authorisation policy on
source.principal). Much better, but the claim is not cryptographically bound to the request beyond the hop. - Forward the original token and revalidate: each service, or its proxy via Istio
RequestAuthentication, validates the same edge token. Simple, but the edge token now travels deep into the platform and is valid at the edge if stolen internally. - Exchange for an internal token: the gateway calls a token service using OAuth 2.0 Token Exchange (RFC 8693, grant type
urn:ietf:params:oauth:grant-type:token-exchange) for a short-lived token with an internalaudienceand, for delegation, anactclaim recording the acting service. Internal tokens are useless at the edge, and scope can narrow per hop. See OAuth Token Exchange vs Token Passthrough and OAuth Transaction Tokens (Txn-Tokens) Guide.
One Istio behaviour causes many gaps: RequestAuthentication rejects invalid tokens, but "when requests carry no token, they are accepted by default". A service that relies on the proxy for JWT checks must also have an AuthorizationPolicy requiring requestPrincipals, or unauthenticated requests pass. Istio also notes that requests with more than one valid JWT are not supported because the resulting principal is undefined, which matters if the gateway adds its own token beside the client's.
Default postures to change
| Setting | Default described in docs | Target posture |
|---|---|---|
Istio PeerAuthentication mode | Mesh-wide policy with an unset mode defaults to PERMISSIVE (mTLS and plain text accepted) | STRICT, after confirming all clients are meshed |
| Istio authorisation | "For workloads without authorization policies applied, Istio allows all requests" | Namespace-wide allow-nothing baseline, then explicit ALLOW policies per service |
Istio RequestAuthentication | Requests without a token accepted | Pair with AuthorizationPolicy requiring requestPrincipals |
Linkerd config.linkerd.io/default-inbound-policy | all-unauthenticated (allows all requests) | all-authenticated or cluster-authenticated, then deny with explicit AuthorizationPolicy per Server or route; use audit first to see impact |
| Gateway API mesh routes (GAMMA) | With no Routes attached to a Service, mesh default behaviour applies | Attach producer Routes deliberately; once attached, unmatched requests are rejected |
Istio evaluates CUSTOM, then DENY, then ALLOW, so a matching deny cannot be overridden by an allow, and once any ALLOW policy applies to a workload, unmatched requests are denied.
Failure modes and overlap
- Gateway bypass: backends reachable directly (a public load balancer, a NodePort, a forgotten ingress) skip every edge check. Fix with network policy and mesh rules that accept external-origin traffic only from the gateway's identity; see the Kubernetes Network Policy Guide.
- Permissive mode left on: a migration to mTLS that never moved from PERMISSIVE to STRICT, so plain text calls still succeed.
- Policy split brain: the gateway blocks an admin route, but an internal path to the same service has no mesh rule, so any compromised pod reaches it.
- Header trust: identity headers accepted from any internal caller (pattern 1 above).
- Edge token sprawl: the client's bearer token forwarded to every downstream service and logged in some of them; see the JWT and Session Tokens in Code and Logs Guide.
- Unmeshed workloads: Linkerd enforces policy only on meshed pods, and Istio's STRICT mode breaks unmeshed clients, so jobs or legacy services outside the mesh become either outages or exceptions nobody revisits.
Where identity fits
Every mesh certificate and every internal token is a non-human identity. Treat the mesh CA and signing keys as tier-0 secrets, keep certificate lifetimes short with automatic rotation, and map each SPIFFE ID or service account to an owning team so authorisation policies can be reviewed. The gateway's own credentials (client secrets for token exchange, keys to backends) belong in a secrets manager. See the Cloud Workload Identity Guide for issuing identities outside the cluster.
Decision criteria
- Who are the callers? External developers and partners need gateway capabilities; dozens of internal services calling each other need mesh identity.
- How many internal hops carry user context? Beyond one or two, plan token exchange or transaction tokens instead of forwarding the edge token.
- Can you run a mesh? A mesh adds a control plane, certificate authority and upgrade cycle. Without platform capacity, start with a gateway, network policy and in-service validation.
- Compliance pressure on internal encryption? If internal traffic must be encrypted and mutually authenticated, a mesh is the least intrusive route to it.
- Kubernetes only, or mixed? SPIFFE identities can span VMs and clusters; a gateway can front all of them. Mixed estates favour SPIFFE-based identity behind a single gateway tier.
Reference architecture and worked scenario
A retailer exposes order and payment APIs to a mobile app and partners, behind which 40 services run on Kubernetes.
- Edge: the API gateway validates OAuth access tokens (issuer, audience, expiry, signature), enforces client quotas, rejects unknown API versions and blocks admin routes from the internet.
- Gateway to mesh: the gateway runs inside the mesh with its own workload identity. It exchanges the client token via RFC 8693 for an internal token with audience
orders-internal, valid for minutes. - Mesh: PeerAuthentication STRICT in all application namespaces; an allow-nothing baseline; ALLOW policies such as "orders may call payments on POST /authorise only". Only the gateway identity may call public-facing services.
- Service: the order service validates the internal token itself and checks that the user owns the order before returning it.
- Outcome: a compromised recommendations pod cannot call payments (mesh denies), cannot forge a user header (no header trust), and any token it steals is not accepted at the edge.
How to implement
- Inventory paths: list every route into each service, including non-gateway ingress, and every service-to-service call from mesh telemetry.
- Fix the edge: make the gateway the only external entry and validate tokens there.
- Enable mTLS in stages: PERMISSIVE namespace by namespace, confirm all traffic is mTLS in telemetry, then STRICT.
- Authorisation in audit first: Linkerd
auditdefault policy, or Istio policies trialled in a staging namespace, then a deny baseline with explicit allows. - Replace header trust: introduce token exchange or source-principal checks for identity headers.
- Roll back safely: revert a namespace to PERMISSIVE or remove a new deny policy as a single, version-controlled change; keep policies in Git with review.
Detection and measurement
- mTLS coverage: percentage of east-west requests over mTLS, from mesh telemetry; target 100 percent in application namespaces.
- Policy coverage: share of services with an explicit ALLOW policy rather than the default.
- Gateway bypass attempts: requests reaching public-facing services from any identity other than the gateway.
- Denied internal calls: mesh authorisation denials by source and destination; spikes suggest compromise or a broken deployment.
- Edge token depth: number of services receiving the original client token; should trend to zero with token exchange.
- Token validation failures: at gateway and service, split by reason (expired, audience, signature).
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | IA-9 Service Identification and Authentication; SC-8 Transmission Confidentiality and Integrity; AC-3 Access Enforcement; AC-4 Information Flow Enforcement; SC-7 Boundary Protection | Workload identity, mTLS, per-call authorisation, gateway as boundary |
| NIST CSF 2.0 | PR.AA Identity Management, Authentication, and Access Control; PR.PS Platform Security; DE.CM Continuous Monitoring | Identities for users and workloads, platform configuration, telemetry |
| NIST SP 800-204A and SP 800-207A | Service mesh security strategies; identity-based access for cloud-native applications | Primary architecture guidance for both layers |
| OWASP API Security Top 10 2023 | API1, API2, API4, API5, API9 | Split between gateway (API2, API4, API9) and service (API1, API5) |
| MITRE ATT&CK v20 | T1190 Exploit Public-Facing Application; T1550.001 Application Access Token; T1528 Steal Application Access Token; T1557 Adversary-in-the-Middle | Edge attacks, token replay internally, interception of plain text traffic |
| MITRE D3FEND | D3-CBAN Certificate-based Authentication; D3-MAN Message Authentication | mTLS workload authentication, signed tokens |
Practitioner checklist
- Write down, per decision, which layer owns it: token validity, client quota, workload allow, object access.
- Make the gateway the only external entry and block direct backend exposure.
- Move every application namespace to mTLS STRICT.
- Apply a deny-by-default authorisation baseline in the mesh.
- Pair Istio RequestAuthentication with an AuthorizationPolicy requiring requestPrincipals.
- Stop trusting identity headers from arbitrary internal callers.
- Exchange edge tokens for short-lived internal tokens where user context crosses several hops.
- Keep object-level authorisation in service code and test it.
- Store mesh and gateway policies in version control with review and staged rollout.
Standards and references
- NIST: SP 800-207A A Zero Trust Architecture Model for Access Control in Cloud-Native Applications in Multi-Cloud Environments
- NIST: SP 800-204A Building Secure Microservices-based Applications Using Service-Mesh Architecture
- IETF: RFC 8693 OAuth 2.0 Token Exchange
- SPIFFE: SPIFFE Concepts
- OWASP: API Security Top 10 2023
- Istio: Security concepts
- Linkerd: Authorization policy
- Kubernetes Gateway API: Service mesh (GAMMA)
Related NHI Mgmt Group resources: API Gateway vs In-Service Token Validation, Kubernetes Ingress Controller vs Service Mesh, Service Mesh Security Guide, NIST SP 800-207A Zero Trust for Cloud-Native Applications Guide.