Cheat SheetsMicroservicesSecurity

Security — Cheat Sheet

Microservices · 4 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Security
Microservices4 topicsQuick revision reference
1

Service-to-Service Authentication

Mutual TLS or short-lived JWT access tokens (client credentials flow) authenticate services with each other; avoid embedding long-lived secrets in code.

  • Never use long-lived shared API keys between services — use short-lived tokens (15min–1h) or mTLS certificates.
  • Client Credentials flow: service obtains a token from the auth server, attaches it as Bearer in every request.
  • Spring Security resource server validates JWT signature via JWKS URI — no shared secret, no round-trip to auth server per request.
  • mTLS via Istio is transparent to application code — Envoy sidecar handles TLS handshake and certificate rotation.
  • Inject client_secret via environment variable or Vault — never hardcode in application.properties or source code.
  • Use scopes to limit what each service is permitted to do — payments:read vs payments:create as separate scopes.
Properties + Java — OAuth2 client credentials flow
# application.properties — order-service calling payment-service
spring.security.oauth2.client.registration.payment-service.client-id=order-service
spring.security.oauth2.client.registration.payment-service.client-secret=${CLIENT_SECRET}
spring.security.oauth2.client.registration.payment-service.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.payment-service.scope=payments:create
spring.security.oauth2.client.provider.keycloak.token-uri=  https://keycloak.internal/realms/platform/protocol/openid-connect/token

# Configure WebClient with OAuth2 token auto-injection
@Configuration
public class WebClientConfig {
    @Bean
    public WebClient paymentWebClient(
            OAuth2AuthorizedClientManager clientManager) {
        ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2 =
            new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientManager);
        oauth2.setDefaultClientRegistrationId("payment-service");

        return WebClient.builder()
            .baseUrl("https://payment-service.internal")
            .filter(oauth2)  // automatically attaches Bearer token
            .build();
    }
}
2

JWT in Microservices

The API gateway validates the JWT and forwards claims downstream as trusted headers; each service reads claims without needing a round-trip to the auth server.

  • JWTs are stateless signed tokens — any service with the public key can validate them without calling the auth server
  • API gateway validates JWT once and forwards decoded claims as trusted headers (X-User-Id, X-Roles) downstream
  • Downstream services must only trust claim headers from internal gateway network, never from external clients
  • Spring Security Resource Server auto-validates JWT signature, expiry, issuer, and audience with zero custom code
  • Token relay propagates the original JWT on downstream calls; use Client Credentials for machine-to-machine calls
  • JWT revocation challenge: tokens are valid until expiry — use short expiry (5–15 min) + refresh tokens or a revocation list
YAML — JWT structure and Spring Boot Resource Server auto-validation
# JWT structure (decoded)
# Header:  {"alg": "RS256", "typ": "JWT", "kid": "key-id-1"}
# Payload: {
#   "sub": "user-123",         ← subject (user id)
#   "iss": "https://auth.example.com",
#   "aud": "order-service",
#   "exp": 1716840000,         ← expiry unix timestamp
#   "iat": 1716836400,
#   "roles": ["USER", "ORDER_MANAGER"],
#   "tenantId": "tenant-abc"
# }
# Signature: RS256(base64(header) + "." + base64(payload), private_key)

# application.yml — resource server validates JWT automatically
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com        # fetches JWKS from /.well-known/openid-configuration
          # or explicitly:
          jwks-uri: https://auth.example.com/.well-known/jwks.json

# Spring Security auto-validates: signature, exp, iss, aud
# Access current user in controller:
# @AuthenticationPrincipal Jwt jwt
# jwt.getClaimAsString("tenantId")
# jwt.getClaimAsStringList("roles")
3

Secrets Management

Inject secrets via HashiCorp Vault, AWS Secrets Manager, or Kubernetes Secrets mounted as env variables; never commit credentials to source control.

  • Never commit secrets to source control — use .gitignore, pre-commit hooks, and git-secrets scanning
  • Kubernetes Secrets are base64-encoded (not encrypted) — enable etcd encryption at rest in production clusters
  • Volume-mounted secrets are safer than environment variables — env vars are exposed in process listings and debug outputs
  • Vault dynamic secrets generate short-lived credentials per request — leaked credentials expire automatically
  • Vault Agent Injector injects secrets as files via pod annotations, removing Vault SDK dependency from applications
  • Cloud IRSA (IAM Roles for Service Accounts) eliminates static AWS credentials entirely — pods assume roles dynamically
YAML — Kubernetes Secret as env var and file mount
# Create a Secret
kubectl create secret generic db-credentials \
  --from-literal=username=app_user \
  --from-literal=password=supersecret

# Pod spec — mount as files (preferred over env vars)
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: order-service
      image: order-service:1.0.0
      # Option 1: env var injection (simpler but less secure)
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
      # Option 2: volume mount (safer — not in process env)
      volumeMounts:
        - name: db-secret
          mountPath: /etc/secrets/db
          readOnly: true
  volumes:
    - name: db-secret
      secret:
        secretName: db-credentials
        defaultMode: 0400   # owner read-only

# Spring Boot reads file-based secrets:
# spring.datasource.password=${file:/etc/secrets/db/password}
4

Zero-Trust Networking

No implicit trust is granted based on network location; every request is authenticated and authorised, enforced by a service mesh (Istio/Linkerd) or explicit token validation.

  • Never trust the network perimeter — every request must carry verifiable identity
  • mTLS authenticates both caller and callee; Istio automates cert rotation via SPIFFE/SPIRE
  • AuthorizationPolicy (Istio) or Spring Security restrict access to specific service identities
  • JWTs carry claims (sub, roles, aud) that are validated cryptographically — no session state needed
  • Least privilege: grant only the minimum permissions each service needs to function
  • Audit logs at the mesh layer capture every service-to-service call for compliance and forensics
YAML — Istio PeerAuthentication + AuthorizationPolicy
# Enable STRICT mTLS for the entire namespace
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: orders
spec:
  mtls:
    mode: STRICT

---
# Only allow order-service to call payment-service POST /payments
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: payment-authz
  namespace: payments
spec:
  selector:
    matchLabels:
      app: payment-service
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/orders/sa/order-service"]
    to:
    - operation:
        methods: ["POST"]
        paths: ["/payments"]
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/microservices