This check flags GKE clusters that allow unrestricted pod-to-pod traffic because no Kubernetes NetworkPolicy enforcement is in place. Without it, a single compromised pod can talk to every other workload in the cluster. Enable Dataplane V2 (or the network policy add-on) and apply default-deny NetworkPolicies per namespace.
By default, Kubernetes runs a flat network. Every pod can reach every other pod on every port, across every namespace, with no questions asked. That works fine in a demo, but it is a poor security posture for anything carrying real traffic. The GKE No Network Policy Configured check looks at your Google Kubernetes Engine clusters and warns you when there is no mechanism to control that pod-to-pod traffic.
This post walks through what the check detects, why an open pod network is a real risk, and how to lock it down without breaking your apps.
What this check detects
The gke_nonetworkpolicy check inspects each GKE cluster and reports a failure when network policy enforcement is not enabled. Concretely, it looks at whether the cluster has a network policy provider configured, either through:
- The legacy Network Policy add-on (Calico-based), or
- GKE Dataplane V2, which provides eBPF-based network policy enforcement natively
If neither is active, the cluster cannot enforce Kubernetes NetworkPolicy objects. You can write all the NetworkPolicy YAML you want, but without an enforcement engine in the cluster, those objects are silently ignored. That is the gap this check catches.
Note: Enabling network policy enforcement and writing NetworkPolicy resources are two separate steps. The cluster setting turns on the engine. NetworkPolicy objects are what actually restrict traffic. A cluster can pass this check by having enforcement enabled, but still allow all traffic until you apply policies.
Why it matters
The flat-network default is convenient until something goes wrong. Here is the scenario security teams worry about.
An attacker finds a vulnerability in a public-facing service, say an outdated image with a known RCE, and gets a shell inside one pod. In a cluster with no network policy, that pod can now reach:
- Your internal databases and caches that were never meant to be exposed
- The Kubernetes API server and metadata endpoints
- Admin dashboards, message queues, and service meshes in other namespaces
- Pods belonging to entirely unrelated teams sharing the cluster
This is lateral movement, and it is how a small breach becomes a large one. The initial foothold is rarely the prize. The blast radius is what hurts. A compromised marketing site pod has no business reaching the billing database, but a flat network lets it try.
There are compliance angles too. PCI DSS, SOC 2, and HIPAA all expect network segmentation between workloads handling sensitive data and everything else. "Every pod can reach every pod" is a hard finding to defend in an audit.
Warning: Shared clusters multiply this risk. If multiple teams or environments run in one cluster without policies, a bug in one team's workload becomes everyone's problem. Multi-tenancy without network policy is not real isolation.
How to fix it
There are two parts: turn on enforcement, then apply policies. Dataplane V2 is the recommended path for new and existing clusters because it is faster, uses eBPF instead of iptables, and includes network policy logging.
Option 1: Enable Dataplane V2 (recommended)
For a new cluster, enable it at creation time:
gcloud container clusters create my-cluster \
--enable-dataplane-v2 \
--enable-ip-alias \
--region=us-central1
Danger: You cannot switch an existing cluster between the legacy dataplane and Dataplane V2 in place. Migrating requires recreating the cluster or creating a new node pool and draining workloads over. Plan this as a maintenance event, not a quick toggle, and test in staging first.
Option 2: Enable the legacy Network Policy add-on
If you are not ready to migrate to Dataplane V2, you can enable the Calico-based add-on on an existing cluster:
gcloud container clusters update my-cluster \
--update-addons=NetworkPolicy=ENABLED \
--region=us-central1
# Then enable enforcement on the cluster
gcloud container clusters update my-cluster \
--enable-network-policy \
--region=us-central1
Warning: Enabling the legacy add-on triggers a rolling recreation of your nodes and adds a Calico agent to each one, consuming some CPU and memory per node. Schedule it during a low-traffic window and confirm you have enough headroom in your node pools.
Step 3: Apply a default-deny policy
Enforcement is on, but traffic is still wide open until you write policies. Start with a default-deny for ingress in each namespace, then explicitly allow what each workload needs.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
An empty podSelector matches every pod in the namespace. With no ingress rules listed, all incoming pod traffic is denied. Now add targeted allow rules. For example, letting only the frontend reach the API:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Tip: Do not forget DNS. A default-deny egress policy will break name resolution because pods can no longer reach kube-dns. Always pair egress restrictions with an explicit allow rule for UDP and TCP port 53 to the kube-system namespace.
Step 4: Verify
Confirm enforcement is active and policies are loaded:
# Check cluster config
gcloud container clusters describe my-cluster \
--region=us-central1 \
--format="value(networkPolicy.enabled, networkConfig.datapathProvider)"
# List policies in a namespace
kubectl get networkpolicy -n production
Then test from inside a pod that something which should be blocked actually is:
kubectl run test --rm -it --image=nicolaka/netshoot -n production -- \
curl --max-time 5 http://some-blocked-service:8080
How to prevent it from happening again
Fixing one cluster by hand is fine. Making sure the next cluster ships correctly is what keeps the finding from coming back.
Codify it in Terraform
Bake Dataplane V2 into your cluster module so no cluster can be provisioned without it:
resource "google_container_cluster" "primary" {
name = "my-cluster"
location = "us-central1"
datapath_provider = "ADVANCED_DATAPATH" # Dataplane V2
ip_allocation_policy {}
}
Note: In the Terraform Google provider, setting datapath_provider = "ADVANCED_DATAPATH" enables Dataplane V2. You do not need the separate network_policy block when using it, since enforcement is built in. The legacy add-on uses the network_policy block instead.
Gate it in CI/CD with policy-as-code
Stop misconfigured clusters before they merge. A Conftest or OPA policy can fail a plan that defines a cluster without an enforcement provider:
# Run in your pipeline against the Terraform plan JSON
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json --policy ./policies
Inside the cluster, enforce that every new namespace gets a default-deny policy. A Gatekeeper or Kyverno policy can require it, or you can use a namespace bootstrap controller that applies the baseline policy automatically when a namespace is created.
Keep it visible
Configuration drifts. Someone spins up a quick cluster for a project, forgets the flag, and it never gets cleaned up. Continuous scanning with Lensix catches these clusters as they appear instead of at audit time, so the gap between "created" and "noticed" stays small.
Best practices
- Default to deny. Start each namespace with a default-deny ingress (and ideally egress) policy, then open only the paths a workload genuinely needs. Allow-listing scales better than chasing down what to block.
- Prefer Dataplane V2. It is the strategic direction for GKE, performs better at scale, and gives you network policy logging so you can see what is being allowed and denied.
- Label workloads consistently. NetworkPolicies select pods by label. A clean, consistent labeling scheme (app, tier, team) makes policies readable and maintainable. Inconsistent labels lead to policies that silently match nothing.
- Always allow DNS in egress policies. The most common self-inflicted outage from network policy is breaking kube-dns. Build a reusable allow-DNS policy and apply it everywhere.
- Test policies in staging. Apply, then verify both that blocked paths fail and allowed paths still work. A policy that is too tight breaks production; one that is too loose defeats the point.
- Combine with other controls. Network policy is one layer. Pair it with workload identity, private clusters, and least-privilege RBAC so a single misconfiguration is never the only thing standing between an attacker and your data.
An open pod network is the kind of risk that costs nothing to ignore right up until the day it costs everything. Enabling enforcement and shipping a default-deny baseline is a small amount of work that meaningfully shrinks your blast radius. Do it once in your cluster module, gate it in CI, and you rarely have to think about it again.

