Skip to content
Install the CLI

Explanation

Network policy

Writing default-deny Kubernetes network policies by hand is tedious and fragile. To let one web service talk to one database, you normally have to write an egress rule in the caller’s namespace, a matching ingress rule in the database’s namespace, and a DNS rule so the caller can resolve the hostname. In Kix, when you turn on networkPolicy.enable = true, the compiler generates both ends of those firewall rules automatically from the service connections your packages already declare.

With networkPolicy.defaultDeny = true (the default when network policy is enabled), Kix emits a baseline default-deny policy in every Kix-managed namespace for the configured networkPolicy.directions ("both", "ingress", or "egress"). Nothing in those namespaces can dial out or accept traffic unless a specific rule allows it.

For every service dependency in the graph, Kix opens a narrow path on both ends:

  • Caller egress: A policy in the calling workload’s namespace allowing egress to the target Service’s pod selector and numeric ports, plus egress to the cluster DNS provider.
  • Callee ingress: A paired policy in the target Service’s namespace (emit.inNamespace) allowing ingress from the caller’s pod selector and namespace on those ports.

Because Kix compiles the whole cluster at once, it knows the pod labels, namespaces, and container ports of both the caller and the callee. The enforcer package (such as packages.cilium, which claims the network-policy-enforcer role) supplies the policy builder (out.netpol.mkPolicy). If networkPolicy.enable = true is set without an enforcer in the cluster, evaluation fails immediately rather than generating policies that nothing would enforce.

How Kix discovers connections from endpoint facts

Section titled “How Kix discovers connections from endpoint facts”

Kix distinguishes between naming a resource (for deploy ordering) and dialing a Service over the network.

When a Service is sealed by scope.mkResource, its address-shaped outputs (out.fqdn, out.url, and custom outputs) carry an endpoint fact in their Nix string context alongside the Service’s identity path. That fact records the Service’s namespace, pod selector, and ports.

  • Conducting versus terminating facts: Non-workload resources such as ConfigMaps and Secrets conduct endpoint facts onward. If a package writes backend.out.fqdn into a ConfigMap and mounts that ConfigMap into a Deployment, the Deployment absorbs the endpoint fact from the ConfigMap. Workload constructors (scope.mkDeployment, scope.mkStatefulSet, scope.mkDaemonSet, and scope.mkJob) terminate the fact and turn it into a network connection.
  • Structural references: Reading out.name carries only the resource’s identity path, not an endpoint fact. That orders the consumer after the target without opening a firewall hole.

Beyond cross-package Service references, Kix automatically derives rules for four other structural patterns:

  1. DNS egress: Every workload with egress policy gets a rule allowing queries to the cluster’s DNS pods, and any workload that dials a Service also gets a deploy-ordering dependency on the DNS provider.
  2. Kubernetes API server egress: During instance evaluation, Kix walks the instance’s RoleBinding and ClusterRoleBinding resources. If a workload’s ServiceAccount is bound to a role, Kix emits an egress rule allowing that workload to reach kube-apiserver.
  3. External ingress and exposure: A workload fronted by a LoadBalancer Service (unless served by an internal provider load-balancer class) gets an ingress rule from world. Similarly, kix.expose automatically emits an ingress rule on the backend admitting the configured Ingress or Gateway controller’s pods.
  4. StatefulSet peer traffic and metric scrapes: StatefulSet workloads receive self-ingress and self-egress rules so peer replicas can form a cluster. At the cluster level, Kix joins each Prometheus or VMAgent custom resource with the ServiceMonitor and PodMonitor objects it selects, opening scrape egress from the monitoring server and scrape ingress on each target’s numeric pod ports.

Different network facts become available at different stages of evaluation. Kix attaches its network policy plugin across three phases so each policy is built as soon as its inputs exist:

PhaseWhat Kix knowsPolicies emitted
Workload phase (inside sealWorkload, pre-seal)One workload’s pod spec, absorbed endpoint facts, and package intentsPer-connection caller egress, callee ingress, DNS egress, StatefulSet self-rules, and declarative package intents
Instance phase (post-fixpoint processor)All resources belonging to one package instanceAPI server egress (from the ServiceAccount to RoleBinding walk), LoadBalancer world ingress, and policies for custom resources that declare factBehavior.endpoint = "terminate"
Cluster phase (whole-cluster processor)All active instances, namespaces, and monitorsPer-namespace default-deny walls, Prometheus and VMAgent scrape policies, and networkPolicy.platformIntents

Running the workload phase before the workload resource is sealed is critical for rollout safety. Kix splices the generated policies into the workload’s requires list before computing its store hash. As a result, kix deploy applies the CiliumNetworkPolicy resources and waits for them to become ready before applying the Deployment itself, ensuring pods never start in a window where their firewall rules are missing.

A dependency graph only sees connections between resources in the build. Two kinds of traffic fall outside that graph:

  • External traffic (meta.network.intents): The internet, external APIs, or the Kubernetes API server dialing an admission webhook are not resources in the Kix build. A package declares these connections once under meta.network.intents using targets like "world", "apiserver", "dns", or "all-pods".
  • Operator-created pods (kix.operandPolicy and platformIntents): When an operator creates data-plane pods at runtime from a custom resource (such as database replicas or proxy pods), Kix does not build their Deployment directly. Packages that author those custom resources use kix.operandPolicy (or CRD factBehavior) to emit policies selecting the operator’s pod labels, while cluster operators can use networkPolicy.platformIntents to open traffic for pods managed outside Kix.