Why Gateway-Level Security Matters in a Microservices World
Once you have more than a handful of services, enforcing auth and rate limiting independently in each one leads to inconsistency and duplicated logic. An API gateway centralizes these concerns so individual services can trust that traffic reaching them has already been vetted.
Centralized JWT Validation at the Gateway
# Kong Gateway plugin config
plugins:
- name: jwt
config:
key_claim_name: kid
claims_to_verify:
- exp
- name: rate-limiting
config:
minute: 100
policy: redis
redis_host: redis.internal
Downstream services receive already-validated requests with identity passed through a trusted header, rather than each service independently verifying tokens.
Service-to-Service Auth with mTLS
External-facing auth (JWTs, API keys) secures client-to-gateway traffic. Internal service-to-service traffic should use mutual TLS so services can verify each other’s identity, not just encrypt the connection:
# Istio PeerAuthentication enforcing strict mTLS
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
Zero Trust: Never Trust the Network Perimeter Alone
Traditional architecture trusted anything inside the VPC. Zero trust assumes a compromised service can exist anywhere, and enforces identity verification on every request, internal or external:
# Istio AuthorizationPolicy: only the checkout service may call payments
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: payments-access
namespace: production
spec:
selector:
matchLabels:
app: payments
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/checkout-service"]
Rate Limiting at Multiple Layers
- Gateway level — protects against abuse from external clients
- Per-service level — protects individual services from internal cascading failures
- Per-tenant level — prevents one customer’s traffic spike from degrading service for others
const tenantLimiter = rateLimit({
windowMs: 60 * 1000,
max: (req) => req.tenant.plan === 'enterprise' ? 1000 : 100,
keyGenerator: (req) => req.tenant.id,
});
Circuit Breaking to Contain Failures
# Istio DestinationRule with circuit breaking
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: payments-circuit-breaker
spec:
host: payments
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
Conclusion
Securing a microservices architecture is a layered problem: gateway-level auth and rate limiting for external traffic, mTLS and explicit authorization policies for internal service-to-service calls, and circuit breaking to contain the blast radius when something does go wrong. No single layer is sufficient on its own.