The question this answers
Where in this system does trust change hands, and what actually enforces it there?
The application serves anonymous internet traffic and holds tenant data that must not leak between tenants. Somewhere between the anonymous request and the row in the database, the system has to stop treating input as hostile and start treating it as authorized — and that transition has to happen at a place you chose deliberately.
A named set of boundaries, each with an enforcement mechanism, so that "is this call trusted" has an answer at every hop instead of being inherited silently from the hop before.
Trust does not decrease gradually; it changes at points
Engineers tend to picture trust as a gradient: the further inside the network you are, the safer things get. That model is wrong in a way that causes real incidents. Trust changes at *points* — discrete places where a request or an identity is re-evaluated and either accepted or rejected. Between those points, trust is constant. If you cannot name the point, there is no point, and whatever crossed it kept whatever trust it started with.
The classic failure is the application-to-data boundary. The database sits in a private subnet, unreachable from the internet, and the team concludes it is protected. It is not authenticated *by anything except the network*. So when a server-side request forgery bug or a compromised dependency gives an attacker code execution in the application tier, they are already on the trusted side, and the database happily serves them every row. Network placement reduced exposure and enforced no boundary at all. This is why Public and Private Subnets is a reduction of attack surface and never a substitute for authentication.
The topology below marks the boundary crossings on the edges. Read it by asking, at each marked edge, one question: what would reject this call if the caller were an attacker who had already compromised the previous hop? Every edge where the honest answer is "nothing" is a boundary you have drawn on a diagram and not built.
The five boundaries and what enforces each
Each boundary has a control that genuinely enforces it and a control that people mistake for enforcement. The second column of the matrix is the useful one, because the mistake is consistent across organizations: the enforcement is assumed to have happened at the previous hop.
Boundary 4 — CI to production — deserves the most attention and gets the least. The pipeline holds an identity that can replace the running application. It executes code from a repository that many people can write to, it pulls dependencies from the public internet, and it typically runs on infrastructure managed with less rigour than the production it deploys to. A compromised pipeline does not need to break into production; it is *authorized* to change production. Scope it, make it assume short-lived credentials rather than hold static keys, restrict which branches can reach the production environment, and require approval on the promotion step rather than on the build — see Build Once, Promote the Same Bytes and Roles vs Static Keys.
Boundary 5 — provider to you — is the one people forget is a boundary at all. The provider secures the hypervisor, the physical facility and the managed service internals. You own the configuration, the identities, the data and every access decision. Almost every publicized "cloud breach" sits squarely on your side of that line: a misconfigured bucket, an over-broad role, a leaked key. See Shared Responsibility.
| Boundary | What changes | Real enforcement | Mistaken for enforcement |
|---|---|---|---|
| Internet → edge | Anonymous hostile traffic becomes shaped, rate-limited, TLS-terminated traffic | TLS, WAF rules, rate limits, request size and shape validation | "It is behind a CDN" — a CDN does not authenticate your users |
| Edge → application | A shaped request becomes an authenticated, authorized action on behalf of a principal | Per-request authentication and authorization in the application or gateway | "The edge already checked it" — the edge checked traffic, not identity |
| Application → data | An authorized action becomes a query against real records | A distinct database credential with a least-privileged role, TLS in transit, tenant scoping in the query path | "The database is in a private subnet" — placement is exposure reduction, not authentication |
| CI → production | A merged commit becomes a running artifact | Scoped short-lived deploy identity, branch restrictions, approval on promotion, signed artifacts pinned by digest | "Only our team can merge" — the pipeline also runs third-party dependencies and actions |
| Provider → you | Their responsibility ends and yours begins | Your configuration, identities, policies, encryption choices and access reviews | "The provider handles security" — they handle their layer; the breach headlines are almost always yours |
The boundary that deploys to production
Work the CI-to-production boundary as a policy problem and the shape of the fix appears immediately. The pipeline identity needs to do exactly one thing to production: replace the running version of a known set of workloads with an artifact that already exists in the registry. It does not need to create identities, read secrets, modify network rules or touch the database. Most pipelines are granted all of those because a broad role made the first deploy work and nobody narrowed it afterwards.
Two specific properties matter more than the permission list. First, the credential must be short-lived and obtained through a federated trust relationship — the pipeline proves which repository and which branch it is running for, and receives a session that expires. A static key stored in the CI system is a permanent production credential sitting in a system with a large attack surface and a long history of supply-chain incidents. Second, the *promotion* to production is the approval gate, not the build. Building on every commit is fine; deploying an arbitrary commit to production is the action that needs a human or a policy in the path.
The panel below is the narrow version. Note the denials: no identity creation, no secret reads, no network changes, and no ability to modify the audit configuration. The pipeline can deploy the application and cannot rewrite the environment around it — so a compromised pipeline is a serious incident that is nonetheless bounded and, crucially, visible.
- compute:UpdateService and compute:DescribeService on the named service only
- registry:GetImage on the one repository this service is built from
- deploy:CreateRelease and deploy:Rollback for this service
- Exactly the update and rollback verbs for one service, with a session that expires in under an hour
- iam:Create*, iam:AttachPolicy, iam:CreateAccessKey — no privilege escalation, no minting of credentials
- secrets:GetSecretValue — the workload reads its own secrets at runtime; the pipeline never sees them
- network:Authorize*, network:Modify* — deployments do not change the network boundary
- audit:* — the deployer cannot alter the record of what it did
Blast radius: A compromised pipeline can ship a malicious version of one service and can be rolled back. It cannot create a persistent identity, cannot read the database credential, cannot open the network, and cannot hide what it did. Compare with the common alternative — an administrator-equivalent static key in the CI settings — where the same compromise is a full account takeover with no expiry.
Key points
- Trust changes at discrete points, not gradually. If you cannot name the enforcement at a crossing, there is no boundary there.
- Network placement reduces exposure and never authenticates. A private database is unauthenticated to anything that reaches the private network.
- The edge shapes traffic; the application authorizes principals. Neither one does the other's job.
- CI to production is a production-privileged boundary reviewed like a build tool, which is why it is the most under-defended crossing in most systems.
- Provider to you is a boundary too, and nearly every publicized cloud breach sits on your side of it.
- Short-lived federated credentials scoped to one repository and branch turn a permanent production key into an expiring, attributable session.
The loop, answered
Every field is required, which is why no lesson here can recommend something without saying what it costs and what simpler thing to consider first.
- • Enumerate every edge in the topology where the caller's identity or trust level differs from the callee's expectation.
- • For each, name the control that would reject a hostile caller: a TLS requirement, an authorization check, a distinct credential, a policy condition, an approval gate.
- • Verify enforcement is at the boundary rather than upstream of it — an authorization check performed only at the gateway is not enforced for anything that reaches the service another way.
- • Assume compromise of each side in turn and re-ask the question, which is how "trusted because internal" gets exposed.
- • Record the boundaries in the same artifact as the topology, so a new component makes its crossings explicit at design time.
- • Re-evaluate boundaries whenever a component is added; new services default to inheriting the trust of whatever called them.
- • Rotate the CI-to-production trust relationship into federated short-lived credentials and delete the static keys, in that order.
- • Test the boundaries: from a shell in the application tier, try to reach something the application should not reach. Document what happened.
- • Keep authentication at every hop even when the network already restricts it — defence in depth here is cheap and pays out during exactly the incidents that matter.
- • Review the provider boundary annually against the shared-responsibility documentation, which moves as services change.
- • Perimeter collapse: one compromised component in the private network reaches everything, because internal calls were never authenticated.
- • Authorization enforced only at the gateway, bypassed by any internal caller, service mesh route or debugging path that reaches the service directly.
- • A pipeline with an administrator-equivalent static key: the supply-chain compromise becomes a full account compromise with no expiry and poor attribution.
- • Boundary drift — a new service added inside the trusted zone with no authentication, because "everything in here is internal".
- • Shared credentials spanning a boundary, so the crossing is real but the record of who crossed is not — see Audit Trails.
- • A tenant boundary enforced only in application code, so a single missing predicate in one query leaks data across tenants.
- • Boundary count grows with the number of edges, not services, so a mesh of mutually-calling services multiplies crossings faster than the service count suggests.
- • Per-hop authentication becomes operationally heavy past a few dozen services, which is where workload identity issuance and mutual TLS start paying for their complexity.
- • Account or project separation is the cheapest coarse boundary at scale and caps blast radius better than any in-network control.
- • The dimension that runs out first is consistency: each new team re-derives the boundary rules slightly differently unless they are written down as platform defaults.
- • This lesson is the structural backbone of the Security View — The Security View marks the crossings, this one decides what enforces them.
- • Every boundary needs both authentication (who is calling) and authorization (may they do this). Network controls contribute to neither and are still worth having.
- • A boundary with no logging is a boundary you cannot prove held. Log crossings, especially the CI-to-production one.
- • Assume-breach is the working posture: for every component, ask what an attacker with code execution there reaches next, and treat the answer as the boundary's real strength.
- • Enforcing boundaries costs latency and engineering time: an authorization check per hop, certificate issuance and rotation, an approval gate that slows deploys.
- • Private connectivity, dedicated accounts per environment and workload-identity infrastructure carry real, recurring line items.
- • Federated short-lived credentials are usually free and are the highest-value change on this list, which makes them the obvious place to start.
- • The counterfactual: a boundary that did not hold turns a contained incident into an account-wide one, with response costs that dwarf the controls.
- • Authentication failure rates per hop — a sudden rise on an internal boundary is either a misconfiguration or someone probing it.
- • Deploy events by identity and source repository, so a deploy from an unexpected branch or actor is visible immediately.
- • Cross-boundary connection attempts that were denied, which is the evidence that the control is doing something.
- • Age and type of pipeline credentials, tracked as a property: any static long-lived production key is a standing finding.
- • The signal that lies: green internal dashboards. Lateral movement inside a trusted zone generates authorized, successful, entirely normal-looking traffic.
- • For a single application and one database, two boundaries do most of the work: authenticate users at the application, and give the application its own database credential. Do not build a mesh for this.
- • Account or project separation per environment is a coarse, cheap boundary that outperforms fine-grained in-network controls for small teams.
- • A managed identity-aware proxy instead of building per-hop mutual TLS, when the requirement is protecting administrative interfaces rather than service-to-service traffic.
- • If the tenant boundary is the one that actually matters, enforce it in the data layer — row-level policies or database-per-tenant — rather than trusting every query to carry the right predicate.
- • Buys containment: a compromise stops at the boundary instead of becoming an account takeover. Costs latency per hop, certificate and credential machinery, and slower deploys where approval gates sit.
- • Per-hop authentication makes local development and debugging genuinely harder, which is the real reason teams skip it.
- • Strict account separation improves containment and complicates every legitimate cross-environment operation, from data copies to shared observability.
What people believe, and what is true
The private subnet is the boundary.
It is exposure reduction. Anything that reaches the private network — a compromised container, an SSRF bug, a peered VPC — is already on the trusted side of it.
The gateway authenticates, so services do not have to.
Any path that reaches a service directly bypasses the gateway. Enforcement upstream of a boundary is not enforcement at it.
CI is a build tool.
CI holds an identity authorized to replace what runs in production, executes third-party code, and is usually the least-hardened production-privileged system you own.
The provider handles security.
They secure their layer. Configuration, identity, data and access decisions are yours, and that is where essentially every publicized cloud breach originates.