Join our Newsletter — 33% off our NHI Course
Home› Guides› Kubernetes Network Policies vs Service Mesh Policies: Choosing…
Guide Cloud Security

Kubernetes Network Policies vs Service Mesh Policies: Choosing and Layering the Right Enforcement Point

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 14 November 2026 17 min read
On this page

Platform teams often treat Kubernetes NetworkPolicy and service mesh authorisation as interchangeable ways to "segment the cluster", and end up with one layer doing a job it was never built for. NetworkPolicy is a layer 3 and 4 allow list keyed on pod labels, namespaces and IP ranges, enforced by the network plugin in the kernel data path. A service mesh policy is evaluated by a proxy that knows the caller's cryptographic workload identity and, at layer 7, the HTTP method and path. This guide compares them as enforcement points and shows how to layer both. How to write NetworkPolicy objects is covered in the Kubernetes Network Policy Guide, and mesh hardening in the Service Mesh Security Guide; this page is about choosing and combining them.

Key takeaways

  • Different questions, different answers. NetworkPolicy asks "may a pod with these labels reach this port?"; mesh policy asks "may the workload holding this certificate call this method on this path?".
  • The mesh cannot police a pod that escapes its proxy. Istio states that sidecar capture covers TCP only and that an application sharing the pod can remove redirection rules, so network policy is the layer that still holds after a pod is compromised.
  • Labels are not identity. NetworkPolicy trusts whoever can set labels and IP addresses; mesh policy trusts the certificate authority and service account bindings. Protect both trust roots, not just the policy objects.
  • Both layers start open. Pods are non-isolated until a NetworkPolicy selects them, Istio allows requests when no ALLOW policy exists and runs PERMISSIVE mTLS by default, and Linkerd's default inbound policy is all-unauthenticated.
  • Layer them on purpose. Coarse namespace and port isolation plus egress control in NetworkPolicy, identity and request authorisation in the mesh, and a written rule for which layer owns each decision.

The short answer

Use NetworkPolicy everywhere, because it is cheap, built into the API, enforced below the application and still effective when a workload is fully compromised. Add service mesh policy where you need to know who is calling rather than where the packet came from: regulated data flows, multi-tenant platforms, services that expose administrative endpoints alongside public ones, and any environment where traffic must be encrypted and mutually authenticated inside the cluster. The Kubernetes documentation itself lists "anything TLS related" and "forcing internal cluster traffic to go through a common gateway" as things NetworkPolicy cannot do and points to service meshes for them. Istio's security best practices make the reverse point: layer Kubernetes NetworkPolicies under Istio policy to limit the damage from a compromised pod. Neither project presents its layer as a replacement for the other.

Side by side

AttributeKubernetes NetworkPolicyService mesh policy (for example Istio AuthorizationPolicy, Linkerd AuthorizationPolicy)
OSI layerLayer 3 and 4: IP, port, protocol (TCP, UDP, SCTP where supported)Layer 4 identity (peer certificate) and layer 7 attributes (HTTP method, path, headers, JWT claims)
Who the caller isA pod matched by labels, a namespace matched by labels, or an ipBlock CIDRA workload identity bound to a Kubernetes service account, carried in an mTLS certificate (Istio principals take the form trust-domain/ns/namespace/sa/account)
Enforcement pointCNI plugin in the node data path (iptables, nftables, eBPF, OVS)Proxy: per-pod sidecar, per-node ztunnel (Istio ambient, layer 4 only) or a waypoint proxy (Istio ambient, layer 7)
DefaultAll traffic allowed until a policy selects the pod for a directionIstio: allow when no ALLOW policy exists, PERMISSIVE mTLS when mode is unset; Linkerd: all-unauthenticated by default
Deny rulesNone in v1; allows are additive. Cluster tiers and deny come from ClusterNetworkPolicy (beta CRD) or CNI-specific policyIstio has CUSTOM, DENY, ALLOW and AUDIT actions with a fixed evaluation order; Linkerd denies once a Server exists unless authorised
EncryptionNonemTLS between proxies
LoggingNone in the API; depends on the plugin (flow logs, policy verdicts)Proxy access logs, metrics with source and destination identity, policy denials returned as HTTP 403 or connection resets
Non-TCP trafficUDP and SCTP rules; ICMP behaviour is plugin specificIstio sidecar capture covers TCP only; UDP and ICMP are not redirected
Survives pod compromiseYes: enforced outside the pod's network namespace controlSidecar: not for the compromised pod's own outbound traffic; still protects other pods' inbound side

Threat model: what each layer stops

The table assumes an attacker with code execution in one application pod.

Attacker moveNetworkPolicyMesh policy
Scan the cluster for open services (ATT&CK T1046 Network Service Discovery)Strong: default deny egress means probes to unlisted pods and ports fail in the kernelPartial: unauthorised requests are refused, but the TCP handshake to the proxy may still reveal that a service exists
Exploit a neighbouring service (T1210 Exploitation of Remote Services)Strong where the flow is not needed; no help where the flow is legitimateStronger for legitimate flows: an ALLOW rule can limit the caller to GET on /api/v1/orders, so an exploit that needs an admin path is refused
Intercept or tamper with traffic on the node or pod network (T1040 Network Sniffing, T1557 Adversary-in-the-Middle)None: traffic is not encryptedStrong once mTLS is STRICT
Impersonate a trusted caller by IP or labelWeak: anyone who can create a pod with matching labels, or reuse an IP, is trustedStrong: the caller needs the private key for the identity, issued only to pods running as that service account
Bypass the proxy from inside the compromised podNot affected: enforcement is in the node data pathThe sidecar of the compromised pod can be bypassed; the destination's proxy still enforces inbound policy
Exfiltrate over DNS or to the internet (T1071.004 DNS)Strong with egress rules that allow only cluster DNS and named CIDRs; FQDN rules need CNI extensionsWeak on its own: Istio says outboundTrafficPolicy REGISTRY_ONLY is best effort, not a security control
Steal the service account token and call the API server (T1552.007 Container API)Strong: deny egress to the API server for pods that never need itIndirect: the API server is usually outside the mesh

NetworkPolicy is the containment layer that limits blast radius after compromise; mesh policy is the precision layer that proves who is calling and limits what legitimate callers may do.

Kubernetes NetworkPolicy as an enforcement point

Architecture in words

A NetworkPolicy object is stored by the API server and read by the CNI plugin's agent on every node, which programs packet filters for the pods on that node. The policy names pods by label selector, and the plugin translates those selectors into the current set of pod IP addresses. When a pod starts, stops or changes labels, the plugin recalculates. No proxy is involved, so there is no per-request latency. The API server accepts policies the plugin ignores, so prove enforcement.

Failure modes

  • Label trust: a namespace owner who can label pods or namespaces can change what a policy matches. Selectors on team-applied labels are only as strong as the RBAC on those labels.
  • Additive allows: v1 has no deny and no priority, so one broad allow policy, perhaps from a Helm chart, silently overrides careful narrow ones.
  • Existing connections: the Kubernetes documentation states that whether a policy change affects an existing connection is implementation defined, so a long-lived connection may outlive the rule that should block it.
  • No evidence: the API logs nothing; without plugin flow logs you cannot show an auditor that a flow was blocked.

Select namespaces by the immutable kubernetes.io/metadata.name label, put non-overridable rules in cluster tiers or CNI global policy, and reject namespaces without default deny at admission (see the Kubernetes Admission Control Guide).

Service mesh policy as an enforcement point

Architecture in words

The mesh control plane issues each workload a short-lived certificate whose identity is derived from its Kubernetes service account, and distributes policy to proxies. In sidecar mode, traffic in and out of the pod is redirected to a proxy container in the same pod; the destination proxy terminates mTLS, reads the peer identity from the certificate, evaluates authorisation policy and forwards the request to the application over loopback. In Istio ambient mode the work is split: a per-node ztunnel handles mTLS and layer 4 authorisation, and an optional waypoint proxy handles layer 7. NIST SP 800-204A and SP 800-204B describe this proxy model and attribute-based access control built on it.

How the main policy models evaluate

  • Istio AuthorizationPolicy: CUSTOM policies are evaluated first, then DENY, then ALLOW. If no ALLOW policy applies to a workload the request is allowed; once any ALLOW policy applies, unmatched requests are denied. An ALLOW policy with no rules denies everything, which is the standard default-deny pattern. The principals and namespaces fields require mTLS because they come from the peer certificate.
  • Istio PeerAuthentication: PERMISSIVE accepts mTLS and plaintext, STRICT accepts only mTLS, DISABLE turns mTLS off. A mesh-wide policy with an unset mode is PERMISSIVE.
  • Linkerd: a Server resource selects a port on a set of pods, and once it exists traffic to that port is denied unless an AuthorizationPolicy grants it. AuthorizationPolicy targets a Server, an HTTPRoute or a namespace and requires a MeshTLSAuthentication (mesh identities or service accounts) or a NetworkAuthentication (CIDRs). The default inbound policy (all-unauthenticated unless changed; other values include cluster-authenticated, deny and audit) can be overridden with the config.linkerd.io/default-inbound-policy annotation.

Failure modes

  • Proxy bypass from inside the pod: Istio documents that capture is TCP only, that some ports are excluded from inbound capture, and that an application in the pod can remove redirection rules or replace the proxy. The security boundary Istio relies on is that a client cannot bypass another pod's proxy.
  • Layer 7 rules where no layer 7 proxy runs: in Istio ambient, ztunnel skips layer 7 rules it cannot evaluate. An ALLOW policy with only HTTP attributes then denies all traffic, while a DENY policy with only HTTP attributes allows all traffic. A DENY rule that mixes layer 4 and layer 7 attributes in one entry is enforced on its layer 4 attributes alone, which denies more than intended.
  • Negative matching: Istio warns that missing attributes count as matches in DENY rules and recommends ALLOW with positive matching or DENY with negative matching, never a mix.
  • Path normalisation gaps: if the proxy and the backend normalise paths differently, a rule for /data/secret may not match /data//secret. Istio's options run from NONE through BASE (the default) and MERGE_SLASHES to DECODE_AND_MERGE_SLASHES.
  • Permissive leftovers: PERMISSIVE mode kept after migration means a non-mesh client can still connect in plaintext, and identity-based ALLOW rules do not match it.

Where identity fits

The real difference between the two layers is what each treats as identity. NetworkPolicy treats labels and addresses as identity, so its trust root is Kubernetes RBAC on pods, labels and namespaces. Mesh policy treats the service account as identity, so its trust root is the certificate authority plus RBAC on who can create pods that run as a given service account. The SPIFFE and SPIRE Guide covers the identity format meshes use, and the Kubernetes NHI Security Guide covers service account hygiene.

Layering both without gaps

When both layers run, write down which layer owns each decision. The split below is the one most platforms converge on; it follows Istio's advice to pair an egress gateway with NetworkPolicy so sidecar bypass does not open egress.

DecisionOwning layerBackstop
Namespace isolation and default denyNetworkPolicy (Baseline tier or per namespace)Mesh default deny
Which ports a pod exposes at allNetworkPolicyLinkerd Server or Istio port-scoped rules
Which service may call which serviceMesh policy by service accountNetworkPolicy allow by namespace and port
Which methods and paths a caller may useMesh layer 7 policy (sidecar or waypoint)Application authorisation
Encryption in transit inside the clusterMesh mTLS STRICTNone at layer 3 and 4
Egress to the internet and SaaSNetworkPolicy plus an egress gateway or FQDN-aware CNI policyMesh egress gateway policy
Access to the API server and cloud metadata endpointNetworkPolicy (or Admin tier deny)None

Two rules keep the layers consistent. First, when the mesh adds ports (for example, ambient mode tunnels traffic between ztunnels, and sidecars talk to the control plane), add those ports to NetworkPolicy explicitly rather than opening namespaces wide; read your mesh version's port list rather than copying one from a blog. Second, do not let NetworkPolicy become a copy of mesh policy. Keep it coarse (namespace, port, egress) and keep fine-grained rules in the mesh, where denials carry an identity.

Some CNI plugins blur the boundary: Cilium policy covers layer 3 selectors, layer 4 ports and TLS SNI, and layer 7 HTTP and DNS rules. That helps with egress FQDN control, but the caller is still identified by labels, so it does not replace mTLS where identity must be proved.

Decision criteria

  • Single team, small cluster, no in-cluster encryption requirement: NetworkPolicy with default deny and tight egress; add a mesh when scale or a regulator demands it.
  • Regulated data in transit inside the cluster (card data, health data, cross-tenant traffic): mesh mTLS STRICT is the practical way to get encryption and mutual authentication at scale; keep NetworkPolicy for containment.
  • Multi-tenant platform: platform-owned cluster tiers in NetworkPolicy so tenants cannot open cross-tenant paths, and mesh policy per tenant namespace so tenants manage their own service-to-service rules.
  • Services with mixed sensitivity endpoints (a public read API and an admin API on the same port): only layer 7 policy can separate them; NetworkPolicy sees one port.
  • Limited platform staff: if nobody can own a mesh's certificate authority and upgrades, a well-run NetworkPolicy estate is safer than a mesh left in PERMISSIVE mode.

Worked scenario: a payments namespace on a shared cluster

A shared cluster hosts a checkout front end, a payments API, a fraud scoring service and a ledger database in the payments namespace, plus thirty other teams' namespaces. Requirements: entry only through the ingress gateway, only checkout may POST /v1/charges, fraud may read charges, encryption inside the namespace, and internet egress only to the card network.

  1. NetworkPolicy baseline: default deny ingress and egress in payments; allow DNS to cluster DNS only; allow ingress from the ingress gateway namespace on the checkout port; allow egress from payments pods to the ledger on its database port; deny the metadata endpoint and API server through a platform tier rule.
  2. Egress: allow egress only to the egress gateway namespace, and let the egress gateway alone reach the card network CIDR or FQDN. A compromised payments pod that removes its sidecar redirection still cannot reach the internet, because NetworkPolicy blocks everything except the gateway.
  3. Mesh identity: separate service accounts for checkout, payments, fraud and ledger clients; STRICT mTLS on the namespace once all clients are meshed.
  4. Mesh authorisation: an empty ALLOW policy as namespace default deny; an ALLOW rule letting the checkout principal POST to /v1/charges on payments; an ALLOW rule letting the fraud principal GET /v1/charges/*; path normalisation set to match the payments framework and tested with doubled slashes and encoded characters.
  5. Result: a compromised fraud pod can reach payments on its port (NetworkPolicy allows the namespace flow) but receives 403 on POST; it cannot reach the internet or the API server; and its traffic is visible with its identity in the payments proxy logs.

How to implement

  1. NetworkPolicy first: map flows from CNI telemetry, then roll out default deny and egress controls namespace by namespace using staged or audit modes, as described in the Kubernetes Network Policy Guide.
  2. Introduce the mesh in PERMISSIVE mode: mesh one namespace, confirm every client has a mesh identity in telemetry, then switch it to STRICT. Track plaintext connections until they reach zero.
  3. Add mesh default deny with audit: run AUDIT policies or Linkerd audit mode for a full business cycle, then apply default deny and explicit service account allows.
  4. Add layer 7 rules only where they matter: admin endpoints, write operations on sensitive data, cross-tenant calls. Confirm a layer 7 proxy (sidecar or waypoint) serves the destination before applying them.
  5. Rollback: keep both policy sets in Git. For NetworkPolicy, an expiring allow policy restores traffic because rules are additive. For Istio, removing the default-deny ALLOW policy restores allow-by-default; for mTLS, return the namespace to PERMISSIVE. Istio notes that policy changes may not affect existing connections, so restart proxies after a change to make new connections follow the new rules.

The Service mesh rollout playbook covers the wider mesh adoption steps.

Detection and measurement

Monitor both policy planes and both data planes. In the Kubernetes audit log, alert on changes to any network or mesh policy object, on namespace label changes, and on new RoleBindings that let a subject create pods as a sensitive service account (see the Kubernetes Audit Log Detection Guide). In the data planes, alert on CNI drops to the API server and metadata endpoint, on mesh 403s from identities that have never been denied before, and on plaintext connections to STRICT namespaces.

  • Default deny coverage: percentage of application namespaces with NetworkPolicy default deny in both directions and mesh default deny.
  • mTLS coverage: percentage of in-cluster connections that are mutually authenticated, and count of namespaces still PERMISSIVE.
  • Layer 7 rule placement: count of HTTP-attribute policies attached to workloads with no layer 7 proxy (target zero).
  • Egress openness: number of NetworkPolicies allowing 0.0.0.0/0 egress, trending down.

Framework mapping

FrameworkReferenceNetworkPolicyMesh policy
NIST SP 800-53 Rev 5AC-4 Information Flow Enforcement; SC-7 Boundary ProtectionPrimarySupporting
NIST SP 800-53 Rev 5IA-9 Service Identification and Authentication; SC-8 Transmission Confidentiality and Integrity; AC-3 Access EnforcementNonePrimary
NIST CSF 2.0PR.IR-01 networks protected from unauthorised logical access; PR.AA-03 services authenticated; PR.DS-02 data in transit protectedPR.IR-01PR.AA-03, PR.DS-02, PR.IR-01
CIS Controls v8.113.4 traffic filtering between network segments; 3.10 encrypt sensitive data in transit13.43.10, 13.4
ISO/IEC 27001:2022 Annex AA.8.20 Networks security; A.8.22 Segregation of networks; A.8.24 Use of cryptographyA.8.20, A.8.22A.8.22, A.8.24
MITRE D3FENDD3-ITF Inbound Traffic Filtering; D3-NTF Network Traffic Filtering; D3-MAN Message AuthenticationD3-ITF, D3-NTFD3-MAN

Common failure patterns

  • Mesh as the only segmentation: no NetworkPolicy because "the mesh handles it", so a compromised pod bypasses its own sidecar and scans or exfiltrates freely.
  • Shared service accounts: several applications run as one service account, so mesh policy cannot tell them apart and every allow is wider than intended.
  • Duplicated fine-grained rules: the same service-to-service list maintained in NetworkPolicy and mesh policy, which drifts until one layer blocks what the other allows and outages are blamed on "the mesh".

Practitioner checklist

  • Prove the CNI enforces NetworkPolicy, and run default deny ingress and egress in every application namespace.
  • Block the API server and cloud metadata endpoint with NetworkPolicy for pods that do not need them.
  • Force egress through an egress gateway or FQDN-aware CNI policy, backed by NetworkPolicy so sidecar bypass does not help.
  • Give every workload its own service account and restrict who can create pods as sensitive ones.
  • Move each meshed namespace from PERMISSIVE to STRICT mTLS and track plaintext connections to zero.
  • Apply mesh default deny, then allow by service account; use ALLOW with positive matching and DENY with negative matching only.
  • Attach layer 7 rules only where a sidecar or waypoint serves the destination; set path normalisation to match the backend.
  • Alert on changes to both policy sets and on namespace label and service account binding changes.

Standards and references

Related NHI Mgmt Group resources: Kubernetes Network Policy Guide, Service Mesh Security Guide, Kubernetes Ingress Controller vs Service Mesh, mTLS Guide, Cloud Workload Identity Guide.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 14 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org