Note 04
Capabilities: a network policy model that does not drift
A default-deny network policy model where workloads declare dependencies by label, destinations admit the same label, and a narrowly privileged pipeline applies it all.
- Kubernetes
- Cilium
- GitOps
- Security
Why hand-written policies drift
A caller's egress rule and the destination's ingress rule are usually written in different places by different people. A rule gets copied with more than it needs. A "temporary" broad rule stays because nobody can prove what removing it would break.
Capabilities
Every tenant namespace is deny-by-default. A shared dependency is a capability: a reusable policy component the namespace lists, and a label the workload carries. The destination admits callers by the same label, so the two sides cannot drift apart.
# destination side: admit any caller that carries the capability, from any namespace
ingress:
- fromEndpoints:
- matchLabels: { netpol.cell.io/svc-payments: "true" }
matchExpressions:
- { key: k8s:io.kubernetes.pod.namespace, operator: Exists }
toPorts: [{ ports: [{ port: "8080", protocol: TCP }] }]The matchExpressions line is not decoration: without it, a fromEndpoints selector silently means "only this namespace".
Where the line is
The test for "is this a capability" is: if a second workload carried this label, would that be correct? Shared services and infrastructure pass it. A destination only one workload uses, such as its own bucket or database, does not: it lives as a private policy in that namespace, so a label can never grant access to someone else's data.
Guardrails on the guardrails
- Policies are applied by a Flux service account that can manage Cilium policies and nothing else.
- CI rejects
world,clusteror0.0.0.0/0in allow rules, FQDN patterns that cover a whole provider, CIDR rules without ports, and the cross-namespace selector that silently means "this namespace only". - Dropped flows are exported and alerted on, so a missing capability becomes a ticket instead of a timeout someone debugs for a day.
- Broad legacy rules are removed only after real traffic shows the replacement covers every flow: parity first, removal second.
Who may talk to whom becomes readable in one directory and reviewable in one pull request.
Start a conversation
Bring your infrastructure
into focus.
Tell me what you are building, operating, or trying to improve.