Skip to main content

Encrypt pod traffic with WireGuard

Inspect 1.36

Turn on Cilium's WireGuard option to encrypt pod-to-pod traffic that crosses a node boundary. Each cross-node packet is encrypted in the kernel before it leaves the sending node, and your applications see no change. Know exactly what it does and does not cover before you rely on it.

Pod-to-pod encryption on the wire is optional. The cluster is secure without it. The connections that matter most already run mTLS: the reverse tunnel, the kubelet, and the cluster's database. Your own pod traffic on Hetzner travels the provider's network between servers you already own. If you want encryption at the application level with L7 metrics and auditability, mTLS through a service mesh is the less invasive path, and covers it. WireGuard is the node-level option, and it is the right one when you want cross-node pod traffic encrypted without touching the workloads.

What WireGuard covers#

WireGuard covers pod-to-pod traffic between nodes. It does not cover:

  • Same-node pod traffic. Packets between pods on the same node stay inside the kernel and never reach the network interface.
  • Host-level traffic. Node agent connections are not covered. Cilium's encrypt-node option would extend WireGuard to host traffic, but it stays off. Host and node connections already run over mutual TLS, so it adds nothing you need, and it is not part of the tested Syself configuration.

Host-level connections rely on the platform's other controls instead: mutual TLS on the reverse tunnel, certificate-based node authentication, and the default-deny host firewall. See .

What is already in place#

You do not need to change the OS image or the firewall. The stack ships everything WireGuard needs.

The kernel ships WireGuard as a module (CONFIG_WIREGUARD=m) baked into the sealed image and auto-loaded when Cilium brings up the WireGuard interface, so you install nothing. The host firewall on worker and control-plane nodes already opens UDP port 51871 between cluster nodes, and it stays open whether WireGuard is on or off. Turning WireGuard on needs no firewall change.

Enable WireGuard#

Tip

Use CiliumNodeConfig to survive cluster upgrades. The platform reconciles the global cilium-config ConfigMap during cluster upgrades, which can overwrite manual patches. Creating a CiliumNodeConfig Custom Resource in the Kubernetes API ensures your WireGuard configuration persists across cluster upgrades.

Apply a `CiliumNodeConfig` resource #

Apply a CiliumNodeConfig manifest targeting your nodes:

enable-wireguard.yamlyaml
		apiVersion: cilium.io/v2
kind: CiliumNodeConfig
metadata:
  name: enable-wireguard
  namespace: kube-system
spec:
  nodeSelector:
    matchLabels: {}
  defaults:
    enable-wireguard: "true"
	
		$ kubectl apply -f enable-wireguard.yaml
	

This enables enable-wireguard across all matching nodes. Leave encrypt-node unset. Cilium treats its absence as false, and host and node traffic already run over mutual TLS, so Cilium node encryption is redundant here and is not part of the tested configuration.

Restart the Cilium DaemonSet #

		$ kubectl -n kube-system rollout restart daemonset/cilium
	

Wait for the rollout to finish:

		$ kubectl -n kube-system rollout status daemonset/cilium
	

Each agent reads the updated configuration on startup and brings up WireGuard interfaces between the nodes. The restart is rolling, so running workloads are not interrupted.

Verify WireGuard is active#

Check the CiliumNodeConfig resource#

		$ kubectl -n kube-system get ciliumnodeconfig enable-wireguard
	

Check the Cilium agent status#

Run this on any node where the Cilium pod is running:

		$ kubectl -n kube-system exec -it daemonset/cilium -- \
  cilium-dbg encrypt status
	

The output shows the WireGuard interface name and the number of encrypted peers. Cilium does not peer a node with itself, so you should see one peer per other node: N-1 peers on an N-node cluster.

Confirm with tcpdump#

To confirm that cross-node traffic is encrypted on the wire, run tcpdump against a node's primary network interface while sending traffic between pods on different nodes. The sealed OS ships no tcpdump and its root filesystem is read-only, so bring the tool with you in a debug pod:

		# Replace <node> and the interface name with the real values:
$ kubectl debug node/<node> -it --image=nicolaka/netshoot -- \
  tcpdump -n -i eth0 udp port 51871
	

You should see UDP traffic on port 51871 between the two node IPs. That traffic is the WireGuard-encrypted payload.

What happens on each node#

Once WireGuard is on, Cilium creates a cilium_wg0 interface on each node. All cross-node pod traffic goes through it. The kernel encrypts each packet leaving the node and decrypts each packet arriving, then hands the decrypted packet to the Cilium datapath (the eBPF program that routes pod traffic).

Hubble still shows the source and destination pod identities, because Cilium decrypts and re-identifies before applying policy. The WireGuard layer is below the Hubble tap, so observability is not affected.

Disable WireGuard#

Delete the CiliumNodeConfig and restart the DaemonSet:

		$ kubectl -n kube-system delete ciliumnodeconfig enable-wireguard
$ kubectl -n kube-system rollout restart daemonset/cilium