← Technical library
architecturesecuritydraft

Kubernetes NetworkPolicy per Tenant: Building a Default-Deny Release Boundary

Chart
Kubernetes NetworkPolicy
Intent
mofu
Author
OpenTenant

How to generate default-deny NetworkPolicy for every tenant release while preserving only the DNS, secrets, observability, and edge paths each workload actually needs.

Default-deny policy graph

Every allowed connection is compiled, not assumed.

OpenTenant starts from isolation, then restores only the DNS, secrets, edge, and application paths declared by the release.

Baseline

Deny ingress + egress

Every selected tenant pod begins isolated

Cluster service

DNS resolution

TCP and UDP 53 reach the discovered resolver

Secret plane

Vault delivery

Only the chosen operator or agent path is opened

Application graph

Declared dependencies

Component labels and ports define east-west traffic

Edge

Tenant ingress

Gateway or tunnel reaches the target service only

CNI enforcement → negative testtenant A ↛ tenant Bpolicy change → release verification

Kubernetes NetworkPolicy defaults for tenant releases

Direct answer

Kubernetes pods are non-isolated by default. A tenant namespace does not become a network boundary until an enforcing CNI applies NetworkPolicy resources that select its pods. OpenTenant automates the safer pattern: every tenant release starts with default-deny ingress and egress, then receives narrow allow rules compiled from the services, secret delivery, DNS, observability, and edge routes declared for that tenant.

The result is not “a NetworkPolicy.” It is a reproducible policy set tied to one release and tested against the connections that release requires.

What Kubernetes NetworkPolicy controls

Kubernetes NetworkPolicy controls layer 3 and layer 4 traffic involving pods. Rules select peers using pod labels, namespace labels, and IP blocks, then constrain ports and protocols.

Two prerequisites are easy to miss:

  1. The cluster must use a network plugin that enforces NetworkPolicy. Creating resources without an enforcing implementation has no effect.
  2. Ingress and egress isolation are independent. A pod becomes isolated in a direction only when a policy selects it for that direction.

Policies are additive rather than ordered. If several policies select a pod, the allowed traffic is the union of their rules. For pod-to-pod traffic, the source egress policy and destination ingress policy must both allow the connection.

Start each tenant from default deny

Kubernetes' multi-tenancy guidance recommends a default policy that denies pod communication, followed by explicit DNS and workload rules. A baseline for a tenant namespace can select every pod and isolate both directions:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: tenant-acme
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

This is intentionally disruptive. Existing charts often assume unrestricted east-west and outbound traffic. That is why OpenTenant treats policy as part of release compilation, not a cluster-wide switch applied after production traffic exists.

Restore DNS deliberately

Default-deny egress can prevent name resolution. The tenant policy set must allow UDP and TCP DNS traffic to the actual DNS pods or service used by the cluster.

Avoid copying an environment-specific label selector blindly. CoreDNS labels and namespaces vary. OpenTenant deployment profiles should discover or declare the DNS target, render the matching rule, and verify resolution from a tenant pod.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: tenant-acme
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - { protocol: UDP, port: 53 }
        - { protocol: TCP, port: 53 }

Compile the release graph into allow rules

A tenant chart typically needs a small number of connection classes.

Edge ingress

Allow traffic from the Gateway, ingress controller, or cloudflared connector to only the tenant service ports. A broad namespace allow would let the edge plane reach every pod; select the actual workload labels instead.

Intra-release traffic

Allow frontends to APIs, APIs to databases, and workers to queues based on component labels and ports. Keep each edge directional. “Everything in the namespace may talk to everything” is simpler, but it removes much of the value of policy.

Secret delivery

Vault Agent sidecars, CSI providers, Vault Secrets Operator, and External Secrets Operator have different network paths. An operator may fetch secrets from its own namespace and write Kubernetes Secrets through the API server, while an injected agent connects from each application pod to Vault. The policy must match the selected delivery model.

Observability

Declare whether metrics are scraped inbound, logs are pushed outbound, or telemetry uses an in-cluster collector. Do not open all egress because the application “needs monitoring.”

Approved external dependencies

Kubernetes NetworkPolicy uses IP blocks for external networks; it does not natively express arbitrary DNS names. SaaS endpoints with changing IP ranges may require a CNI extension, egress gateway, proxy, or another policy layer. OpenTenant should mark unsupported hostname-only dependencies instead of pretending a static CIDR is durable.

Label integrity is part of the boundary

Policies trust labels. If a tenant-controlled workload can add the label used by an edge connector or privileged service, it may gain access that the author did not intend.

OpenTenant should reserve platform labels, render them from the trusted tenant identity, and use admission policy to stop tenant workloads from claiming protected identities. Namespace labels used by namespaceSelector need the same treatment.

Be especially careful with {} as a namespaceSelector: Kubernetes multi-tenancy guidance warns against broad empty namespace selectors where cross-namespace traffic is allowed.

What NetworkPolicy does not solve

NetworkPolicy is not a complete tenant security boundary.

  • It does not authorize Kubernetes API operations; use RBAC.
  • It does not limit CPU, memory, or object counts; use quotas and limits.
  • It does not encrypt all traffic; use workload TLS or a service mesh when required.
  • It does not isolate the shared kernel; use runtime hardening, sandboxing, dedicated nodes, or dedicated clusters for stronger threat models.
  • It does not protect storage by itself.

OpenTenant can select a stronger deployment profile when namespace-level isolation is insufficient. Automation should make the trade-off explicit, not hide it.

How OpenTenant automates policy safely

tenant manifest
  → identify release components and ports
  → apply trusted tenant and component labels
  → render default-deny ingress and egress
  → add DNS and declared dependency rules
  → add edge, secrets, and observability paths
  → install chart and policy set together
  → run positive and negative connectivity checks

Every generated policy is owned by the tenant release and carries the source revision and configuration hash. Upgrades reconcile the policy set alongside the Helm release. Removing a dependency removes its allow rule only after readiness checks prove the new release no longer needs it.

Verification checklist

  • Confirm the CNI enforces NetworkPolicy with a negative test.
  • Verify default-deny covers every tenant pod, including chart hooks and jobs.
  • Confirm DNS works over UDP and TCP.
  • Test every declared application connection in both directions.
  • Prove a pod in tenant A cannot reach tenant B's service, database, or metrics endpoint.
  • Verify protected labels cannot be self-assigned.
  • Test Vault or external-secret delivery under the same policies.
  • Confirm edge connectors can reach only declared tenant services.
  • Record policy failures as release failures, not post-deployment warnings.

Sources

OpenTenant turns these controls into a default attached to every tenant lifecycle, while keeping the generated rules visible and reviewable.

From chart installation to tenant operations.

See how OpenTenant models release, secrets, domain, and lifecycle boundaries.

Inspect Architecture