WireGuard With Calico in K8s for Host to Host Encryption

Dhawalsaini Devops
5 min readJan 31, 2023

Calico’s best-known security feature is an implementation of Kubernetes Network Policies, which provides a way to secure container workloads by restricting traffic to and from trusted sources. While the source of traffic can be controlled, without encryption the traffic itself is vulnerable to interception.

A common solution is to encrypt traffic at the application layer using protocols like Transport Layer Security (TLS), but it’s also possible to encrypt traffic at a lower infrastructure level using protocols like IPSec.

IPSec has shipped as a standard option in the Linux kernel for a long time, but it introduces a layer of management complexity for setting up and maintaining secure network connections. WireGuard is a kernel alternative to IPSec that aims to “be faster, simpler, leaner, and more useful.” It’s always been possible to run Calico on top of an IPSec enabled network that you are managing yourself, but the 3.14 release of Project Calico introduced a new tech-preview of Calico managed encryption backed by Wireguard.

Before we begin..

Using Calico, all nodes with WireGuard enabled will peer with each other, forming an encrypted mesh.

Our goal with leveraging WireGuard was not to compromise. We wanted to offer the simplest, most secure and fastest way to encrypt data in transit in a Kubernetes cluster without needing mTLS, IPsec or complicated configuration requirements. In fact, you can think of WireGuard as just another overlay with the added benefit of encryption.

The user simply enables WireGuard with one command, and Calico looks after everything else, including:

  • Creating the WireGuard network interface on each node.
  • Calculating and programming the optimum MTU.
  • Creating the WireGuard public/private key pair for each node.
  • Adding the public key to each node resource for sharing across the cluster.
  • Programming the peers for each node.
  • Programming the IP route, IP tables and routing tables with firewall classifier marks (fwmarks) to handle routing correctly on each node.
  • Creating the WireGuard public/private key pair for each node.
  • Adding the public key to each node resource for sharing across the cluster.
  • Programming the peers for each node.
  • Programming the IP route, IP tables and routing tables with firewall classifier marks (fwmarks) to handle routing correctly on each node.

We just specify the intent; the cluster does everything else.

Supported encryption

  • Pod-to-pod traffic on different hosts flows encrypted.
  • Host-to-host traffic also flows encrypted.

Un-Supported encryption

  • Pod-to-pod traffic on the same host flows unencrypted.

Installation of WireGuard on Worker Node

On all nodes in the cluster are installed with WireGuard that you want to participate in Calico encryption.

  • For Red Hat Enterprise Linux 8 — the easiest way is via ELRepo’s pre-built module. (other OS Linux distributions read this Installation guide)
$ sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm
$ sudo yum install kmod-wireguard wireguard-tools

Note: After you install WireGuard, you may need to reboot your machines to make the required kernel modules available.

Enable WireGuard for a cluster

  • Enable IPv4 WireGuard encryption across all the nodes using the following command.
kubectl patch felixconfiguration default - type='merge' -p '{"spec":{"wireguardEnabled":true}}'
  • Enable IPv6 WireGuard encryption across all the nodes using the following command.
kubectl patch felixconfiguration default - type='merge' -p '{"spec":{"wireguardEnabledV6":true}}'
  • To enable both IPv4 and IPv6 WireGuard encryption across all the nodes, use the following command.
kubectl patch felixconfiguration default - type='merge' -p '{"spec":{"wireguardEnabled":true,"wireguardEnabledV6":true}}'

Note: If Calico is already installed in k8s cluster, you need to restart their pod to effect this configuration.

After patch you can verify configuration as given below

First, Check FelixConfiguration “vxlanEnabled” and “wireguardEnabled” value should be “true”

[root@k8smst1 ~]# kubectl get FelixConfiguration -o yaml

Output:

apiVersion: v1
kind: List
items:
- apiVersion: crd.projectcalico.org/v1
kind: FelixConfiguration
metadata:
generation: 3
name: default
spec:
bpfLogLevel: ""
logSeverityScreen: Info
reportingInterval: 0s
vxlanEnabled: true
wireguardEnabled: true

Second, verify that the nodes are configured for WireGuard encryption

$ kubectl get node <NODE-NAME> -o yaml | grep -A 8 annotations:

Output:

annotations:
projectcalico.org/IPv4VXLANTunnelAddr: 10.244.164.0
projectcalico.org/IPv4WireguardInterfaceAddr: 10.244.164.17
projectcalico.org/WireguardPublicKey: puRquunB6LLaaiWADIsusG4V035mFISDFwrzDtfnX14=

Third Step, You can check WireGuard status on node as well:

[root@k8swkr1 ~]# wg show

Output:

interface: wireguard.cali
public key: puRquunB6LLaaiWADIsusG4V035mFISDFwrzDtfnX14=
private key: (hidden)
listening port: 51820
fwmark: 0x100000

peer: 5e2b6EjsChX2bOvqVX6NsA1G5yLGQ2Ms1xVavFWSyCo=
endpoint: 57.255.232.78:51820
allowed ips: 10.244.98.64/26, 10.244.98.64/32, 10.244.98.107/32
latest handshake: 38 seconds ago
transfer: 480.95 MiB received, 479.31 MiB sent

peer: Z67U7DVEDiItblthBN8WzxLMlq2LsVR+x9UWbdX9dUU=
endpoint: 57.255.232.79:51820
allowed ips: 10.244.39.64/32, 10.244.39.64/26, 10.244.39.107/32
latest handshake: 1 minute, 10 seconds ago
transfer: 5.22 GiB received, 5.26 GiB sent

Packet Flow with WireGuard Explained

The diagram below shows the various packet flow scenarios in a cluster with WireGuard enabled.

Key Point : green indicates unencrypted traffic and red indicates encrypted traffic.

Pods on the same host:

  • Packets are routed to the WireGuard table.
  • If the target IP is a pod on the same host, then Calico will have inserted a “throw” entry in the WireGuard routing table directing the packet back to the main routing table. There, the packet will be directed to the interface on the host side of the veth pair for the target pod, and it will flow unencrypted (shown in green on the diagram).

Pods on different nodes:

  • Packets are directed to the WireGuard table.
  • A routing entry is matched for the destination pod IP and sent to the WireGuard device cali.wireguard.
  • WireGuard device encrypts (shown in red on the diagram) and encapsulates the packet and sets a fwmark to prevent routing loops.
  • WireGuard device encrypts the packet with the public key of the peer it matches for the destination podIP (allowed IP), encapsulates it in UDP, and marks it with a special fwmark to prevent routing loops.
  • The packet is sent through eth0 to the destination node where the reverse happens.
  • This also works for host traffic (for example, host-networked pods).

PS : I am doing this for learning purposes. If you think that I made some mistakes or should have done it another way please let me know.

Thanks for reading….

--

--