Kubernetes operator that generates NetworkPolicies for Kubernetes, Cilium or Calico from declarative allow/deny rules and built-in templates, with an optional learning period before enforcement.
A Kubernetes controller that automatically generates and manages Kubernetes Network Policies based on user-defined rules and templates.
The Network Policy Generator is a Kubernetes operator that simplifies the creation and management of Network Policies by providing two main operational modes:
- Learning Mode: Holds off enforcement for a specified duration, then switches to enforcing mode. Traffic collection is not implemented yet, so this mode records no traffic and produces no suggestions
- Enforcing Mode: Automatically generates and applies Network Policies based on the configured rules
This tool helps security teams and cluster administrators implement network segmentation more effectively by:
- Reducing manual Network Policy creation overhead
- Supporting both permissive (allow-based) and restrictive (deny-based) policy approaches
- Enabling gradual transition from learning to enforcement phases
- Supporting multiple CNI backends via
policyEnginefield (kubernetes,cilium,calico) - Providing built-in policy templates for common workload types (web-app, database, monitoring, etc.)
- Pod Label Selector — Target specific pods by label instead of entire namespaces
- CIDR-based Rules — Define ingress/egress rules for external IP ranges (e.g., databases, external APIs)
- Named Port Support — Use service port names (
http,grpc) instead of numeric ports - Dry Run Mode — Preview generated policies in status without applying them to the cluster
- Policy Diff/Audit — Track policy changes (Created/Updated/Unchanged, and Deleted for a policy the spec no longer needs) in status for audit trails
- Owned cleanup — Every generated policy carries
app.kubernetes.io/managed-by: network-policy-generatorand asecurity.policy.io/generator-uidlabel, so policies left behind by an earlier spec (a dropped namespace, a different engine) are deleted, and editing or deleting a generated policy by hand is corrected right away - Event Recording — Emit Kubernetes Events on policy apply, delete, mode transition, and errors
- Prometheus Metrics — Custom metrics for reconcile count, duration, active generators, and policy operations
- Schema Validation — Core spec rules are enforced by the CRD itself (CEL
x-kubernetes-validations), so they apply on every cluster with no extra setup - Webhook Validation — Optional admission webhook adding CIDR-format and cross-field checks (enable with
--enable-webhooksflag, requires cert-manager)
- Kubernetes v1.25+ (the CRD ships CEL validation rules, which require
x-kubernetes-validations) - kubectl v1.11.3+
- For Cilium policies: Cilium CNI installed on the cluster
- For Calico policies: Calico CNI installed on the cluster
Recommended: OCI registry (Helm 3.8+)
# Single-command install — no helm repo add needed
helm install npg oci://ghcr.io/somaz94/charts/network-policy-generator \
--version 0.5.0The chart creates the namespace named by its namespace value (network-policy-generator-system unless you change it) and deploys the controller there, whatever --namespace says, so install it without --namespace and --create-namespace.
Alternative: classic Helm repo
# Add the Helm repository
helm repo add network-policy-generator https://somaz94.github.io/network-policy-generator/helm-repo
helm repo update
# Install with default values
helm install npg network-policy-generator/network-policy-generator
# Or install with custom values
helm install npg network-policy-generator/network-policy-generator \
--set image.tag=v0.5.0 \
--set crds.cleanup=falseFor full Helm chart options, see Helm README.
kubectl apply -f https://raw.githubusercontent.com/somaz94/network-policy-generator/main/dist/install.yaml# Clone the repository
git clone https://github.com/somaz94/network-policy-generator.git
cd network-policy-generator
# Install CRDs
make install
# Deploy the controller
make deploy IMG=somaz940/network-policy-generator:v0.5.0# Check the controller is running
kubectl get pods -n network-policy-generator-system
# Check CRDs are installed
kubectl get crd networkpolicygenerators.security.policy.ioAfter installation, create a NetworkPolicyGenerator resource:
# Apply a sample policy
kubectl apply -f config/samples/security_v1_networkpolicygenerator-deny.yaml
# Check the status
kubectl get networkpolicygenerator
# View generated NetworkPolicies
kubectl get networkpolicy -AAvailable sample configurations:
security_v1_networkpolicygenerator-allow.yaml: Allow-based policy examplesecurity_v1_networkpolicygenerator-deny.yaml: Deny-based policy examplesecurity_v1_networkpolicygenerator.yaml: Combined deny-policy example (enforcing mode, global rules)security_v1_networkpolicygenerator-pod-selector.yaml: Pod label selector examplesecurity_v1_networkpolicygenerator-cidr-rules.yaml: CIDR-based egress/ingress rulessecurity_v1_networkpolicygenerator-named-port.yaml: Named port (http,grpc) examplesecurity_v1_networkpolicygenerator-dry-run.yaml: Dry run mode (preview without applying)security_v1_networkpolicygenerator-full-features.yaml: All features combinedsecurity_v1_networkpolicygenerator-calico-deny.yaml: Calico deny policysecurity_v1_networkpolicygenerator-calico-allow.yaml: Calico allow policysecurity_v1_networkpolicygenerator-template-web-app.yaml: Web-app policy templatesecurity_v1_networkpolicygenerator-template-database.yaml: Database policy templatesecurity_v1_networkpolicygenerator-template-backend-api.yaml: Backend API policy templatesecurity_v1_networkpolicygenerator-template-monitoring.yaml: Monitoring policy templatesecurity_v1_networkpolicygenerator-cilium-deny.yaml: Cilium deny policysecurity_v1_networkpolicygenerator-cilium-allow.yaml: Cilium allow policytest-policy.yaml: Namespace-specific policy examplestest.yaml: Test pods and services for validation
# Helm
helm uninstall npg
# kubectl
kubectl delete -f https://raw.githubusercontent.com/somaz94/network-policy-generator/main/dist/install.yaml
# From source
make undeploy
make uninstallCreates a policy that denies all traffic by default, only allowing traffic from specified namespaces:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: test-policy-generator-allow
spec:
mode: "enforcing"
policy:
type: "allow" # Default deny, explicit allow
deniedNamespaces: # Namespaces to deny access from
- "test-ns1"
- "test-ns2"
globalRules:
- type: "allow"
port: 80
protocol: TCP
direction: "ingress"
- type: "allow"
port: 443
protocol: TCP
direction: "egress"Creates a policy that allows all traffic by default, only denying traffic from specified namespaces:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: test-policy-generator-deny
spec:
mode: "enforcing"
policy:
type: "deny" # Default allow, explicit deny
allowedNamespaces: # Only these namespaces are allowed
- "test-ns1"
- "test-ns2"
globalRules:
- type: "allow"
port: 80
protocol: TCP
direction: "ingress"
- type: "allow"
port: 443
protocol: TCP
direction: "egress"Waits for a specified duration before enforcing policies:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: traffic-learner
spec:
mode: "learning"
duration: "1m" # Learning period before enforcing (use longer durations in production)
policy:
type: "deny"
allowedNamespaces:
- "test-ns1"
- "test-ns2"
deniedNamespaces:
- "test-ns3"
- "test-ns4"Apply different policies to different namespaces:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: test-policy-generator-deny
namespace: test-ns1
spec:
mode: "enforcing"
policy:
type: "deny"
allowedNamespaces:
- "test-ns3" # Only test-ns3 can access test-ns1
globalRules:
- type: "allow"
port: 80
protocol: TCP
direction: "ingress"Target specific pods by label instead of applying to the entire namespace:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: pod-selector-example
spec:
mode: "enforcing"
policy:
type: "deny"
allowedNamespaces:
- "kube-system"
podSelector:
app: nginx
tier: frontend
globalRules:
- type: "allow"
port: 80
protocol: TCP
direction: "ingress"Define egress/ingress rules for external IP ranges:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: cidr-rules-example
spec:
mode: "enforcing"
policy:
type: "deny"
allowedNamespaces:
- "kube-system"
cidrRules:
- cidr: "10.0.0.0/8"
direction: "egress"
protocol: TCP
port: 5432
- cidr: "192.168.1.0/24"
except:
- "192.168.1.100/32"
direction: "ingress"
protocol: TCP
port: 443Use service port names instead of numeric ports:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: named-port-example
spec:
mode: "enforcing"
policy:
type: "deny"
allowedNamespaces:
- "kube-system"
globalRules:
- type: "allow"
namedPort: "http"
protocol: TCP
direction: "ingress"
- type: "allow"
namedPort: "grpc"
protocol: TCP
direction: "egress"Preview generated policies without applying them:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: dry-run-example
spec:
mode: "enforcing"
dryRun: true
policy:
type: "deny"
allowedNamespaces:
- "kube-system"
globalRules:
- type: "allow"
port: 80
protocol: TCP
direction: "ingress"Generated policies are stored in .status.generatedPolicies as JSON. No NetworkPolicy resources are created.
Generate Calico-native crd.projectcalico.org/v1 NetworkPolicy resources:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: calico-deny-example
spec:
mode: "enforcing"
policyEngine: "calico"
policy:
type: "deny"
allowedNamespaces:
- "kube-system"
podSelector:
app: web
globalRules:
- type: "allow"
port: 80
protocol: TCP
direction: "ingress"Calico policies use selector-based syntax (app == 'web'), namespace selectors via projectcalico.org/name, and include automatic DNS egress allow rules.
Use built-in templates for common workload types instead of writing rules from scratch:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: template-web-app-example
spec:
mode: "enforcing"
templateName: "web-app"
policy:
type: "deny"
allowedNamespaces:
- "kube-system"Available templates:
| Template | Description |
|---|---|
zero-trust |
Deny all traffic, allow only DNS egress |
web-app |
Allow HTTP/HTTPS ingress, DNS and HTTPS egress |
backend-api |
Allow API ports (8080, 8443, 9090) ingress from allowedNamespaces, HTTPS egress |
database |
Allow DB ports (3306, 5432, 6379, 27017) ingress from allowedNamespaces, DNS-only egress |
monitoring |
Allow Prometheus scraping (9090, 9100) from allowedNamespaces, HTTPS egress |
Templates are merged with user-defined globalRules (user rules take precedence over template rules). The web-app ingress rules stay open to anywhere; the other templates' ingress rules use from: allowedNamespaces, so a database port is not exposed to every pod and external client.
Each rule sets type, port or namedPort, protocol and direction, plus an optional from:
from |
Ingress admits / egress reaches |
|---|---|
anywhere (default) |
Every source or destination, in the cluster or outside it |
allowedNamespaces |
Pods in spec.policy.allowedNamespaces, or only the policy's own namespace when that list is empty |
sameNamespace |
Pods in the policy's own namespace |
type: deny needs policyEngine: cilium or calico. Cilium writes it to ingressDeny/egressDeny, and Calico places it ahead of the allow rules; both apply it before any allow. A standard Kubernetes NetworkPolicy cannot deny, so the CRD rejects a deny rule on the kubernetes engine.
The status has fields for namespace and rule suggestions, but they stay empty until traffic collection is implemented:
apiVersion: security.policy.io/v1
kind: NetworkPolicyGenerator
metadata:
name: traffic-learner-improved
spec:
mode: "learning"
duration: "5m"
policy:
type: "deny"After the learning period, check suggestions:
# View suggested namespaces
kubectl get networkpolicygenerator traffic-learner-improved -o jsonpath='{.status.suggestedNamespaces}'
# View suggested port/protocol rules
kubectl get networkpolicygenerator traffic-learner-improved -o jsonpath='{.status.suggestedRules}'# View all NetworkPolicyGenerator resources
kubectl get networkpolicygenerator
# Get detailed information about a specific generator
kubectl describe networkpolicygenerator <name>
# Check the status
kubectl get networkpolicygenerator <name> -o yamlThe CRD enforces the following rules at admission time. They are part of the CRD schema (CEL x-kubernetes-validations), so they apply even when the optional webhook is disabled — an invalid resource is rejected by kubectl apply rather than failing later during reconciliation.
| Rule | Message |
|---|---|
spec.mode defaults to learning when omitted |
— |
spec.duration must be positive when spec.mode is learning |
spec.duration is required and must be positive when mode is 'learning' |
Each spec.globalRules[*] sets exactly one of port or namedPort |
exactly one of port or namedPort must be specified |
A namespace may not appear in both allowedNamespaces and deniedNamespaces |
a namespace cannot be listed in both allowedNamespaces and deniedNamespaces |
spec.globalRules[*].type: deny only with policyEngine cilium or calico |
globalRules of type deny need policyEngine cilium or calico; a Kubernetes NetworkPolicy can only allow |
spec.globalRules[*].from defaults to anywhere when omitted |
— |
A zero duration in learning mode is rejected because the generator would transition straight to enforcing on the next reconcile, applying policies with no learning period.
Enabling the webhook (--enable-webhooks) adds checks that the CRD schema cannot express, most notably CIDR-format validation for spec.cidrRules.
# Unit tests
make test
# Integration tests (auto-detects CNI and runs matching tests)
make test-integration # Auto-detect CNI
make test-integration ENGINE=kubernetes # Kubernetes only
make test-integration ENGINE=cilium # Cilium only
make test-integration ENGINE=calico # Calico only
make test-integration ENGINE=all # Force all engines
# Helm chart tests (lint, install, upgrade, policy tests, uninstall)
make test-helm # Auto-detect CNI
make test-helm ENGINE=kubernetes # Kubernetes only
make test-helm ENGINE=cilium # Cilium only
make test-helm ENGINE=calico # Calico only
make test-helm ENGINE=all # Force all enginesFor detailed manual test steps and sample descriptions, see Test Guide.
| Document | Description |
|---|---|
| Helm Chart | Helm chart installation, configuration, and values reference |
| Testing Guide | Unit, integration, Helm, and manual test instructions |
| Troubleshooting | Common issues and solutions |
| Version Bump | Checklist for releasing a new version |
| Contributing | How to contribute to this project |
Issues and pull requests are welcome.
- helios-lb: A bare-metal LoadBalancer for Kubernetes, a lightweight MetalLB alternative.
- k8s-namespace-sync: An operator that syncs Secrets and ConfigMaps across namespaces.
- kube-drift: An operator that runs the same manifest-vs-cluster comparison on a schedule, inside the cluster.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
