|
| 1 | +# ADR-0008: Enforce per-tenant egress control via Amazon VPC CNI NetworkPolicy |
| 2 | + |
| 3 | +- **Status:** Accepted |
| 4 | +- **Date:** 2026-07-14 |
| 5 | +- **Deciders:** HC Lo (hclo) |
| 6 | +- **Depends on:** [ADR-0007](0007-gvisor-runtime-tier.md) |
| 7 | + |
| 8 | +## Context |
| 9 | + |
| 10 | +For multi-tenant agent workloads, the highest-leverage isolation control is not |
| 11 | +the container runtime — it is **egress control**. Credential exfiltration and |
| 12 | +lateral movement use perfectly normal syscalls that any runtime (runc, gVisor, |
| 13 | +microVM) permits; only the network layer can stop them (see the mechanism table |
| 14 | +in ADR-0007). |
| 15 | + |
| 16 | +The Helm chart has always shipped a per-tenant `NetworkPolicy` |
| 17 | +(`networkPolicy.enabled: true` by default) that is effectively default-deny: |
| 18 | + |
| 19 | +- **Egress allowed:** DNS, Amazon EKS Pod Identity Agent (`169.254.170.23`), IMDS, |
| 20 | + TCP 443 to public IPs (except `10.0.0.0/8`), same-namespace pods. |
| 21 | +- **Everything else denied:** cross-tenant traffic, VPC-internal addresses, |
| 22 | + non-443 protocols. |
| 23 | + |
| 24 | +**However, on Amazon EKS these objects were stored but not enforced.** The Amazon VPC |
| 25 | +CNI's NetworkPolicy support is disabled by default and must be enabled |
| 26 | +explicitly on the add-on; nothing in this stack did so. The policy was |
| 27 | +decorative. |
| 28 | + |
| 29 | +## Decision |
| 30 | + |
| 31 | +1. Enable NetworkPolicy enforcement on the `vpc-cni` managed add-on via |
| 32 | + `configurationValues: {"enableNetworkPolicy": "true"}` (CDK). |
| 33 | +2. Keep the existing per-tenant policy as-is (HTTPS-only egress posture). |
| 34 | +3. Treat FQDN-level egress filtering as a documented **non-goal** with an |
| 35 | + upgrade path (below), not a platform feature. |
| 36 | + |
| 37 | +## What this changes for agent behavior |
| 38 | + |
| 39 | +| Tenant/agent behavior | After enforcement | |
| 40 | +|-----------------------|-------------------| |
| 41 | +| HTTPS APIs and sites (Amazon Bedrock, AWS Secrets Manager, ghcr.io, npm, web tools over 443) | Unchanged | |
| 42 | +| Pod Identity, IMDS, DNS, same-namespace | Unchanged (explicit allows) | |
| 43 | +| Amazon EFS mounts | Unchanged (NFS mount runs in the node network namespace, not the pod's) | |
| 44 | +| Plain-HTTP (`http://`, port 80) external sites | **Blocked** | |
| 45 | +| Non-443 protocols (SSH/git :22, custom :8080, SMTP) | **Blocked** | |
| 46 | +| VPC-internal / cross-tenant addresses (`10.0.0.0/8`) | **Blocked** | |
| 47 | + |
| 48 | +## Residual risk (stated honestly) |
| 49 | + |
| 50 | +Egress to **any** public host on TCP 443 remains allowed. An agent that is |
| 51 | +prompt-injected into exfiltrating data over HTTPS is *not* stopped by this |
| 52 | +policy. Kubernetes `NetworkPolicy` is L3/L4 and cannot express hostnames. |
| 53 | + |
| 54 | +## Upgrade path (non-goals for this sample) |
| 55 | + |
| 56 | +- **AWS Network Firewall** — VPC-level FQDN/SNI allowlisting; cluster-wide |
| 57 | + granularity, no chart changes. |
| 58 | +- **CNI with DNS-aware policy (e.g. Cilium)** — per-tenant FQDN allowlists; |
| 59 | + requires replacing the CNI, too heavy for this sample. |
| 60 | +- **Egress proxy (allowlist + audit)** — strongest auditability; requires the |
| 61 | + agent tool-chain to honor proxy configuration. |
| 62 | + |
| 63 | +## Options considered |
| 64 | + |
| 65 | +- **Enforce existing policy (chosen):** one add-on setting; per-tenant |
| 66 | + granularity; zero new components. |
| 67 | +- **AWS Network Firewall now:** rejected for the sample — cost + VPC redesign, |
| 68 | + and cluster-level (not per-tenant) granularity. |
| 69 | +- **Do nothing:** rejected — shipping an unenforced NetworkPolicy misleads |
| 70 | + adopters about the actual security posture. |
| 71 | + |
| 72 | +## Consequences |
| 73 | + |
| 74 | +**Positive** |
| 75 | +- The documented tenant isolation model ("Namespace isolation + NetworkPolicy + |
| 76 | + ABAC") becomes true. |
| 77 | +- Blocks metadata-service and cross-tenant lateral movement paths. |
| 78 | + |
| 79 | +**Negative / caveats** |
| 80 | +- Agent tools that need port-80 or non-443 endpoints require an explicit |
| 81 | + per-tenant policy exception. |
| 82 | +- Adopters whose VPC CIDR is outside `10.0.0.0/8` should adjust the `except` |
| 83 | + block to their VPC CIDR (documented in `values.yaml`). |
| 84 | +- Enforcement behavior at pod startup is *standard mode* (default-allow until |
| 85 | + policies attach); see the EKS docs if strict mode is required. |
| 86 | + |
| 87 | +## References |
| 88 | + |
| 89 | +- https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html |
| 90 | +- https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy-configure.html |
0 commit comments