nftables is the successor to iptables for Linux packet filtering and firewalling. When used with Kubernetes, nftables is often the backend that the Container Network Interface (CNI) plugin uses to program network policies and service routing.

Here’s how to see nftables in action managing Kubernetes CNI firewall rules.

Let’s say you have a simple two-node Kubernetes cluster. On one node, you have a frontend pod running a web server, and on another node, you have a backend pod running an API. The frontend pod needs to communicate with the backend pod on a specific port.

First, we need to apply a CNI plugin that supports nftables as its backend. Calico and Cilium are popular choices that can be configured to use nftables. For this example, let’s assume Calico is installed and configured to use nftables.

You deploy your pods:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: api
        image: python:3.9-slim
        command: ["python", "-m", "http.server", "8080"]
        ports:
        - containerPort: 8080

Now, let’s create a Kubernetes Service for the backend so the frontend can discover and communicate with it.

apiVersion: v1
kind: Service
metadata:
  name: backend-svc
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

At this point, you can exec into the frontend pod and try to curl the backend-svc on port 80. The traffic will flow through the Kubernetes networking infrastructure.

Now, let’s look at what’s happening on the node where the frontend pod is running using nftables. You’ll need root privileges for these commands.

Run sudo nft list ruleset. You’ll see a complex set of rules, but we’re interested in the table named kube_services and potentially kube_forward or kube_filter depending on your CNI and configuration.

The kube_services table is where Kubernetes services are translated into actual IP addresses and ports. You’ll see entries that map the virtual IP (VIP) of backend-svc to the real IP address and port of the backend pod.

Here’s a simplified, conceptual view of what you might find in your nftables ruleset:

table ip kube_services {
  chain KUBE_SERVICES_INPUT {
    type filter hook input priority 0; policy accept;
    # ... other service rules ...
    ip daddr 10.96.0.10 tcp dport 80 ct state new,established accept
    # This rule might be more complex, involving DNAT to the actual pod IP
    # For simplicity, let's assume the CNI handles the jump to a NAT chain
  }
  chain KUBE_SERVICES_DNAT {
    type nat hook preresouting priority -100; policy accept;
    # ... other service DNAT rules ...
    ip daddr 10.96.0.10 tcp dport 80 ip saddr 192.168.1.50 counter jump target KUBE_SERVICES_DST_10_96_0_10
  }
  chain KUBE_SERVICES_DST_10_96_0_10 {
    # This chain selects the backend pod IP for the service
    ip daddr 10.96.0.10 tcp dport 80 ip saddr 192.168.1.50 counter ip daddr 172.17.0.5 # Assuming backend pod IP is 172.17.0.5
  }
}

# You might also see rules in a table like 'kube_forward' or 'filter'
# for actual pod-to-pod communication, load balancing, and network policies.
table ip filter {
  chain FORWARD {
    type filter hook forward priority 0; policy accept;
    # Rules to allow traffic between pods on different nodes
    # Example: allow traffic from frontend pod's IP to backend pod's IP
    iifname "eth0" ip saddr 192.168.1.50 ip daddr 172.17.0.5 ct state related,established accept
    iifname "eth0" ip saddr 192.168.1.50 ip daddr 172.17.0.5 accept
  }
}

In this snippet:

  • kube_services table is responsible for translating the service VIP (10.96.0.10 for backend-svc) to the actual backend pod IP (172.17.0.5).
  • The KUBE_SERVICES_DNAT chain handles the Destination Network Address Translation (DNAT) for incoming traffic to the service VIP.
  • KUBE_SERVICES_DST_10_96_0_10 is a specific chain that contains rules to select which backend pod to send the traffic to (if there were multiple replicas).
  • The filter table, specifically the FORWARD chain, enforces network policies and allows legitimate pod-to-pod traffic between nodes. The CNI plugin programs these rules to ensure only authorized communication happens.

The most surprising thing about nftables in Kubernetes is how seamlessly it integrates. You don’t typically interact with nftables directly for day-to-day Kubernetes operations. The CNI plugin acts as an abstraction layer, translating Kubernetes NetworkPolicies and Service definitions into precise nftables rules. This means that even if you’re not an nftables expert, you’re benefiting from its power and flexibility through your CNI.

To see a real-time transaction, you can use sudo nft monitor. This will show you nftables events as they happen, including rule additions, deletions, and packet matches (if you configure counters).

For example, if you curl the backend-svc from the frontend pod, you might see a counter increment on a specific nftables rule. You can add counters to your rules by adding counter to the end of a rule, like in the example above. After running sudo nft monitor, you can trigger traffic and observe the packet and byte counts increase on the relevant rules.

The CNI plugin is responsible for managing these rules, adding and removing them dynamically as pods are created, deleted, or rescheduled, and as NetworkPolicies are applied or modified. This dynamic management is key to Kubernetes networking.

A crucial detail often missed is how nftables handles IP address masquerading for egress traffic. When a pod initiates a connection to an external IP address, its source IP (a private cluster IP) is typically masqueraded by the node’s IP address. This is handled by nftables rules in the nat table, usually in a chain like POSTROUTING. The CNI plugin configures these rules to ensure that return traffic knows how to get back to the correct pod.

The next concept you’ll encounter is how nftables is used to implement Kubernetes NetworkPolicies, which provide granular control over which pods are allowed to communicate with each other.

Want structured learning?

Take the full Nftables course →