A NetworkPolicy exists in the API, but your connection still succeeds. Or you allow ingress and the connection still fails. Both are understandable once you separate selection, direction, and enforcement.
We’ll build one allowed path between two test Pods and change one permission at a time.
Start with two decisions
Egress is traffic leaving a Pod. Ingress is traffic entering a Pod. If both sides are isolated in the relevant direction, both must allow a new connection.
Policies are additive: their allowed traffic combines. A default-deny policy does not override a separate broad allow. Creating the API object also does not prove that the cluster’s network implementation enforces it. These rules are described in the NetworkPolicy concepts.
Read the diagram as two checks on a connection, not one global firewall switch.
Establish a working baseline
Confirm your current context points to the disposable cluster. Create a new namespace and two fixtures:
kubectl create namespace policy-lesson
kubectl run api -n policy-lesson --image=busybox:1.37.0 \
--labels=app=api --restart=Never --command -- sh -c \
'mkdir -p /tmp/www && printf "policy-ok\n" > /tmp/www/index.html && httpd -f -p 8080 -h /tmp/www'
kubectl run client -n policy-lesson --image=busybox:1.37.0 \
--labels=role=frontend --restart=Never --command -- sleep 3600
kubectl wait -n policy-lesson --for=condition=Ready pod/api pod/client --timeout=120s
API_IP=$(kubectl get pod api -n policy-lesson -o jsonpath='{.status.podIP}')
kubectl exec -n policy-lesson client -- wget -T 3 -qO- "http://${API_IP}:8080/"
Expect policy-ok. Resolve any image, admission, or listener failure before continuing. Using the Pod IP keeps DNS out of this first experiment. The official policy exercise provides another small verification pattern.
Remove permission, then restore half of it
Save and apply this as deny.yaml using kubectl apply -f deny.yaml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: isolate
namespace: policy-lesson
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
Repeat the same request; it should no longer succeed. Policy propagation can take time. If new requests continue to work, investigate enforcement and other policies before assuming isolation exists.
Now save and apply ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-from-frontend
namespace: policy-lesson
spec:
podSelector:
matchLabels: {app: api}
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels: {role: frontend}
ports:
- {protocol: TCP, port: 8080}
Pause and predict
Will the original client request succeed after adding only this ingress policy?
Need a hint?
The source Pod still has a separate egress decision.
Show the reasoning
Complete the path
Save and apply egress.yaml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-to-api
namespace: policy-lesson
spec:
podSelector:
matchLabels: {role: frontend}
policyTypes: [Egress]
egress:
- to:
- podSelector:
matchLabels: {app: api}
ports:
- {protocol: TCP, port: 8080}
Repeat the request and expect policy-ok. These peer selectors refer to Pods in this policy’s namespace. Reply traffic for an allowed connection doesn’t require a separate reverse grant.
If you later test service names, add the DNS path deliberately. Don’t interpret a blocked DNS lookup as proof that the application policy is wrong.
Try a different identity
Change only the client label:
kubectl label pod client -n policy-lesson role=other --overwrite
Repeat the request. It should fail again once policy updates are enforced. Restore role=frontend and verify recovery. The backend remains running, so this is stronger evidence than testing a port where nothing listens.
Apply the idea
Someone adds an allow-all egress policy for the client. Does the default-deny object cancel that permission?
Need a hint?
Allowed traffic combines across policies.
Show the reasoning
When finished, delete only the dedicated namespace with kubectl delete namespace policy-lesson. You should now be able to predict all four stages: baseline success, denial, ingress-only failure, and success after both allowances.