Network Policies¶
banlieue ships opt-in NetworkPolicy templates in deploy/network-policies/
(ADR-0094). Without them, every banlieue pod can reach anything on the pod
network and beyond, and anything in the cluster can reach its ports. With
them, each component can talk only to what it needs.
They are not applied by the default install. A wrong egress rule breaks banlieue quietly (a provider that cannot reach its backend just stops making progress), so applying them is a deliberate step.
What the policies allow¶
00-default-deny.yaml denies all ingress and egress for every pod labelled
app.kubernetes.io/name=banlieue. One allow policy per component then opens
what that component needs, selected by app.kubernetes.io/name=banlieue plus
app.kubernetes.io/component:
| Component | Runs as | Egress | Ingress |
|---|---|---|---|
controller |
Deployment | DNS, API server | metrics, health |
operator |
Deployment | DNS, API server | metrics, health |
imagebuilder |
Deployment | DNS, API server | metrics, health |
provider-vsphere |
Deployment (hand-installed or operator-spawned) | DNS, API server, vCenter TCP 443 | metrics, health |
provider-proxmox |
operator-spawned Deployment | DNS, API server, Proxmox VE API TCP 8006 | metrics, health |
provider-libvirt |
operator-spawned Deployment | DNS, API server, libvirtd TLS TCP 16514 | metrics, health |
vsphere-import |
Job in the image-build namespace | DNS, API server, TCP 443 (vCenter and the ESXi hosts behind each datastore) | none |
libvirt-import |
Job in the image-build namespace | DNS, API server, TCP 16514 | none |
registry-push |
Job in the image-build namespace | DNS, registry TCP 443 | none |
The rules behind those words:
- DNS: UDP and TCP 53, only to pods labelled
k8s-app=kube-dnsin thekube-systemnamespace. - API server: TCP 443 and 6443 to any address. The API server is not a
pod, so it cannot be matched by a pod selector, and its address differs per
cluster. Port 6443 is there because many CNIs evaluate policy after the
kubernetesService has been translated to the API server's real port. - Backends: only the backend's port, to any address.
metrics(8080): only from pods in namespaces labelledbanlieue.io/monitoring=true.health(8081): from anywhere. Kubelet probes do not come from a pod, so they cannot be matched by a selector.
The registry push Job runs without a service account token and never calls the API server, so it gets no API server rule.
Because policies select by label rather than by name, the same file covers a
provider you installed from deploy/provider-vsphere/ and one the operator
spawned for a Provider: both carry app.kubernetes.io/component=provider-<backend>.
Applying them¶
The manifests carry no namespace, so you apply the directory once per namespace that runs banlieue pods:
# Controller, operator, imagebuilder and any hand-installed provider.
kubectl apply -n banlieue-system -f deploy/network-policies/
# Image-build namespace: the import and registry push Jobs.
kubectl apply -n banlieue-imagebuild -f deploy/network-policies/
Operator-spawned providers run in ProviderClass.spec.workloadNamespace, or
in the Provider's own namespace when that field is unset. Apply the same
directory there too:
kubectl get providerclass -o custom-columns=NAME:.metadata.name,NS:.spec.workloadNamespace
kubectl apply -n <workload-namespace> -f deploy/network-policies/
Applying every file in every namespace is harmless: a policy whose component does not run in that namespace selects no pods.
Then let your monitoring namespace scrape metrics:
The default deny selects banlieue pods only. Other pods in the same
namespace, such as kairos-operator's OSArtifact build pods in
banlieue-imagebuild, are not affected by these policies.
Narrowing the destinations¶
Every rule except DNS restricts the port and leaves the destination open,
because banlieue cannot know your addresses. To narrow one, add a to: with
your CIDRs to your copy of the file. Each rule's comment shows the spot. For
example, pinning the API server and a libvirt host network:
egress:
- to:
- ipBlock:
cidr: 192.0.2.10/32 # API server
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 6443
- to:
- ipBlock:
cidr: 198.51.100.0/24 # libvirt hosts
ports:
- protocol: TCP
port: 16514
Find the API server address with
kubectl get endpointslices -n default -l kubernetes.io/service-name=kubernetes.
On vSphere, TCP 443 appears in both the API server rule and the vCenter rule,
so 443 is open to any address until you narrow both. Some CNIs do not match
the API server by ipBlock at all (it is host-network traffic); check your
CNI's documentation before narrowing that rule.
Other site edits:
- A registry on another port (for example
registry.internal:5000, or plain HTTP) needs that port added toregistry-push.yaml. - OpenTelemetry tracing (ADR-0092): when you set
OTEL_EXPORTER_OTLP_ENDPOINT, each exporting component needs one more egress rule for the collector's address and port. The shipped policies do not guess it, so without that rule spans are dropped.
Out of scope¶
- Cloud Hypervisor provider. It runs on the hypervisor host under systemd, not in a pod, so a NetworkPolicy cannot select it. Restrict it with the host's own firewall.
- kairos-operator build pods. They are created and labelled by kairos-operator, not banlieue, and pull from whatever registries the image source names.
How the shape is kept honest¶
crates/banlieue-operator/tests/network_policies.rs parses every manifest in
deploy/network-policies/ and fails if:
- the default deny is missing;
- a component with a Deployment under
deploy/, a provider the operator spawns, or a banlieue Job has no policy; - a selector uses label keys or values other than the ones the operator stamps;
- any egress rule allows a port outside UDP/TCP 53, TCP 443, 6443, 8006 and 16514, or a component gets a port it does not need;
- any ingress rule allows something other than
metricsandhealth.
It checks the files in the repository, not your edited copies.