Skip to content

Enable generated network policy

Enable generated network policy when you want Kix to restrict workloads using the Service relationships already present in the deployment graph. Kix creates default-deny policies and the allowances each workload needs.

This guide assumes the cluster has a package that provides the network-policy-enforcer role. Follow Install Cilium / network policy support first if the cluster does not have one.

In the calling package, use an address from the dependency’s out API:

how-to/platform/network-policy-client.nix (L15–L44)
build =
{
self,
scope,
backend,
...
}:
{
deployment = scope.mkDeployment {
name = scope.instanceName;
spec = {
replicas = 1;
selector.matchLabels = scope.selectorLabels;
template.spec.containers = [
{
name = "client";
image = "docker.io/library/nginx:1.27-alpine";
env = [
{
name = "BACKEND_URL";
value = backend.out.url { };
}
];
}
];
};
};
root = self.deployment;
};

View source on GitHub ↗

backend.out.url { } supplies the application with the Service URL and gives Kix the destination, port, and protocol needed for policy generation. Keep the endpoint connected to the workload that makes the request.

Not every out field provides enough information for policy generation. out.fqdn, out.url, and address outputs declared by a package identify the Service being reached, so Kix can derive an egress rule. Structural values such as out.name and out.selector create a dependency edge but no network-policy rule. Using one as an address can therefore pass kix check and still produce a blocked connection at runtime.

When a workload references a Service without using one of its addresses, Kix emits a no netpol edge derived trace with the Service name and a suggestion to use out.fqdn or out.url. This is a warning, not a check failure. If the reference is intentionally structural, such as a label or display value, mark it with facts.refOnly to suppress the warning.

Enable network policy in the cluster definition:

how-to/platform/network-policy-cluster.nix (L28–L32)
networkPolicy = {
enable = true;
defaultDeny = true;
directions = "both";
};

View source on GitHub ↗

defaultDeny = true creates a policy for each managed workload namespace. directions = "both" denies ingress and egress unless another generated rule allows it.

Add the destination and caller instances. For a cross-namespace connection, wire the dependency explicitly:

how-to/platform/network-policy-cluster.nix (L65–L73)
instances.services.backend = {
package = packages.echo-server;
config.message = "Hello across a generated network policy!";
};
instances.apps.client = {
package = clientPackage;
deps.backend = ref.services.backend;
};

View source on GitHub ↗

The client runs in apps; the backend Service and its pods run in services.

Evaluate the cluster:

Run in kix-examples/
❱ kix check how-to-platform-network-policy
 TOOL         RESULT  DETAILS                                                       
 eval         pass    48 manifests evaluated                                        
 kubeconform  pass    skipped (this validation tool is not yet integrated with Kix) 
 pluto        pass    skipped (this validation tool is not yet integrated with Kix) 
 kyverno      pass    skipped (this validation tool is not yet integrated with Kix) 
 scorecard    pass    0 errors, 45 warnings, 6 info

Inspect the policies produced by the build:

Run in kix-examples/ Output excerpt
❱ kix build how-to-platform-network-policy --output json Show output
[
  {
    "apiVersion": "cilium.io/v2",
    "kind": "CiliumNetworkPolicy",
    "metadata": {
      "name": "apps-default-deny",
      "namespace": "apps"
    },
    "spec": {
      "egress": [
        {}
      ],
      "endpointSelector": {},
      "ingress": [
        {}
      ]
    }
  },
  {
    "apiVersion": "cilium.io/v2",
    "kind": "CiliumNetworkPolicy",
    "metadata": {
      "name": "client-deployment-client-egress",
      "namespace": "apps"
    },
    "spec": {
      "egress": [
        {
          "toEndpoints": [
            {
              "matchLabels": {
                "k8s-app": "kube-dns",
                "k8s:io.kubernetes.pod.namespace": "kube-system"
              }
            }
          ],
          "toPorts": [
            {
              "ports": [
                {
                  "port": "53",
                  "protocol": "UDP"
                },
                {
                  "port": "53",
                  "protocol": "TCP"
                }
              ]
            }
          ]
        },
        {
          "toEndpoints": [
            {
              "matchLabels": {
                "app.kubernetes.io/instance": "backend",
                "app.kubernetes.io/name": "backend",
                "k8s:io.kubernetes.pod.namespace": "services"
              }
            }
          ],
          "toPorts": [
            {
              "ports": [
                {
                  "port": "8080",
                  "protocol": "TCP"
                }
              ]
            }
          ]
        }
      ],
      "endpointSelector": {
        "matchLabels": {
          "app.kubernetes.io/instance": "client",
          "app.kubernetes.io/name": "client"
        }
      }
    }
  },
  {
    "apiVersion": "cilium.io/v2",
    "kind": "CiliumNetworkPolicy",
    "metadata": {
      "name": "client-deployment-client-to-backend-ingress",
      "namespace": "services"
    },
    "spec": {
      "endpointSelector": {
        "matchLabels": {
          "app.kubernetes.io/instance": "backend",
          "app.kubernetes.io/name": "backend"
        }
      },
      "ingress": [
        {
          "fromEndpoints": [
            {
              "matchLabels": {
                "app.kubernetes.io/instance": "client",
                "app.kubernetes.io/name": "client",
                "k8s:io.kubernetes.pod.namespace": "apps"
              }
            }
          ],
          "toPorts": [
            {
              "ports": [
                {
                  "port": "8080",
                  "protocol": "TCP"
                }
              ]
            }
          ]
        }
      ]
    }
  },
  {
    "apiVersion": "cilium.io/v2",
    "kind": "CiliumNetworkPolicy",
    "metadata": {
      "name": "services-default-deny",
      "namespace": "services"
    },
    "spec": {
      "egress": [
        {}
      ],
      "endpointSelector": {},
      "ingress": [
        {}
      ]
    }
  }
]

The client egress policy permits DNS and traffic to the backend’s http port. Kix also places a matching ingress policy in services, scoped to the backend pods and the client pods in apps. The two namespace default-deny policies make those allowances effective.

Deploy the cluster, then list the policies installed by Cilium:

Run in kix-examples/
❱ kix deploy how-to-platform-network-policy
❱ kubectl get ciliumnetworkpolicies --all-namespaces