Technical Theory

Kubernetes Network Policies

Introduction

This tutorial will guide you through creating and applying Kubernetes Network Policies. Network Policies control traffic flow at the IP address or port level (OSI layer 3 or 4). You will learn how to restrict both ingress (incoming) and egress (outgoing) traffic for your pods, enhancing the security of your Kubernetes cluster.

Prerequisites:

  • A running Kubernetes cluster (minikube, kind, or a cloud provider).
  • kubectl configured to connect to your cluster.
  • Basic understanding of Kubernetes concepts (Pods, Deployments, Services).

Task 1: Setting up the Namespace and Deployments

First, create a dedicated namespace for our demonstration and deploy a few applications. This will allow us to isolate our network policies and deployments.

  1. Create a namespace called network-policy-demo:

    NODE_TYPE // yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: network-policy-demo
    NODE_TYPE // bash
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Namespace
    metadata:
      name: network-policy-demo
    EOF
    NODE_TYPE // output
    namespace/network-policy-demo created
  2. Deploy two applications: web and db, within the network-policy-demo namespace.

    web.yaml:

    NODE_TYPE // yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web
      namespace: network-policy-demo
      labels:
        app: web
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: web
      template:
        metadata:
          labels:
            app: web
        spec:
          containers:
            - name: web
              image: nginx:latest
              ports:
                - containerPort: 80

    db.yaml:

    NODE_TYPE // yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: db
      namespace: network-policy-demo
      labels:
        app: db
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: db
      template:
        metadata:
          labels:
            app: db
        spec:
          containers:
            - name: db
              image: postgres:14
              ports:
                - containerPort: 5432
              env:
                - name: POSTGRES_PASSWORD
                  value: securepassword
    NODE_TYPE // bash
    kubectl apply -f web.yaml
    kubectl apply -f db.yaml
    NODE_TYPE // output
    deployment.apps/web created
    deployment.apps/db created
    Create web.yaml and db.yaml using your preferred text editor and then apply the deployment configurations.
  3. Verify the deployments are running:

    NODE_TYPE // bash
    kubectl get deployments -n network-policy-demo
    NODE_TYPE // output
    NAME   READY   UP-TO-DATE   AVAILABLE   AGE
    db     1/1     1            1           <age>
    web    1/1     1            1           <age>

Task 2: Testing Connectivity Before Network Policies

Before applying any Network Policies, let’s confirm that the web pod can connect to the db pod.

  1. Get the name of the web pod:

    NODE_TYPE // bash
    kubectl get pods -n network-policy-demo -l app=web -o name
    NODE_TYPE // output
    pod/web-<random-string>
  2. Exec into the web pod and install telnet:

    NODE_TYPE // bash
    kubectl exec -n network-policy-demo -it <web-pod-name> -- apt-get update && apt-get install -y telnet
    Replace <web-pod-name> with the actual name of the web pod. You may need to adjust the package manager based on the container image. For example, some images may use apk add telnet.
  3. Get the name of the db pod:

    NODE_TYPE // bash
    kubectl get pods -n network-policy-demo -l app=db -o name
    NODE_TYPE // output
    pod/db-<random-string>
  4. Test connectivity from the web pod to the db pod on port 5432:

    NODE_TYPE // bash
    kubectl exec -n network-policy-demo -it <web-pod-name> -- telnet <db-pod-name> 5432
    NODE_TYPE // output
    Trying <db-pod-ip>...
    Connected to <db-pod-name>.
    Escape character is '^]'.

    If the command connects (shows “Connected to …”), it confirms connectivity before any policies are in place. Type Ctrl+] then q to exit telnet.

Task 3: Implementing a Default Deny Network Policy

As a best practice, start by implementing a default deny policy for the entire namespace. This blocks all ingress and egress traffic unless explicitly allowed.

  1. Create a Network Policy called default-deny in the network-policy-demo namespace:

    NODE_TYPE // yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny
      namespace: network-policy-demo
    spec:
      podSelector: {}
      policyTypes:
        - Ingress
        - Egress
    NODE_TYPE // bash
    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny
      namespace: network-policy-demo
    spec:
      podSelector: {}
      policyTypes:
        - Ingress
        - Egress
    EOF
    NODE_TYPE // output
    networkpolicy.networking.k8s.io/default-deny created
    podSelector: {} selects all pods in the namespace. policyTypes: - Ingress - Egress applies the default deny to both incoming and outgoing traffic.
  2. Re-run the connectivity test from the web pod to the db pod. It should now fail.

    NODE_TYPE // bash
    kubectl exec -n network-policy-demo -it <web-pod-name> -- telnet <db-pod-name> 5432

    The connection will likely time out, indicating that the network policy is blocking the traffic.

Task 4: Allowing Ingress Traffic to the db pod

Now, create a Network Policy to allow only traffic from the web pod to the db pod on port 5432.

  1. Create a Network Policy called allow-web-to-db in the network-policy-demo namespace:

    NODE_TYPE // yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-web-to-db
      namespace: network-policy-demo
    spec:
      podSelector:
        matchLabels:
          app: db
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app: web
          ports:
            - protocol: TCP
              port: 5432
    NODE_TYPE // bash
    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-web-to-db
      namespace: network-policy-demo
    spec:
      podSelector:
        matchLabels:
          app: db
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app: web
          ports:
            - protocol: TCP
              port: 5432
    EOF
    NODE_TYPE // output
    networkpolicy.networking.k8s.io/allow-web-to-db created
    This policy selects pods with the label app: db and allows ingress traffic from pods with the label app: web on TCP port 5432.
  2. Re-run the connectivity test from the web pod to the db pod. It should now succeed.

    NODE_TYPE // bash
    kubectl exec -n network-policy-demo -it <web-pod-name> -- telnet <db-pod-name> 5432

    The connection should now be established.

Task 5: Allowing Egress Traffic from the web pod to the Internet (Example)

In many real-world scenarios, you need to allow egress traffic from your pods to external services. This example demonstrates allowing the web pod to make DNS queries.

  1. Create a Network Policy called allow-web-dns-egress in the network-policy-demo namespace:

    NODE_TYPE // yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-web-dns-egress
      namespace: network-policy-demo
    spec:
      podSelector:
        matchLabels:
          app: web
      policyTypes:
        - Egress
      egress:
        - to:
            - namespaceSelector: {} # all namespaces
              podSelector: {} # all pods
          ports:
            - protocol: UDP
              port: 53
    NODE_TYPE // bash
    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-web-dns-egress
      namespace: network-policy-demo
    spec:
      podSelector:
        matchLabels:
          app: web
      policyTypes:
        - Egress
      egress:
        - to:
            - namespaceSelector: {} # all namespaces
              podSelector: {} # all pods
          ports:
            - protocol: UDP
              port: 53
    EOF
    NODE_TYPE // output
    networkpolicy.networking.k8s.io/allow-web-dns-egress created
    This policy selects pods with the label app: web and allows egress traffic to any pod on UDP port 53 (DNS). In a real-world scenario, you should limit the to section to your cluster’s DNS service IP range.
  2. Exec into the web pod and test DNS resolution. You can use nslookup or dig. First you must install dnsutils:

    NODE_TYPE // bash
    kubectl exec -n network-policy-demo -it <web-pod-name> -- apt-get update && apt-get install -y dnsutils

    Then, test DNS resolution of a public domain:

    NODE_TYPE // bash
    kubectl exec -n network-policy-demo -it <web-pod-name> -- nslookup google.com

    If DNS resolution works, the egress policy is correctly configured.

Conclusion

In this tutorial, you learned how to:

  • Create a Kubernetes namespace and deploy applications.
  • Implement a default deny Network Policy.
  • Allow specific ingress traffic between pods.
  • Allow egress traffic from a pod to the internet (specifically DNS).

By understanding and implementing Network Policies, you can significantly enhance the security and isolation of your Kubernetes workloads. Remember to always start with a default deny policy and carefully define the necessary ingress and egress rules for each application.

Next Topic