Skip to content
Starone

Security

Zero-trust application architecture

Zero trust means no request is authorised because of where it came from. In practice that is workload identity, short-lived credentials and policy evaluated on every call.

· 5 min read · Starone Engineering

Zero trust is an architectural property, not a product. A system has it when no request is authorised because of the network it arrived from. Every call carries a verifiable identity, every call is checked against policy, and a compromised component gains only what its own identity was allowed to do.

Our view is that most of the value comes from three unglamorous pieces of work: giving every workload an identity, removing long-lived secrets, and moving authorisation out of scattered application code into policy that can be reviewed. Network segmentation still matters, but as a second layer and not as the basis for trust.

Identity is the perimeter

The perimeter model assumes that anything inside the network is friendly. That assumption fails the moment one pod, one laptop or one dependency is compromised, because the attacker inherits the trust of the network segment. Lateral movement is the predictable result.

Replacing location with identity changes the question each service asks. It no longer asks whether a request came from the internal subnet. It asks who the caller is, what proves it, and whether this caller may perform this action on this resource. There are always two identities to consider: the workload making the call and the end user on whose behalf it is made.

Service identity and mutual TLS

Workload identity should be issued by the platform, not configured by hand. SPIFFE is the common model: each workload receives a short-lived X.509 certificate whose identity is derived from attested properties such as its Kubernetes service account and namespace. A service mesh or a library then uses those certificates for mutual TLS, so both ends of every connection are authenticated and the traffic is encrypted.

Mutual TLS is frequently mistaken for the whole solution. It proves which service is calling. It says nothing about whether that service should be allowed to call this endpoint, and it carries no information about the user. Authentication without an authorisation policy on top produces an encrypted flat network.

Short-lived credentials over stored secrets

A static API key in an environment variable is valid until someone remembers to rotate it, and it works equally well from an attacker's machine. Short-lived credentials shrink that window to minutes and remove most of the rotation burden, because expiry is the rotation.

  • Workload identity federation to cloud providers through OIDC, in place of stored access keys
  • Database credentials issued on demand with a lease, for example through Vault's dynamic secrets
  • CI pipelines that exchange a signed job token for scoped, time-limited deployment rights
  • User tokens with short lifetimes, audience restrictions and refresh handled at the edge

Separate the policy decision from its enforcement

Authorisation has two parts. The policy enforcement point intercepts the request and blocks or allows it. The policy decision point evaluates the rules and returns the answer. Keeping them separate means policy is written once, versioned, tested and reviewed, instead of being re-implemented as conditionals in every service.

The practical design question is where the decision runs. A central decision service is simple to reason about and adds a network hop and a single point of failure to every request. Our default is local evaluation: an engine such as Open Policy Agent runs beside the service or inside the gateway, and policy bundles are distributed to it. Decisions take microseconds and survive an outage of the control plane. The cost is that policy changes propagate with a delay, which has to be acceptable for revocation scenarios.

Decide in advance what happens when a decision cannot be made. Failing closed is correct for anything that changes state or exposes data. Failing open is occasionally justified for low-risk read paths where availability matters more, and that exception should be written down, not discovered during an incident.

Least privilege has to reach the data

A common gap is a well-authenticated service mesh in front of services that all connect to the database as the same powerful user. The check at the front door is real, and behind it every service is a confused deputy that can read anything.

Closing the gap means propagating the end user's context through the call chain and narrowing it at each hop. Token exchange lets a service swap the incoming token for one scoped to the single downstream audience it needs. Each service gets its own database role with access to its own tables, and row-level policies apply the user or tenant context where the data model allows it.

  • One identity per service, never a shared platform account
  • Tokens scoped to an audience so that a stolen token cannot be replayed elsewhere
  • Default-deny between services, with each allowed path declared explicitly

Why a VPN is not zero trust

A VPN authenticates a device once and then places it on a network. Everything reachable from that network is now reachable from that device, for as long as the session lasts. That is the perimeter model with a longer cable. It also makes the VPN concentrator a single high-value target.

An identity-aware proxy inverts this. Each application is published individually, each request is authenticated and authorised, and device posture can be part of the decision. Access to one internal tool grants nothing else. A VPN remains reasonable for legacy protocols that cannot sit behind a proxy, provided it is treated as transport and not as authorisation.

Where to start, and when not to

Zero trust has a real cost: certificate infrastructure, policy tooling, more moving parts to debug and an engineering team that must understand them. For a single modular monolith with one database and a small team, a full mesh with per-request policy evaluation is disproportionate. Strong user authentication, a managed secret store and separate database roles deliver most of the benefit.

For a system with many services, several teams or sensitive data, we would begin with workload identity and the removal of static secrets, then introduce policy in audit mode to observe what would be denied before enforcing it. Enforcing a default-deny policy on a system whose real call graph nobody has measured is how outages are caused.

Working on something similar

We are happy to compare notes. Talk to the engineers who wrote this.