Skip to content

Commit aa98008

Browse files
authored
Merge pull request #11 from aws-samples/feat/enforce-network-policy
feat(security): enforce per-tenant NetworkPolicy via Amazon VPC CNI (ADR-0008)
2 parents c8d2a90 + de36426 commit aa98008

5 files changed

Lines changed: 130 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ cdk/cdk.json.usw2
1818
cdk/cdk.json.use1
1919
cdk/cdk-us-east-1.json
2020
screenshots/
21+
.playwright-mcp/

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,32 @@ Sign up at `https://<your-domain>/auth/` with an email matching your `allowedEma
139139
| Signup | AWS WAF Bot Control (opt-in) + email domain restriction + rate limiting |
140140
| Network | Internet-facing ALB with CF-only SG (pl-82a045eb) + AWS WAF + HTTPS |
141141
| Auth | Amazon Cognito signup + local token auth + 3-layer origin protection |
142-
| Tenant | Namespace isolation + NetworkPolicy + ABAC |
142+
| Tenant | Namespace isolation + NetworkPolicy (enforced via VPC CNI, see below) + ABAC |
143143
| Secrets | exec SecretRef -- fetched on-demand, never persisted |
144144
| LLM | Amazon Bedrock via Pod Identity -- zero API keys |
145145
| Cost | Per-tenant monthly budget with per-model pricing |
146146
| Data | PVC persists across scale-to-zero (Amazon EFS, multi-AZ) |
147147
| Audit | CloudTrail + Amazon S3 + Athena + Amazon EKS control plane logging |
148148

149+
### Security model
150+
151+
Each isolation layer buys something specific — and honestly does not buy
152+
something else. The per-tenant NetworkPolicy is **actually enforced**: the
153+
stack enables the Amazon VPC CNI network policy agent (off by default on
154+
Amazon EKS), giving tenants an HTTPS-only egress posture (443 to public,
155+
Pod Identity/IMDS/DNS allowed, cross-tenant and VPC-internal blocked).
156+
Exfiltration over permitted HTTPS remains out of scope for L3/L4 policy —
157+
the upgrade path (AWS Network Firewall FQDN filtering, DNS-aware CNI) is
158+
documented as a non-goal. Note also that enforcement runs in *standard mode*:
159+
a brand-new pod is default-allow for the brief window before its policies
160+
attach, so NetworkPolicy is not a startup-time exfiltration boundary (see
161+
ADR-0008 for strict mode).
162+
163+
Details and trade-offs:
164+
165+
- [ADR-0007 — gVisor runtime tier + runtime tier extension contract](docs/adr/0007-gvisor-runtime-tier.md)
166+
- [ADR-0008 — Enforced per-tenant egress control](docs/adr/0008-enforce-network-policy-egress.md)
167+
149168
## Cost
150169

151170
| Resource | 3 tenants | 100 tenants |

cdk/lib/eks-cluster-stack.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,25 @@ export class EksClusterStack extends cdk.Stack {
164164
});
165165

166166
// ── Other EKS Add-ons (no special IAM needed) ───────────────────────────
167-
for (const addonName of ['eks-pod-identity-agent', 'vpc-cni', 'coredns', 'kube-proxy']) {
167+
for (const addonName of ['eks-pod-identity-agent', 'coredns', 'kube-proxy']) {
168168
new eks.CfnAddon(this, addonName.replace(/-/g, ''), {
169169
clusterName: cluster.clusterName,
170170
addonName,
171171
});
172172
}
173173

174+
// VPC CNI with NetworkPolicy enforcement enabled (ADR-0008). Without this,
175+
// the per-tenant NetworkPolicy objects shipped by the Helm chart are stored
176+
// by the API server but NOT enforced — the Amazon VPC CNI network policy agent is
177+
// off by default on Amazon EKS.
178+
// https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html
179+
new eks.CfnAddon(this, 'vpccni', {
180+
clusterName: cluster.clusterName,
181+
addonName: 'vpc-cni',
182+
configurationValues: JSON.stringify({ enableNetworkPolicy: 'true' }),
183+
resolveConflicts: 'OVERWRITE',
184+
});
185+
174186
// ── CloudWatch Container Insights ─────────────────────────────────────
175187
const cwObsRole = new iam.Role(this, 'CwObservabilityRole', {
176188
assumedBy: new iam.ServicePrincipal('pods.eks.amazonaws.com'),
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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

helm/charts/openclaw-platform/values.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ ingress:
4343
alb.ingress.kubernetes.io/scheme: internal
4444
alb.ingress.kubernetes.io/target-type: ip
4545

46+
# Per-tenant egress control (ADR-0008). Enforced only when the cluster enables
47+
# the Amazon VPC CNI network policy agent (this stack's CDK does). Posture: HTTPS-only
48+
# egress — DNS / Pod Identity / IMDS / TCP 443-to-public allowed; plain HTTP
49+
# (:80), non-443 protocols, and VPC-internal (10.0.0.0/8) blocked.
50+
# If your VPC CIDR is not within 10.0.0.0/8, adjust the egress `except` block
51+
# in templates/networkpolicy.yaml to your VPC CIDR.
4652
networkPolicy:
4753
enabled: true
4854

0 commit comments

Comments
 (0)