Zero-Trust Networking

The traditional security model has a name: castle-and-moat. The firewall is the moat. Once you're inside the perimeter, you're trusted. Everything inside can talk to everything else. The implicit assumption is that the hard shell keeps the bad actors out and only good actors are inside.

In a world of cloud VPCs, microservices, third-party SaaS integrations, and developers working from coffee shops, the moat has too many holes to be load-bearing. The question isn't whether this model fails. It's why so many organizations still design around it.

The perimeter model and why it fails

In a traditional on-prem network from 2005, the perimeter model was reasonably sensible. Your servers were in a building you controlled. The firewall separated the internet from the internal network. Machines inside the firewall were machines your organization owned. Trust based on network location was a reasonable proxy for trust based on identity, because being inside the network was hard to fake.

Modern infrastructure invalidates every one of those assumptions. Your "internal network" now spans an AWS account, a GCP project, an office VPN, several developer laptops, and a few third-party SaaS services that have OAuth access to your systems. When Service A in us-east-1 calls Service B in eu-west-1, which side of the firewall is that? When a developer's laptop is on a hotel WiFi connected via VPN, is it "inside"?

The deeper problem is what a perimeter breach enables. If your internal network trusts all internal connections, then a single compromised host (one engineer's laptop with malware, one misconfigured EC2 instance exposed to the internet, one service with a remote code execution vulnerability) becomes a foothold to reach everything else. There's no authentication between your API service and your database; the database accepts connections from "inside the VPC." A compromised service can query the database directly. This is lateral movement: an attacker who gets in anywhere can get to everything.

Breaches that use lateral movement are disproportionately costly precisely because the perimeter model gives attackers a free pass once they're past the outer wall. The 2020 SolarWinds attack is the canonical example: attackers compromised a build system, used that access to reach internal networks of thousands of organizations, and moved laterally for months before detection. The outer firewall was intact. None of it mattered.

The zero-trust model

Zero-trust (the term comes from a 2010 Forrester paper by John Kindervag, though Google's internal BeyondCorp implementation is the most-studied real-world deployment) starts from a different assumption: treat the network as hostile regardless of where a request originates. Your database should require authentication from your API service even if they're in the same VPC. Your internal admin panel should require authentication from a corporate laptop even if it's on the corporate network.

The core shift is from network location as trust to identity as trust. The question a service asks when it receives a request is not "where did this come from" but "who sent this and can I verify that claim cryptographically." If the answer is yes, grant access to exactly what this identity is authorized to access, nothing more. If the answer is no, reject the request regardless of the source IP.

This limits lateral movement: a compromised service can only reach the services and resources it has valid credentials for, not everything on the internal network. A breach is still bad, but the blast radius is bounded by the compromised identity's permissions rather than by the entire internal network.

mTLS: mutual authentication

The standard way to implement service-to-service identity in a zero-trust model is mTLS, or mutual TLS (Transport Layer Security, the protocol that puts the "S" in HTTPS).

Standard TLS has the server present a certificate (a cryptographic document signed by a trusted certificate authority that proves "I am who I claim to be"). The client verifies the certificate and, if it's valid, trusts the server. This is the one-sided authentication you use when visiting a bank's website: your browser verifies the bank's certificate; the bank doesn't cryptographically verify who you are at the TLS layer (that's what your password is for at the application layer).

mTLS adds the other direction: the client also presents a certificate. Service A proves "I am Service A" to Service B, and Service B proves "I am Service B" to Service A. Neither relies on the network to have filtered out imposters. An attacker who can send packets to Service B still can't impersonate Service A without Service A's private key. The cryptography is the authentication; the network location is irrelevant.

The encryption is a bonus: mTLS encrypts all traffic between services, so even if an attacker can observe network traffic (on a misconfigured VPC, or via a compromised network device), they see ciphertext, not plaintext database queries or API keys.

Certificate management at scale

mTLS sounds straightforward until you think about what it requires in practice: every service needs a certificate. In a microservices architecture with hundreds of services, that's hundreds of certificates. Each certificate needs to be issued, distributed to the right service, rotated before it expires, and revoked if the service is compromised. Doing this manually is not feasible. Doing it wrong creates security holes.

The SPIFFE (Secure Production Identity Framework for Everyone) standard addresses this. SPIFFE defines a workload identity format, a URI like spiffe://example.com/service/payment-api, and a protocol for issuing and validating identity documents. SPIRE (the SPIFFE Runtime Environment) is the reference implementation that runs in production: it automatically issues short-lived X.509 certificates to workloads based on their identity (attested through the node they're running on, the Kubernetes service account they use, and so on).

Short-lived certificates (24 hours is common) are significantly better than long-lived certificates plus a revocation list. The reason: revocation is slow and unreliable in practice. Certificate revocation lists (CRLs) and the Online Certificate Status Protocol (OCSP) require services to check in to find out whether a certificate has been revoked. This check is often cached, sometimes skipped, and always introduces latency. A 24-hour certificate that leaks is expired by tomorrow; it doesn't need to be revoked. The security property you care about, that compromised credentials stop working, is automatic rather than dependent on a revocation check.

Vault PKI (from HashiCorp) and cert-manager (a Kubernetes-native certificate controller) are the other common alternatives. The details differ; the principle is the same: automate issuance and rotation so that credentials are short-lived and no human needs to be in the loop.

Service meshes

Implementing mTLS, certificate management, and associated policies in every service individually creates a different problem: every service team needs to get it right, and "every team implements security correctly" is not a security model you can rely on. A service mesh moves these concerns to the infrastructure layer.

Istio and Linkerd are the two widely-used service meshes for Kubernetes. The basic mechanism is the same: a sidecar proxy (a small process running alongside each service pod) intercepts all inbound and outbound network traffic. The sidecar handles mTLS. It holds the service's certificate, terminates incoming mTLS connections, and establishes outgoing mTLS connections, without the application code knowing or caring. From the application's perspective, it's making a plain HTTP call; the sidecar upgrades it to mTLS transparently.

Beyond security, the sidecar also provides: automatic retries and timeouts, circuit breaking (stop sending requests to a service that's failing rather than letting failures cascade), and detailed telemetry (exactly how long each service-to-service call takes, what the error rates are). These capabilities are consistent across every service in the mesh without any service-specific implementation.

The cost is real. Every service-to-service request passes through two extra processes (the sender's sidecar and the receiver's sidecar). The sidecars consume CPU and memory. The control plane (the component that manages sidecar configuration) is a significant operational responsibility. Istio in particular has a reputation for complexity that its maintainers have been working to reduce. Linkerd's design prioritizes operational simplicity and has significantly lower overhead, at the cost of fewer features.

Whether a service mesh is the right choice depends on scale and requirements. A small number of services with explicit mTLS in each one is often more straightforward. A large organization with many teams and services, where consistency matters and the operational cost can be absorbed centrally, is where a mesh pays off.

The right threat model

Zero-trust isn't a product you buy. Enterprise vendors sell "zero-trust solutions" because "zero-trust" became a marketing term. The underlying thing is a threat model: assume an attacker can be inside your network perimeter, via phishing, a supply chain compromise, a misconfigured public resource, or any other vector. Design so that assumption doesn't give them everything.

The shift in question is significant. The perimeter model asks: "how do I keep attackers out?" The zero-trust model asks: "what can an attacker who's already inside do, and how do I limit that?" The first question has a comfortable answer (firewalls, VPNs, perimeter monitoring). The second question is harder, because the answer involves redesigning how trust works across every service-to-service interaction, every privileged credential, every access control decision.

The second question is the one that matches how breaches actually unfold. The ones that cause serious damage are almost never the ones that bounced off a firewall. They're the ones that got in quietly, moved laterally, and weren't detected for months. Zero-trust doesn't prevent every breach. It limits what a breach gives the attacker, and in practice that is what determines how bad the damage gets.

Back to writing