16 Implementing service identity
Modern systems are a web of services calling other services, so knowing exactly which service is making each call is foundational to security. The chapter explains service identity as a unique, verifiable identity for an application or instance, essential to authorization, auditing, and secure communication in a Zero Trust world where the old network perimeter can’t be relied on. It frames the problem through an impersonation attack to show how weak or missing identity turns authorization into guesswork, then introduces the core building blocks: identity artifacts (certificates, JWTs, workload metadata), authentication to prove possession, trusted issuers as anchors (CAs and token providers), and the scope of identity (shared versus per-instance) for precision and traceability.
With the basics in place, the chapter explores enforcing access in code at the application layer. This gives fine-grained, business-aware control but demands consistency across teams. Common patterns include OAuth2 client credentials (services obtain short-lived tokens—often JWTs—from an IdP and present them to peers), API keys (simple but risky due to leakage, spoofing, and poor rotation/expressiveness), and custom request signing with asymmetric keys to provide authenticity and integrity. It emphasizes secure key storage, rotation, and defenses like timestamps and body hashes to block replay and tampering. The trade-off: powerful, tailored rules close to domain data, at the cost of duplicated effort and the risk of uneven implementation quality.
The chapter then moves authorization into the platform for consistency and a strong default posture. Service meshes (e.g., Istio, Linkerd) use sidecar proxies and mutual TLS to authenticate services, encrypt traffic, and enforce “who can talk to whom” centrally, while API gateways guard the system’s edge, validating tokens and policies for North–South traffic. This infrastructure-first approach reduces boilerplate and human error but adds operational complexity and cannot express business-specific decisions, which remain in application code. The recommended model layers both: the infrastructure establishes verified identities, encrypted channels, and coarse access controls, and the applications apply fine-grained, context-rich authorization—delivering defense in depth that scales.
In a system multiple services communicate with one another to solve various use cases. If the communication isn’t secure, attackers may manage to exploit vulnerabilities and change or steal data.
Services that trust incoming messages without verifying the sender's identity are vulnerable—if an attacker gains access, they can impersonate trusted services and send messages to manipulate or extract data from the system.
Each application includes logic to validate incoming requests. Every request should contain a token, key, or other proof that identifies the sender as a trusted counterpart. If the request lacks valid proof, the service rejects it.
Steps the app follows for validating a request.
The client credentials flow. Each service obtains a token from an identity provider by providing their valid client credentials. They use this token to send requests to other services in the system. The token has a limited lifespan and a service needs to get a new token from time to time.
The API key authentication approach is very simplistic. Both sides know a secret they use to prove who they are when sending the requests.
Key pairs and asymmetric cryptographic algorithms can be used to sign the requests made by services to ensure better security. In this approach the service signs the request it makes with a private key. The service receiving the request can validate the signature using the public key and decide whether it approves or rejects the request.
In a service mesh, each service is deployed together with a sidecar app. The sidecar app is the one that validates the requests which go towards the app it protects.
The anatomy of a pod. The two apps, the sidecart app which takes care of the authorization and the service are always deployed together and can only be scaled together. The sidecard app intercepts all the requests and decides if it forwards the request to the service based on the configured authorization rules.
Infrastructure authorization is often combined with app-level authorization. In certain cases, some authorization rules are more business context-specific and need to be defined in the service with access to the needed data.
An API Gateway is designed to guard the system entrance. Besides security, it sometimes also takes other responsibilities, such as observability (tracking the requests that go in and out) and service discovery (finds out to which service the request is addressed).
Summary
- Service identity is the foundation for securing service-to-service communication.
- At the application level, services validate tokens, API keys, or signed messages to decide if a request should be allowed.
- Application-level authorization gives flexibility and fine-grained control, but it requires every service to implement authorization correctly, which can lead to inconsistencies and security gaps.
- Infrastructure-level approaches like service meshes and API gateways move the responsibility outside of the app, providing consistent enforcement, automatic mTLS, and centralized policy management.
- Service meshes secure internal traffic (East-West), while gateways focus on external requests (North-South).
- Modern systems often combine both approaches: infrastructure handles identity and baseline access, while the app enforces business-specific rules.
- A mixed model (which includes both application and infrastructure level configurations) for implementing service authorization improves security, reduces human error, and supports Zero Trust architectures.
- No single solution fits all systems. Choosing between application and infrastructure-level authorization depends on system complexity, security needs, and team responsibilities.
- System complexity refers to the number of services, the communication patterns between them, and the diversity of technologies in use. Simple systems might get away with centralized gateways or API keys, while distributed microservices often require more fine-grained, decentralized enforcement at the application level.
- Security needs vary based on the sensitivity of the data and the threat model. Systems dealing with financial transactions, personal data, or regulated workloads may require multi-layered authorization (e.g., both infra and app level) to ensure defense in depth.
- Team responsibilities influence where enforcement is best placed. If a platform team manages shared infrastructure but app teams own service logic, it's often more maintainable to delegate coarse-grained rules to the infra layer and keep fine-grained policies within the apps themselves.
FAQ
What is service identity and why does it matter?
Service identity is a unique, verifiable identity assigned to a service or instance. It lets other services know who is calling so they can authorize, audit, and secure communications. Without it, any caller can impersonate a trusted service, leading to data theft or manipulation.How are identity, authentication, and trust anchors different?
- Identity: the “who” (certificates, JWTs, metadata like service accounts).- Authentication: proving the identity is genuine (mTLS, signature verification, JWT validation).
- Trust anchors: who vouches for identities (certificate authorities, token issuers/IdPs). These three work together to establish trustworthy calls.
What does Zero Trust mean for service-to-service calls?
Zero Trust assumes no request is trustworthy by default—even inside your network. Every call must be verified, authenticated, and authorized. This prevents lateral movement and impersonation if an internal component is misconfigured or compromised.What is the scope of identity, and why prefer a narrow scope?
Scope describes how specific an identity is (shared per service vs. unique per instance). Narrow scopes (per instance/workload) improve containment, auditability, and policy precision, especially in dynamic environments like Kubernetes.How does application-level service authorization work?
Typical flow: (1) the caller sends a credential (token/signature), (2) the callee validates it, (3) authorization rules are evaluated (who can call what), and (4) allow or deny. Pros: fine-grained, business-aware. Cons: duplicated logic, inconsistent enforcement if teams differ.Which mechanisms can I use at the application level, and what are their trade-offs?
- OAuth2 client credentials: robust, short-lived tokens, rich claims, centralized control; needs IdP integration and token handling.- API keys: simple but static, easy to leak/spoof, limited expressiveness; not ideal for production at scale.
- Custom signing (asymmetric keys): flexible, strong integrity; requires careful key management, canonicalization, and replay protection.
When should I use OAuth2 client credentials and what should I watch for?
Use it for reliable, scalable service identity with central policy. Validate issuer/audience/scopes, rotate keys, cache and refresh tokens safely, and leverage framework support (e.g., Spring Security) for fewer mistakes.Why are API keys risky, and how can I mitigate if I must use them?
They are static, carry no context, are easy to leak, and can be spoofed without transport security. If you must use them, mitigate with mTLS/HTTPS, per-service keys with least privilege, rotation, storage in a secrets manager, IP allowlists, rate limits, and thorough logging.How does custom request signing work, and how do I prevent replay attacks?
The caller signs a canonical string (e.g., method|path|timestamp|body hash) with a private key; the receiver verifies with the public key. Prevent replays by including timestamps/nonce, enforcing tight clock windows, rejecting duplicates, securing key storage, and rotating keys.What do service meshes and API gateways add, and how do they work together?
- Service mesh (East-West): sidecar proxies enforce mTLS, verify service identity, and apply allow/deny policies across internal traffic.- API gateway (North-South): authenticates external requests, validates tokens, applies rate limits and policies at the edge.
Use both for layered defense: the gateway keeps untrusted traffic out; the mesh secures internal calls; the app enforces business-specific rules.
Software Security for Developers ebook for free