Encrypt pod traffic with WireGuard
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 Networking and Cilium 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-nodeoption 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 Zero trust on public networks .
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:
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
Related#
- Zero trust on public networks : how each host-level connection is secured.
- Encrypt etcd : encrypting secrets in the etcd database.
- Ports and listeners : the full list of open ports, including UDP 51871.
- Security architecture : where pod encryption fits in the platform security model.
- See network flows with Hubble : observing traffic flows alongside WireGuard encryption.
Control pod egress (and the FQDN limitation)
Restrict what your pods can call out to with Cilium egress policy, and understand that FQDN and DNS rules are rejected at apply time, so you use address, port, and identity rules instead.
Workload runtimes and isolation
How the standard runtime, user namespaces, and the secure Kata runtime isolate workloads on Syself Autopilot, and which pod security controls you must configure.