Restrict agent network traffic with NetworkPolicy¶
This guide shows you how to apply per-agent ingress and egress restrictions using a networking.k8s.io/v1 NetworkPolicy generated by the operator from your CR spec. All ingress/egress rules pass through unchanged from the spec; there are no operator-side defaults or rewriting.
How to enable¶
spec:
networkPolicy:
enabled: true
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: hermes-operator-system
egress:
- to:
- namespaceSelector: {} # any namespace
ports:
- protocol: TCP
port: 443 # LLM provider HTTPS
- to:
- namespaceSelector: {} # cluster DNS
ports:
- protocol: UDP
port: 53
The operator reconciles a NetworkPolicy/hermes-<name> in the agent's namespace. The podSelector.matchLabels targets the agent's standard labels (hermes.undermountain.cc/{agent, agent-ns}); no need to author the selector yourself.
Spec fields¶
| Field | Type | Notes |
|---|---|---|
enabled |
bool | When false, any previously-created NetworkPolicy is deleted in-place (not ownerRef GC, which only triggers on CR delete). |
ingress |
[]networkingv1.NetworkPolicyIngressRule |
Direct passthrough. Empty + policyTypes includes "Ingress" → ALL ingress denied. |
egress |
[]networkingv1.NetworkPolicyEgressRule |
Direct passthrough. Empty + policyTypes includes "Egress" → ALL egress denied. |
policyTypes |
[]networkingv1.PolicyType |
When empty, K8s derives from whether ingress/egress are non-nil. |
Using K8s's own types means anyone who has written a NetworkPolicy by hand already knows the schema; there is no operator-side abstraction to learn.
Common patterns¶
Allow only operator-namespace ingress (so dashboard probe works) + LLM-provider egress¶
spec:
networkPolicy:
enabled: true
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: hermes-operator-system
egress:
- to:
- namespaceSelector: {}
ports:
- { protocol: TCP, port: 443 }
- to:
- namespaceSelector: {}
ports:
- { protocol: UDP, port: 53 }
Ingress-only deny-all (egress unmanaged)¶
Total deny-all¶
spec:
networkPolicy:
enabled: true
policyTypes: [Ingress, Egress]
# ingress + egress both nil → both denied
The CRD rejects enabled: true with no ingress and no egress rules, since that combination is almost always user error.
Important: CNI enforcement¶
Effective enforcement depends on the cluster's CNI. Calico, Cilium, and Antrea enforce NetworkPolicy. The default kindnet (kind clusters) creates the resource but does not enforce it in the data plane. The operator's E2E suite verifies the resource shape, label selectors, and toggle-off cleanup; it does not test enforcement (kindnet would not validate it).
To verify enforcement in a real cluster, exec into the agent pod and try to reach a forbidden destination:
kubectl -n hermes exec deployment/hermes-<name> -- \
curl -sv --max-time 5 https://example.com
# should hang / time out under deny-all egress
What you can't express here¶
- FQDN-based egress (e.g., allow only
api.deepseek.com). StandardNetworkPolicyis IP/port/selector-based. Calico'sGlobalNetworkPolicywith DNS-based selectors or Cilium'sCiliumNetworkPolicycan express this; both are out of scope for the operator's CRD, but cluster admins can layer their own policies on top. - Cluster-wide policies across multiple agents. Each
HermesAgent's NetworkPolicy is per-CR with no shared template. For uniform rules across many agents, manage that separately (Kyverno, aGlobalNetworkPolicy, etc.).