← Start of tutorial

WIUNIX ACADEMY

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

Page 2 of 3. Review the earlier steps and prerequisites before continuing.

YOUR SYSTEM. YOUR STEPS.

Where are you working?

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

Red Hat Enterprise 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
05

LET’S BUILD

Apply the fixture and establish a healthy baseline

This creates lab resources. Before any policy, both clients should resolve and reach the server. If either fails, fix the fixture or existing cluster policy first; otherwise a later timeout cannot demonstrate your new policy. The namespace may inherit clusterwide policies, admission rules or quotas even though it is new.

bash

Operator terminal; authorized lab context

cd '/tmp/academy-netpol' || exit 1
kubectl --context='lab' create namespace 'academy-lab' || exit 1
kubectl --context='lab' label namespace 'academy-lab' academy.wiunix.com/lab=networkpolicy || exit 1
kubectl --context='lab' apply --dry-run=server -f lab.yaml || exit 1
kubectl --context='lab' create -f lab.yaml || exit 1
kubectl --context='lab' -n 'academy-lab' wait --for=condition=Ready pod --all --timeout=180s
kubectl --context='lab' -n 'academy-lab' exec client -- wget -Y off -T 3 -qO- http://api:8080/hostname
kubectl --context='lab' -n 'academy-lab' exec stranger -- wget -Y off -T 3 -qO- http://api:8080/hostname
kubectl --context='lab' -n 'academy-lab' get service api -o wide
How do I know it worked?

Both fresh requests return the lab server hostname; record the Service IP.

06

LET’S BUILD

Write the default-deny policy

Use a separate new file so rollback is limited to policies. Denying egress also denies DNS, which is intentional in the first negative test. Traffic from a Pod’s own node and non-TCP/UDP/SCTP behavior have standard API limitations; this lab does not claim host isolation or Layer7 authorization.

bash

Operator terminal; authorized lab context

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

A distinct policy file is ready, without overwriting the fixture.

07

LET’S BUILD

Isolate both directions

An empty Pod selector covers every Pod in this lab namespace only. The explicit policyTypes prevent ambiguity. No rule grants traffic yet.

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

File contents; save at the displayed filename

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: academy-default-deny
  namespace: academy-lab
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
How do I know it worked?

The file declares namespace-scoped ingress and egress isolation.

08

LET’S BUILD

Prove that a fresh connection is denied

After propagation, test DNS and the literal Service IP. Set service_ip to the actual baseline ClusterIP first; a nonexistent sample IP proves nothing. A nonzero result is expected now. Verify server readiness separately; an unhealthy server is not a policy test. Some CNIs retain established connections, so each wget must open a new connection.

bash

Operator terminal; authorized lab context

cd '/tmp/academy-netpol' || exit 1
kubectl --context='lab' create -f deny.yaml
kubectl --context='lab' -n 'academy-lab' exec client -- nslookup api
kubectl --context='lab' -n 'academy-lab' exec client -- wget -Y off -T 3 -qO- 'http://10.96.20.30:8080/hostname'
kubectl --context='lab' -n 'academy-lab' get pod api
kubectl --context='lab' -n 'academy-lab' get networkpolicy
How do I know it worked?

DNS and direct fresh HTTP fail while the API Pod stays Ready; otherwise stop and investigate CNI/other policies.

09

LET’S BUILD

Write narrow DNS and application allowances

The DNS peer combines namespaceSelector and podSelector inside the same peer item: both must match. A second peer item would mean OR. Both UDP53 and TCP53 matter. This selector assumes real CoreDNS Pods, not node-local DNS. The application rule permits only role=client to app=api on TCP8080 and the matching client egress.

bash

Operator terminal; authorized lab context

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

A new allowance file is ready; actual DNS labels were confirmed in preflight.

10

LET’S BUILD

Allow DNS and the exact client-to-API flow

These allowances do not grant general internet access or ingress to clients. Service DNAT ordering can vary by implementation; the positive/negative tests are required to establish this fixture’s behavior. Do not add broad CIDR allowances merely to force a passing test.

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

File contents; save at the displayed filename

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: academy-dns, namespace: academy-lab}
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}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: academy-api-ingress, namespace: academy-lab}
spec:
  podSelector:
    matchLabels: {app: api}
  policyTypes: [Ingress]
  ingress:
  - from:
    - podSelector:
        matchLabels: {role: client}
    ports: [{protocol: TCP, port: 8080}]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: academy-client-egress, namespace: academy-lab}
spec:
  podSelector:
    matchLabels: {role: client}
  policyTypes: [Egress]
  egress:
  - to:
    - podSelector:
        matchLabels: {app: api}
    ports: [{protocol: TCP, port: 8080}]
How do I know it worked?

Three narrow policies preserve the default-deny base and grant only explicit paths.

Explore all tutorials ↗