You have ten microservices running in Kubernetes. Each one validates JWTs, checks scopes, maintains sessions, and implements its own RBAC rules. One team uses jsonwebtoken v8, another uses a custom Go library, a third rolled their own HMAC check because “it was simple.” They all accept alg: none. Three accept RS256 and HS256 simultaneously.
This is not a security posture. This is a distributed security liability — and Kubernetes makes the problem worse, because the cluster creates an illusion of isolation that encourages teams to treat each Pod as a security boundary it was never designed to be.
The pattern of embedding authentication and authorization logic inside individual containers is one of the most pervasive anti-patterns in Kubernetes-based microservices. It feels like ownership and simplicity. It is, in practice, inconsistency at scale — and the blast radius of a single misconfiguration is your entire service portfolio.
Kubernetes provides the primitives to fix this at the infrastructure layer: Ingress controllers, the Gateway API, service mesh sidecars, admission webhooks, and workload identity via SPIFFE. None of these require a line of auth code inside your application containers.
This article explains why the anti-pattern exists, what’s wrong with it technically, and what the correct Kubernetes-native alternatives are — with concrete implementation guidance and references to the standards and incidents that validate the argument.
The Anti-Pattern: What It Looks Like
In-Application JWT Validation
Every service imports an auth library and validates tokens independently:
# Pattern seen in thousands of microservices
from jose import jwt
def authenticate(token: str):
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return payload["sub"]
Variations include:
Algorithm confusion : accepting both HS256 and RS256, or letting the token header drive verification behavior instead of pinning acceptable algorithms server-side. This is a distinct JWT implementation failure class, documented extensively by PortSwigger on JWT attacks and RFC 8725
alg: none bypass : libraries that accept unsigned tokens when alg is set to none. This is a documented attack vector in Auth0’s JWT security analysis
Missing exp / iss / aud validation : trusting a valid signature without checking whether the token is expired, for the right audience, or from the right issuer
Key confusion attacks : accepting a public RS256 key as an HS256 symmetric secret
Session State in Every Service
When services maintain session state directly, they duplicate logic that has no business being duplicated — cookie validation, refresh token flows, PKCE verification — and each implementation diverges over time.
RBAC Reimplemented Per Service
Authorization rules (“can this user access this resource?”) end up embedded in service logic, mixed with business logic, tested inconsistently, and impossible to audit across the portfolio.
Why This Is a Structural Problem
1. The Vulnerability Surface Scales With Your Service Count
Each new microservice is a new JWT validation surface. A single incorrect library configuration — an unvalidated alg, a missing aud check — is an authentication bypass affecting that entire service. With ten services, you have ten potential misconfigurations. With a hundred services, the probability that at least one is misconfigured approaches certainty.
The OWASP Kubernetes Security Cheat Sheet and OWASP Microservices Security Cheat Sheet both identify in-service auth as a primary attack surface in microservices environments. NIST SP 800-204 and its companion NIST SP 800-204A on DevSecOps make the same argument: security controls belong at infrastructure boundaries , not inside application code.
2. Maintenance Cost Is Multiplicative
When a JWT vulnerability is disclosed — and they are disclosed regularly — you update one library in one service. Then another. Then you discover service C is pinned to an old version because it has a transitive dependency conflict. Meanwhile the vulnerability is exploitable in production.
The CNCF Cloud Native Security Whitepaper frames this directly: security controls implemented redundantly across services create maintenance overhead that teams cannot sustain, leading to version drift and policy divergence.
3. Centralized Policy Is Impossible to Enforce
When policy is in code — even well-factored library code — it cannot be changed atomically across services. A policy update requires a coordinated deployment across every affected service. In practice, services deploy on different schedules, managed by different teams, with different testing cycles. The result is that at any given moment, some fraction of your services are running different authorization rules.
This is the core argument in Google’s BeyondCorp model and the Zero Trust Architecture guidance from NIST SP 800-207 : authentication and authorization decisions should be made by a centralized, auditable policy enforcement point — not distributed across workloads.
4. Secrets Distribution Is a Problem You Don’t Need
If every service validates JWTs, every service needs the signing key (for symmetric algorithms) or the public key (for asymmetric). Distributing and rotating signing keys across a fleet of microservices is an operational burden with meaningful blast radius: a leaked symmetric key compromises every service holding it.
The CNCF SPIFFE/SPIRE project was built specifically to solve this class of problem: workload identity should be cryptographically attested, not rely on secrets distributed to application code.
The Real-World Incidents
The alg: none Class
In 2015, critical vulnerabilities in JWT libraries from Auth0 affecting Python, PHP, Node.js, Ruby, Java, and .NET allowed attackers to forge tokens by setting alg: none. The signature was not verified. The vulnerability was present in applications that had copied JWT validation code from tutorials or used unpatched libraries — exactly the pattern that in-service auth produces at scale.
Java’s Psychic Signatures (CVE-2022-21449)
CVE-2022-21449 affected ECDSA signature verification in Oracle Java SE and GraalVM, including java.security.Signature paths used by higher-level libraries. The bug allowed certain malformed ECDSA signatures to verify when they should not. JWT validation was in scope only when the deployment used an affected Java runtime and ECDSA-signed tokens, for example ES256. A gateway would help only if verification happened on a patched or unaffected runtime at the gateway instead of inside every Java service.
CVE-2023-2728 (Kubernetes Mountable Secrets Bypass)
CVE-2023-2728 was not an ImagePolicyWebhook outage behavior. It was a Kubernetes API server issue where users could use ephemeral containers to bypass the mountable secrets policy enforced by the ServiceAccount admission plugin. Clusters were affected only when the ServiceAccount admission plugin, the kubernetes.io/enforce-mountable-secrets annotation, and ephemeral containers were used together. The adjacent ImagePolicyWebhook issue was CVE-2023-2727 , also involving ephemeral containers, but it is a separate CVE.
The Uber API Gateway Evolution
Uber’s engineering blog describes its API gateway as a centralized layer for routing, protocol conversion, rate limiting, load shedding, header propagation, security auditing, and user access blocking. That supports the architectural point here: high-volume platforms move cross-cutting controls into shared infrastructure. The public source does not prove that Uber migrated specifically from per-service authentication to gateway authentication, so that stronger claim should not be made.
Netflix Zuul
Netflix’s Zuul is an L7 gateway for dynamic routing, monitoring, resiliency, security, and related edge concerns. Netflix’s own Zuul posts list authentication among common edge-service uses, but they do not frame Zuul primarily as a case study in eliminating per-service auth. Treat it as evidence that authentication is a natural edge concern at scale, not as proof of a specific migration story.
The Alternatives
Use these as complementary controls, not as a single replacement for all authentication and authorization logic:
Alternative When to use it What it solves Principal trade-off API Gateway / Edge Auth External API clients, public ingress, partner integrations, mixed auth mechanisms at the boundary Central JWT/API-key/OAuth2 validation, rate limiting, request shaping, and identity header propagation before traffic reaches services Does not secure east-west service calls by itself; trusted headers require strict network boundaries Service Mesh mTLS Service-to-service traffic inside the cluster, especially across teams or sensitive domains Workload identity, automatic mTLS, peer authentication, and proxy-level authorization policy Adds data-plane/control-plane complexity and operational coupling to sidecars or ambient mesh components OAuth2 Proxy Browser-facing internal apps that need OIDC login, redirects, cookies, and session handling Delegates login/session management to a reverse proxy and forwards authenticated identity headers Best for HTTP/browser flows; not a general machine-to-machine authorization system OPA Complex, auditable, frequently changing authorization rules Separates policy decisions from application releases and can run as sidecar, service, ext_authz backend, or admission control via Gatekeeper Policy/data distribution and failure behavior must be designed deliberately SPIFFE/SPIRE Multi-cluster, multi-cloud, or meshless environments that need portable workload identity Issues short-lived workload identities without application-managed shared secrets Provides identity, not business authorization; needs registration and attestation lifecycle management
Option 1: API Gateway (Edge Auth)
An API Gateway sits at the perimeter and handles authentication before a request reaches any downstream service. Services receive pre-validated identity in a trusted header.
What it does: – Validates JWTs, API keys, OAuth2 tokens – Enforces rate limiting per identity – Strips and re-adds Authorization headers as needed – Routes to upstream services with verified identity headers
When to use it: – North-south traffic (external clients → cluster) – Mixed authentication mechanisms (JWT + API key + mTLS) at the Ingress layer – Teams that want to centralize auth policy without rolling out a full service mesh
Tools:
Gravitee.io API Gateway can be deployed on Kubernetes via its Helm chart and integrates with the Kubernetes Gateway API :
helm repo add graviteeio https://helm.gravitee.io
helm install gravitee-apim graviteeio/apim \
--namespace gravitee \
--create-namespace \
--set gateway.replicaCount=2 \
--set gateway.ingress.enabled=true \
--set gateway.ingress.hosts[0]=api.example.com
Once deployed, authentication policies are declared on the ApiV4 CRD — no application code involved:
apiVersion: gravitee.io/v1alpha1
kind: ApiV4
metadata:
name: payment-api
namespace: gravitee
spec:
name: "Payment API"
type: PROXY
listeners:
- type: HTTP
paths:
- path: /v1/payments
entrypoints:
- type: http-proxy
endpointGroups:
- name: default
type: http-proxy
endpoints:
- name: upstream
type: http-proxy
configuration:
target: http://payment-service.production.svc.cluster.local:8080
flows:
- name: JWT validation
enabled: true
request:
- policy: jwt
enabled: true
configuration:
signature: RSA_RS256
publicKeyResolver: JWKS_URL
jwksUrl: https://idp.example.com/.well-known/jwks.json
checkTokenRevocation: true
requiredClaims:
- name: aud
value: payment-api
- policy: rate-limit
enabled: true
configuration:
rate:
limit: 100
periodTime: 1
periodTimeUnit: MINUTES
Gravitee’s Kubernetes operator reconciles ApiV4 resources against the gateway, making API policy a first-class GitOps object — versioned, reviewed, and deployed the same way as any other Kubernetes manifest.
Other Kubernetes-native options: Emissary-Ingress (formerly Ambassador) with its AuthService CRD; Traefik with ForwardAuth middleware on IngressRoute resources.
Limitations: API Gateways handle north-south traffic. They don’t address east-west (service-to-service) authentication inside the cluster.
Option 2: Service Mesh (East-West mTLS + Auth)
A service mesh provides mutual TLS between every service pair and enforces authorization policy at the sidecar proxy, without any application code changes.
What it does: – Automatic mTLS between all service-to-service calls – Workload identity via X.509 certificates (SPIFFE SVIDs) – Fine-grained AuthorizationPolicy at the Envoy sidecar – JWT validation at the proxy, not the application
Istio Implementation:
Istio uses Envoy’s ext_authz filter and native RequestAuthentication + AuthorizationPolicy CRDs :
<pre class="wp-block-c