A pragmatic cloud security baseline for enterprise product teams implementing zero trust without slowing delivery pipelines.
"Zero trust" is the most overloaded phrase in enterprise security. In most pitches it means "buy our identity-aware proxy". In practice, what actually moves the security posture of an enterprise dev org is a small set of defaults that make the insecure path the harder path. This post is what we deploy as a baseline for enterprise product teams running on AWS or Azure — not a Forrester wave, just what we ship.
The five non-negotiable defaults
These are enforced in code (Terraform modules, CI gates, organization policies) so they cannot be skipped:
- **No static cloud credentials anywhere.** Workloads use workload identity (IRSA on EKS, managed identity on Azure). CI uses OIDC federation to assume short-lived roles. Long-lived access keys are blocked at the org level.
- **Every environment in its own account/subscription.** Dev/staging/prod are isolated by account, not by IAM policy inside one account. Blast radius matters more than convenience.
- **Least-privilege IAM templated in code.** Teams cannot write inline policies in the console. They pick from a curated module library; new permissions require a PR to the security repo.
- **All outbound traffic via egress proxy with allowlist.** No direct internet egress from prod workloads. Allowlist is a YAML file under version control.
- **All audit events to a write-only log account.** No production role can delete or modify audit logs. Period.
Control layers
Identity layer
Everything starts with identity. SSO from a single IdP (Entra ID or Okta) federates into the cloud, into the CI system, into observability, into the artifact registry. MFA is enforced at the IdP, not at each downstream system — doing it the other way creates MFA fatigue and weak fallback paths.
Service-to-service authentication uses SPIFFE/SPIRE-issued certificates inside the cluster and signed JWTs (with mTLS at the mesh layer) across services. We don't use shared API keys between internal services.
Network layer
Network is no longer the primary trust boundary, but it's still the cheapest one. We default to:
- Private VPCs/VNets with no public load balancers in front of internal services.
- Public-facing edge through a single CDN + WAF with deterministic IP ranges.
- Service mesh (Linkerd or Istio) enforcing mTLS for all in-cluster traffic.
Policy-as-code in CI
Every infrastructure PR runs through OPA/Conftest policies before plan/apply. The policies block: public S3 buckets, RDS without encryption, IAM policies with `*` resource, security groups open to 0.0.0.0/0 on non-edge ports, container images from unsigned registries.
This is more effective than runtime scanning because the insecure resource never gets created.
What we don't bother with
We deliberately don't deploy: agent-based EDR on every container (noisy, slow), CASB for SaaS unless there's a specific compliance ask, blockchain-anything. These create more operational debt than security value for mid-market enterprises.
Developer experience
The baseline only sticks if developers can ship. We maintain: a `vc-init` CLI that scaffolds a new service with all defaults in 90 seconds; a one-click ephemeral environment per PR; a clear exception process (90-day expiry, security review, automatic re-evaluation) for the rare case a control needs a temporary waiver.
Closing
Zero trust isn't a product, it's a default. Pick five controls, enforce them in code, make the secure path the easy path, and audit quarterly. Skip the rest until you have a specific reason.