Ravindra BagaleCourses & study guides मराठी

Chapter 19: Kubernetes Services, Ingress and Volumes

19.10 Safety

Why: the problem

In many clusters every Pod can talk to every other Pod. That is the default. Now imagine an attacker gets into the public blog Pod of a fintech like Razorpay through an old plugin. From there, with default settings, the attacker can reach the payments database Service directly. Services and volumes make apps easy to reach and data easy to share, so they also need guard rails. The main ones are simple. A NetworkPolicy says which Pods may talk to which. Secrets are mounted as read-only files instead of being baked into images or printed in logs. hostPath is avoided. Volumes are mounted read only when the app only reads. LoadBalancers are limited by source ranges or kept internal. Ingress uses TLS. In this lesson you will prove the risk first, then close it with a default-deny NetworkPolicy and an allow rule, mount a Secret read-only, and block hostPath with Pod Security Admission.

What we will build

You will run a db Service and two client Pods, web and blog. First both reach db. Then you will apply a default-deny policy and an allow rule so only web reaches db. Then you will mount a Secret read-only and see a namespace with the restricted Pod Security level refuse a hostPath Pod.

What: every term in plain English

  • NetworkPolicy — Rules that say which Pods can send traffic to which Pods and ports.
  • Default deny — A policy that blocks all incoming traffic unless another policy allows it.
  • podSelector — Picks the Pods a policy applies to, by label.
  • ingress rule — Allowed incoming traffic (not the same as an Ingress object).
  • CNI — The network plug-in. It must support NetworkPolicy (Calico, Cilium, or kind v0.24+ which ships kube-network-policies).
  • Pod Security Admission — A built-in check that refuses Pods breaking a security level.
  • restricted level — The strictest level: no root, no hostPath, no privilege escalation.
  • readOnly mount — The container can read the volume but not change it.
Create kind (v0.24 or newer enforces NetworkPolicy) Make a practice namespace Run the db and its Service Run two clients Prove the risk
Safety: only the right Pods talk, secrets stay read only

How: step by step

  1. Create kind (v0.24 or newer enforces NetworkPolicy)

    kind version && kind create cluster --name lab
    

    What it does: Recent kindnet supports NetworkPolicy. On older kind, install Calico.

    Output

    kind v0.24.0 go1.22.6 linux/amd64
    Set kubectl context to "kind-lab"
    
  2. Make a practice namespace

    kubectl create namespace practice
    kubectl config set-context --current --namespace=practice
    

    What it does: Keeps all lab objects in one folder that is easy to delete.

    Output

    namespace/practice created
    Context "kind-lab" modified.
    
  3. Run the db and its Service

    kubectl run db --image=traefik/whoami:v1.10 --labels=app=db --port=80 && kubectl expose pod db --port=80
    

    What it does: A stand-in for a database, labelled app=db.

    Output

    pod/db created
    service/db exposed
    
  4. Run two clients

    kubectl run web --image=curlimages/curl:8.8.0 --labels=app=web -- sleep 3600
    kubectl run blog --image=curlimages/curl:8.8.0 --labels=app=blog -- sleep 3600
    

    What it does: web should reach db; blog should not.

    Output

    pod/web created
    pod/blog created
    
  5. Prove the risk

    for p in web blog; do echo -n "$p: "; kubectl exec $p -- curl -s -m 2 -o /dev/null -w "%{http_code}\n" db; done
    

    What it does: Both get 200. That is the problem.

    Output

    web: 200
    blog: 200
    

Example 1: Default deny, then allow only web to db

Stack. NetworkPolicy, kindnet

Goal. Default deny, then allow only web to db.

netpol.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes: ["Ingress"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-web
spec:
  podSelector:
    matchLabels: {app: db}
  policyTypes: ["Ingress"]
  ingress:
  - from:
    - podSelector:
        matchLabels: {app: web}
    ports:
    - {protocol: TCP, port: 80}

terminal

kubectl apply -f netpol.yaml
for p in web blog; do echo -n "$p: "; kubectl exec $p -- curl -s -m 2 -o /dev/null -w "%{http_code}\n" db || echo blocked; done

What each line does

  • podSelector: {} — Applies to every Pod in the namespace.
  • policyTypes Ingress with no rules — Deny all incoming traffic.
  • podSelector app: db — The second policy protects db.
  • from podSelector app: web — Only Pods labelled web may connect.
  • ports TCP 80 — And only on port 80.

Green output

networkpolicy.networking.k8s.io/default-deny-ingress created
networkpolicy.networking.k8s.io/db-allow-web created
web: 200
blog: 000
blocked

Expected result. Only web reaches db now. A broken blog Pod can no longer touch the database.

Example 2: Mount a Secret as a read-only file, not an env var in logs

Stack. Secret volume

Goal. Mount a Secret as a read-only file, not an env var in logs.

secret-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: app
  labels: {app: web}
spec:
  containers:
  - name: app
    image: busybox:1.36
    command: ["sh", "-c", "sleep 3600"]
    volumeMounts:
    - name: creds
      mountPath: /etc/creds
      readOnly: true
  volumes:
  - name: creds
    secret:
      secretName: db-creds
      defaultMode: 0400

terminal

kubectl create secret generic db-creds --from-literal=password=lab-only-pass
kubectl apply -f secret-pod.yaml && kubectl wait --for=condition=ready pod/app
kubectl exec app -- ls -l /etc/creds/
kubectl exec app -- sh -c "echo hack > /etc/creds/password" || true

What each line does

  • secretName db-creds — The Secret to mount.
  • defaultMode: 0400 — Files readable only by the owner.
  • readOnly: true — The app cannot change it.
  • lab-only-pass — A lab value. Real values come from a secret manager.
  • echo hack — Fails, proving the mount is read only.

Green output

secret/db-creds created
pod/app condition met
lrwxrwxrwx 1 root root 15 Oct 25 05:10 password -> ..data/password
sh: can't create /etc/creds/password: Read-only file system

Expected result. The password is a read-only file. It does not show up in kubectl describe, and the app cannot overwrite it.

Example 3: Pod Security Admission blocks hostPath

Stack. Pod Security Admission (built in since 1.25)

Goal. Pod Security Admission blocks hostPath.

terminal

kubectl label ns practice pod-security.kubernetes.io/enforce=restricted --overwrite
kubectl run bad --image=busybox:1.36 --overrides='{"spec":{"volumes":[{"name":"h","hostPath":{"path":"/"}}],"containers":[{"name":"bad","image":"busybox:1.36","command":["sleep","3600"],"volumeMounts":[{"name":"h","mountPath":"/host"}]}]}}' 

What each line does

  • label enforce=restricted — Turn on the strictest Pod Security level for this namespace.
  • hostPath path / — Tries to mount the whole node disk. Very dangerous.
  • --overrides — Passes a full Pod spec to kubectl run.

Green output

namespace/practice labeled
Error from server (Forbidden): pods "bad" is forbidden: violates PodSecurity "restricted:latest": restricted volume types (volume "h" uses restricted volume type "hostPath"), allowPrivilegeEscalation != false, runAsNonRoot != true

Expected result. The API server refused the Pod before it ran. The label protects the namespace from risky volumes and settings.

Verify: did it work?

  1. List policies

    kubectl get networkpolicy
    

    What it does: Shows both policies.

    Output

    NAME                   POD-SELECTOR   AGE
    db-allow-web           app=db         2m
    default-deny-ingress   none           2m
    
  2. Check namespace labels

    kubectl get ns practice --show-labels
    

    What it does: Shows the Pod Security label.

    Output

    NAME       STATUS   LABELS
    practice   Active   kubernetes.io/metadata.name=practice,pod-security.kubernetes.io/enforce=restricted
    

When it is red 1

NetworkPolicy applied but blog still gets 200

Cause. The CNI does not enforce NetworkPolicy (old kind, flannel).

Fix, step by step

  1. Check kind version is v0.24+ or install Calico or Cilium.
  2. Re-test.

When it is red 2

After default deny, DNS lookups fail

Cause. An egress default deny blocked port 53 to CoreDNS.

Fix, step by step

  1. Add an egress rule allowing UDP and TCP 53 to kube-system.
  2. Our example denies ingress only, so DNS keeps working.

When it is red 3

pods "app" is forbidden: violates PodSecurity "restricted:latest"

Cause. The restricted label also blocks Pods without securityContext.

Fix, step by step

  1. Add runAsNonRoot: true, allowPrivilegeEscalation: false, capabilities drop ALL and seccompProfile RuntimeDefault.
  2. Or use the baseline level for labs.

Cleanup

  1. Delete the policies and Pods

    kubectl delete networkpolicy --all && kubectl delete pod --all --now
    

    What it does: Removes the lab objects.

    Output

    networkpolicy.networking.k8s.io "db-allow-web" deleted
    networkpolicy.networking.k8s.io "default-deny-ingress" deleted
    
  2. Delete the practice namespace

    kubectl delete namespace practice
    

    What it does: Deletes every object you made in this lesson in one go.

    Output

    namespace "practice" deleted
    
  3. Delete the lab cluster

    kind delete cluster --name lab
    

    What it does: Removes the whole cluster container. On minikube use: minikube delete.

    Output

    Deleting cluster "lab" ...
    Deleted nodes: ["lab-control-plane"]
    

Ravindra Bagale's Tip

Default madhe sagle Pods ekmekanshi boltat. Aadhi sagla band kara (default deny), mag fakt garjeche darwaje ughda!

Practice tasks

Easy Prove the default

Why: See open traffic first.

What to do: Call db from blog before any policy.

How:
  1. kubectl exec blog -- curl db

Check: You get 200.

Medium Allow by namespace

Why: Teams often allow a whole namespace.

What to do: Allow traffic to db from namespace frontend.

How:
  1. Use namespaceSelector.

Check: Only Pods in frontend reach db.

Hard Egress lock-down

Why: Stop data leaving.

What to do: Deny all egress from blog except DNS.

How:
  1. policyTypes Egress.
  2. Allow port 53.

Check: blog cannot curl example.com but can resolve names.

Easy Say Safety in two lines

Why: If you can say it simply, you understand it. Interviewers want the short version first.

What to do: Write two short lines about Safety for a fresher who knows only Docker basics.

How:
  1. Write what problem it solves.
  2. Write the one kubectl command you use for it.
  3. Read it aloud to a friend.

Check: A fresher understands it without asking a question.

Easy Start a fresh lab cluster

Why: Practice needs a throwaway cluster. If you break it, you delete it and start again.

What to do: Create a kind cluster, or a minikube cluster, and check the node is Ready.

How:
  1. kind create cluster --name lab
  2. kubectl get nodes
  3. kubectl get pods -A

Check: One node shows STATUS Ready and the kube-system Pods are Running.

Medium Break it on purpose

Why: You must know the red message by face. During an outage there is no time to read slowly.

What to do: Cause one error from the "When it is red" part of this lesson and fix it.

How:
  1. Follow the error section.
  2. Copy the exact message and the kubectl describe Events into your notes.
  3. Write the fix in one line.

Check: You fixed it using only the steps, without searching the web.

Medium Write a one-page runbook

Why: At 2 a.m. nobody remembers the right command. A short page saves the on-call person.

What to do: Write a runbook page for this lesson.

How:
  1. Title, when to use, exact kubectl command, how to check, how to undo.

Check: A teammate can follow it alone.

Medium Repeat it on a cloud cluster

Why: kind is a laptop toy. Real clusters run on EKS, GKE, AKS or k3s on a VM.

What to do: Do the main steps of this lesson again on a small k3s VM or a free-tier managed cluster.

How:
  1. Install k3s on one Ubuntu 22.04 VM: curl -sfL https://get.k3s.io | sh -
  2. Copy the kubeconfig.
  3. Apply the same YAML and compare the output.

Check: Same output on the cloud cluster as on kind.

Hard Explain it like an interview

Why: Interviewers want real stories, not definitions.

What to do: Record a 60 second answer about this lesson.

How:
  1. Say the problem, the YAML field you used, the kubectl command you ran, and one error you hit.

Check: Your answer has a real command and a real error message.

Hard Draw the flow top to bottom

Why: A drawing shows who talks to whom and in what order.

What to do: Draw this lesson as boxes from top to bottom.

How:
  1. Start at kubectl apply, go through the API server, controller and scheduler, end at the running container.

Check: Each box matches a real thing you saw in kubectl output.

Homework: try at home

Try at home

At home, add a third Pod, api, and change db-allow-web so both web and api can reach db, but blog still cannot. Then add an egress policy on blog that allows only DNS.

Recap

  • By default every Pod can reach every Pod.
  • A default-deny policy plus allow rules limits traffic.
  • Mount Secrets as read-only files.
  • Avoid hostPath; enforce Pod Security levels.
  • Limit LoadBalancers with source ranges and use TLS on Ingress.

FAQ

Are Kubernetes Secrets encrypted?

They are base64, not encrypted, unless you turn on encryption at rest (EKS can use KMS).

Should I use env vars for secrets?

Files are safer. Env vars can leak in crash dumps and debug output.

Does NetworkPolicy need a special CNI?

Yes. Calico, Cilium and kind v0.24+ (built-in kube-network-policies) support it. Plain flannel does not.

What are the Pod Security levels?

privileged, baseline and restricted.

Can I test a policy without blocking?

Use the warn or audit labels of Pod Security; for NetworkPolicy, test in a lab namespace.

Interview questions

  1. What is a NetworkPolicy?

    A namespaced object that limits ingress and egress traffic for selected Pods, enforced by the CNI.

  2. How do you isolate a database in Kubernetes?

    Default-deny ingress plus a policy allowing only the app Pods on the DB port.

  3. How should secrets be given to Pods?

    From a secret manager via the Secrets Store CSI driver or Secrets, mounted read-only, never in images.

  4. What is Pod Security Admission?

    A built-in admission controller that enforces privileged, baseline or restricted standards per namespace.

  5. Why is hostPath dangerous?

    It gives access to the node file system and can lead to node takeover.

Quick quiz

Q1. What is the default network rule between Pods?

All Pods can talk to all Pods.

Q2. What does podSelector: {} in a policy mean?

All Pods in the namespace.

Q3. Why mount Secrets as read-only files?

The app cannot change them and they stay out of env dumps and logs.

Q4. What blocks hostPath at the namespace level?

Pod Security Admission with the restricted (or baseline) level.

Q5. What must the CNI support?

NetworkPolicy.

Samajla ka?

Ravi Sir's Cards for this lesson

मराठी cards