Module 01 · Lesson 01

Network Security Policies

Restrict cluster-level access with default-deny and targeted allow rules.

Concept
Learning objectives
  • Explain why selecting a pod flips it to deny-by-default.
  • Write a policy that allows one workload to reach another, and nothing else.
  • Spot the indentation trap that turns an AND into an OR.
NetworkPolicy Kubernetes

The default is wide open

A fresh cluster applies no network restrictions at all. Every pod can reach every other pod, in every namespace. A four-tier application with a frontend, an API, a database and a monitoring stack has, by default, a direct path from the public web tier to the database.

A NetworkPolicy changes that, but not in the way a firewall rule would. It has no deny action and no ordering, and understanding why is most of this topic.

Anatomy of a policy

yamlallow-web-to-api.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-web-to-api
  namespace: backend          # 1. policies are NAMESPACED
spec:
  podSelector:                # 2. which pods THIS policy protects
    matchLabels:
      app: api
  policyTypes:                # 3. which directions it governs
  - Ingress
  ingress:
  - from:                     # 4. who is allowed
    - namespaceSelector:
        matchLabels:
          tier: frontend
      podSelector:
        matchLabels:
          app: web
    ports:
    - protocol: TCP
      port: 80

Read it as: in namespace backend, for pods labelled app=api, deny all ingress except TCP 80 from pods labelled app=web in namespaces labelled tier=frontend.

Four rules that decide every outcome

Rule 1: selecting a pod flips it to deny-by-default

A pod with no policy selecting it allows everything. The moment any policy with Ingress in policyTypes selects it, all ingress is denied except what that policy, and any other policy selecting it, explicitly allows.

Rule 2: policies are additive, and there is no deny

Two policies selecting the same pod produce the union of their allows. There is no deny rule, no priority and no ordering. If traffic is getting through and you do not want it to, you are looking for the policy that allows it, not writing one that blocks it.

Rule 3: policyTypes decides the directions

policyTypes: [Ingress] leaves egress completely untouched and wide open. policyTypes: [Ingress, Egress] locks both to what you allow. Omitting Egress is the most common way people believe they have isolated a workload when they have not.

Rule 4: one dash changes the meaning entirely

This is the exam's favourite trap. Compare the indentation:

yaml
# AND: pods labelled app=web that are IN namespaces labelled tier=frontend
- from:
  - namespaceSelector: {matchLabels: {tier: frontend}}
    podSelector: {matchLabels: {app: web}}

# OR: ANY pod in tier=frontend namespaces, PLUS any app=web pod in THIS namespace
- from:
  - namespaceSelector: {matchLabels: {tier: frontend}}
  - podSelector: {matchLabels: {app: web}}

One leading dash turns an intersection into a union. In the second form, every pod in every frontend namespace is allowed, which is almost never what the task asked for. Check the indentation every single time.

Check the labels you are selecting on

Selectors match labels. A wrong label matches zero pods and the policy becomes a silent no-op, which looks identical to a policy that is working.

terminal
$kubectl get ns --show-labels
$kubectl -n backend get pods --show-labels
Every namespace is selectable by name

Kubernetes sets kubernetes.io/metadata.name on every namespace automatically, so you can always select one by name without labelling anything yourself: namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: frontend}}.

Denying egress breaks DNS first

The moment you add Egress to policyTypes, the pod can no longer reach CoreDNS, so every name lookup fails. The symptom is not "connection refused", it is a hang followed by a resolution error, and it looks nothing like a network policy problem. Any egress policy needs an explicit allow to kube-system on UDP and TCP 53.

A pod is selected by two policies. The first allows port 80 from the frontend, the second allows port 443 from monitoring. What reaches the pod?

You add an egress policy to a workload and it immediately stops working, with name resolution errors. What did you most likely forget?

Traffic you want blocked is still arriving at a pod that several policies select. What do you do?

Recap

Selecting a pod flips it to deny-by-default for the directions named in policyTypes. Policies only add allows, never deny. Watch the leading dash that turns AND into OR, and remember that egress rules break DNS unless you allow it. Next: reviewing configuration against the CIS Benchmark.