Illustrative engineer at a laptop beside Kubernetes policy teaching panels and lab racks.

WIUNIX ACADEMY

Kubernetes NetworkPolicy: prove default deny, DNS and one allowed path

Build a disposable labeled namespace, deny traffic, allow explicit UDP/TCP DNS and one client-to-API flow, then test allowed and forbidden peers.

Advanced45 min readUpdated
Make this guide yours

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Rocky Linux 9Reviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment4 editable values

THE OUTCOME

What will you have at the end?

Demonstrate actual CNI enforcement and the additive ingress/egress policy model in a disposable lab.

Supported operating systems and versions

Documented disposable lab. No cluster policy was applied during authoring; CNI-specific enforcement remains a required reader test. The current tutorial form accepts simple identifiers of at most32characters. This is a form limitation, not the Kubernetes resource-name limit. Never truncate or rename a live object to fit. For a longer existing resource/context name, follow this same diagnostic sequence in the operator-owned direct CLI/editor workflow with its exact original name; no silent default substitution.

Before you begin

  • Cluster-owner authorization and a CNI with documented NetworkPolicy enforcement
  • New disposable namespace; no production workloads
  • Approved server/tool images preserving the sample command behavior; kubectl permissions to create/delete lab resources
  • The current tutorial form accepts simple identifiers of at most32characters. This is a form limitation, not the Kubernetes resource-name limit. Never truncate or rename a live object to fit. For a longer existing resource/context name, follow this same diagnostic sequence in the operator-owned direct CLI/editor workflow with its exact original name; no silent default substitution.
01

LET’S BUILD

Prepare the selected operator host

These are operator-host packages only. Review the transaction before approving; do not install tools inside application containers. The cluster, server and application binaries must already exist with owner-approved versions. Use Bash for the following panels. Package installation changes this selected host but starts no new application server.

Use an existing Linux kubectl within one minor version of every API-server version; inspect the actual server version in the authorized context before proceeding. Do not install kubelet/kubeadm or overwrite cluster client repositories for this diagnosis.

bash

Selected operator host; approved package transaction

sudo dnf install ca-certificates bash coreutils
if ! command -v curl >/dev/null 2>&1; then sudo dnf install curl; fi
cat /etc/os-release
command -v bash curl timeout
command -v kubectl
kubectl version --client --output=yaml
How do I know it worked?

The OS/version matches the selection, required tools resolve, and existing application/client versions satisfy the stated compatibility boundary. Stop if a binary is missing or incompatible.

Preserve host security and existing paths

Keep SELinux enforcing and firewalld rules in place. These client-side diagnostics require outbound access to the approved API/TLS/SFTP destination; they need no new inbound host port. A failure in a confined systemd service may differ from an interactive shell. Review AVC denials, file labels and service permissions with the policy owner rather than disabling SELinux, broad allow rules or disabling the firewall. Trust-chain/crypto-policy failures require a compatible endpoint, not weakened system crypto. Existing custom data/config paths need their established labels; never run a recursive relabel blindly.

bash

Selected operator host; read-only security inspection

if command -v getenforce >/dev/null 2>&1; then getenforce; fi
if command -v firewall-cmd >/dev/null 2>&1; then firewall-cmd --state; fi

A policy object is not proof of enforcement

The cluster CNI must implement NetworkPolicy. An accepted API object on an unsupported CNI can leave traffic open. Standard policies are additive: a broad allow in another policy can permit the same traffic. Both source egress and destination ingress must allow a connection when both endpoints are isolated.

Run only in a new disposable namespace, never add default deny to an existing production namespace as a learning exercise. This lab uses ordinary non-hostNetwork Pods, TCP HTTP and CoreDNS Pod selectors; NodeLocal DNSCache and provider-specific policy layers require a different DNS rule.

02

LET’S BUILD

Check context, CNI and name absence

Confirm the CNI documentation and cluster-owner approval before proceeding. The absence check must show NotFound; any existing namespace means choose another reviewed name. Do not continue after Forbidden or a connection error. Use an already approved digest-pinned HTTP server and tool image in the manifest, reviewed by the cluster owner; the readable tags below are lab examples, not supply-chain acceptance.

bash

Operator terminal; authorized lab context

kubectl config get-contexts 'lab'
kubectl --context='lab' get namespace 'academy-lab'
kubectl --context='lab' -n kube-system get pods -l k8s-app=kube-dns --show-labels
kubectl --context='lab' auth can-i create namespaces
How do I know it worked?

An authorized context, enforcing CNI, CoreDNS labels and absent lab namespace are confirmed.

03

LET’S BUILD

Create only the new lab manifest

Use a new working directory; exclusive creation prevents overwriting an existing file. The next YAML is file content. Review it before submitting to the API. Resource requests/limits keep this small fixture bounded but do not prove image safety.

bash

Operator terminal; authorized lab context

umask 077
mkdir '/tmp/academy-netpol' && cd '/tmp/academy-netpol' || exit 1
(set -C; : > lab.yaml) || exit 1
${EDITOR:-vi} lab.yaml
How do I know it worked?

A new private directory and empty lab.yaml exist for the reviewed file content.

04

LET’S BUILD

Define server, allowed client and forbidden peer

All Pods live in the new namespace. The server and tools are separate roles. Replace image references with your approved pinned equivalents before applying; preserve their port/command behavior. The BusyBox shell sleeps so each connectivity test starts a fresh connection.

yaml · /tmp/academy-netpol/lab.yaml

File contents; save at the displayed filename

apiVersion: v1
kind: Pod
metadata:
  name: api
  namespace: academy-lab
  labels: {app: api}
spec:
  containers:
  - name: web
    image: registry.k8s.io/e2e-test-images/agnhost:2.53
    args: [netexec, --http-port=8080]
    ports: [{containerPort: 8080}]
    resources:
      requests: {cpu: 10m, memory: 32Mi}
      limits: {cpu: 200m, memory: 128Mi}
---
apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: academy-lab
spec:
  selector: {app: api}
  ports: [{port: 8080, targetPort: 8080}]
---
apiVersion: v1
kind: Pod
metadata:
  name: client
  namespace: academy-lab
  labels: {role: client}
spec:
  containers:
  - name: tools
    image: busybox:1.37
    command: [sh, -c, 'sleep 86400']
    resources:
      requests: {cpu: 5m, memory: 8Mi}
      limits: {cpu: 100m, memory: 32Mi}
---
apiVersion: v1
kind: Pod
metadata:
  name: stranger
  namespace: academy-lab
  labels: {role: stranger}
spec:
  containers:
  - name: tools
    image: busybox:1.37
    command: [sh, -c, 'sleep 86400']
    resources:
      requests: {cpu: 5m, memory: 8Mi}
      limits: {cpu: 100m, memory: 32Mi}
How do I know it worked?

Only the named disposable namespace and four lab resources are declared.

Explore all tutorials ↗