11 minute read

How to ensure that the traffic between my pods on the pod network across my OpenShift cluster it’s encrypted and confidential? How can I enable IPsec to secure my network traffic?

Let’s dig in!

1. Overview

When we have an untrusted network and you want to secure your communications across your cluster for your workloads, encryption services are recommended. Encryption services may also be required for regulatory or compliance reasons (e.g. FIPS compliance).

To achieve this, you can enable IPSec in your OpenShift cluster using the OVN Kubernetes CNI plugin.

IPsec is a protocol suite that enables secure network communications between IP endpoints by providing the following services:

  • Confidentiality
  • Authentication
  • Data Integrity

Broadly used in the Virtual Private Networks (VPNs), IPSec authenticates and encrypts the packets of data to provide secure encrypted communication between two computers over an Internet Protocol network

Also uses cryptographic security services to protect communications over Internet Protocol (IP) networks. It supports network-level peer authentication, data origin authentication, data integrity, data confidentiality (encryption), and replay protection.

2. IPSec Encryption in OpenShift 4 with OVN Kubernetes CNI Plugin

The OVN Kubernetes network plugin uses OVN to instantiate virtual networks for Kubernetes.

These virtual networks use Geneve, a network virtualization overlay encapsulation protocol, to tunnel traffic across the underlay network between Kubernetes Nodes.

OVN tunnel traffic is transported by physical routers and switches. These physical devices could be untrusted (devices in public network) or might be compromised.

Enabling IPsec encryption for this tunnel traffic can prevent the traffic data from being monitored and manipulated, ensuring in that way that OVN data plane (Geneve) traffic between pods on different nodes is confidential, authenticated, and has not been tampered with.

In a nutshell, the purpose of enabling IPsec is to encrypt all traffic between pods on the cluster network when that traffic leaves the node (and correspondingly decrypt traffic that enters the node).

It will not encrypt traffic between pods on the host network, as this traffic does not traverse OVN.

However, as a side effect, this will also encrypt pod traffic that originates from pods on the host network and is destined for the cluster network as this network does traverse OVN

3. Enable IPSec in of OpenShift 4 Cluster

As we described before, with IPsec enabled, all network traffic between nodes on the OVN-Kubernetes Container Network Interface (CNI) cluster network travels through an encrypted tunnel.

IPsec is disabled by default when you install OpenShift 4 clusters. IPsec encryption can be enabled only during cluster installation and cannot be disabled after it is enabled.

Let’s install an OpenShift 4.8 cluster with IPSec enabled (this cluster it’s NOT reachable from outside of a VPN for security purposes :D).

  • First prepare the install-config.yaml:
cat <<EOF > install-config.yaml
apiVersion: v1
baseDomain: rober.lab
compute:
- hyperthreading: Enabled
  name: worker
  replicas: 0
controlPlane:
  hyperthreading: Enabled
  name: master
  replicas: 3
metadata:
  name: ocp4
networking:
  clusterNetworks:
  - cidr: 10.254.0.0/16
    hostPrefix: 24
  networkType: OVNKubernetes
  serviceNetwork:
  - 172.30.0.0/16
platform:
  none: {}
pullSecret: '$(< ~/.openshift/pull-secret)'
sshKey: '$(< ~/.ssh/id_rsa.pub)'
EOF

NOTE: as you can check the OVN Kubernetes CNI Plugin it’s used in this case.

  • Copy the install-config.yaml and the folder for the installation:
mkdir ocp4
cp -pr install-config.yaml ocp4/
cd ocp4
  • Generate the manifests from the previous install-config.yaml:
openshift-install create manifests
  • Create the cluster network config file inside the manifest folder where the other manifests are located:
cat <<EOF > manifests/cluster-network-03-config.yml
apiVersion: operator.openshift.io/v1
kind: Network
metadata:
  name: cluster
spec:
  defaultNetwork:
    type: OVNKubernetes
    ovnKubernetesConfig:
      ipsecConfig: {}
      mtu: 1400
EOF

IPsec is enabled by updating the Cluster Network Operator configuration during installation.

To enable it the ipsecConfig needs to be configured inside the ovnKubernetesConfig section of a new manifest containing the specifics that will be parsed and used by the CNO for the installation of the cluster.

For more information check the official OpenShift installation advance documentation for network configurations.

With this you can install the OpenShift cluster with the regular procedure as is described in the link before.

Once the OpenShift installation it’s finished, you can check the ovn-ipsec daemonset that manages the daemons (pluto/ovs-monitor-ipsec) responsible for configuring IPsec.

# oc get ds -n openshift-ovn-kubernetes ovn-ipsec
NAME        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGE
ovn-ipsec   6         6         6       6            6           beta.kubernetes.io/os=linux   3d3h

Because of this daemonset, as you can check there are ipsec pods running in all the nodes of our OCP4 cluster:

# oc get pod -n openshift-ovn-kubernetes -o wide | grep ipsec
ovn-ipsec-4qp86        1/1     Running   0          38m   192.168.7.23   master2.ocp4.rober.lab   <none>           <none>
ovn-ipsec-pk7vh        1/1     Running   0          38m   192.168.7.21   master0.ocp4.rober.lab   <none>           <none>
ovn-ipsec-q4mwj        1/1     Running   0          22m   192.168.7.11   worker0.ocp4.rober.lab   <none>           <none>
ovn-ipsec-trz5m        1/1     Running   0          22m   192.168.7.12   worker1.ocp4.rober.lab   <none>           <none>
ovn-ipsec-vjmw8        1/1     Running   0          38m   192.168.7.22   master1.ocp4.rober.lab   <none>           <none>

Ovn-ipsec contains two daemons (pluto: IPsec IKE daemon, ovs-monitor-ipsec: OVS IPsec daemon):

[root@worker0 ~]# ps aux | grep pluto | tail -n 2 | grep -v grep
root         148  0.0  0.1 380736 14524 ?        Ssl  15:01   0:01 /usr/libexec/ipsec/pluto --leak-detective --config /etc/ipsec.conf --logfile /var/log/openvswitch/libreswan.log

[root@worker0 ~]# ps aux | grep ovs-monitor-ipsec | grep -v grep | tail -n 2
root         172  0.0  0.2 125188 16804 ?        Ss   15:01   0:00 /usr/libexec/platform-python /usr/share/openvswitch/scripts/ovs-monitor-ipsec --pidfile=/var/run/openvswitch/ovs-monitor-ipsec.pid --ike-daemon=libreswan --no-restart-ike-daemon --log-file --detach --monitor unix:/var/run/openvswitch/db.sock
root         173  0.0  0.2 125188 18632 ?        S    15:01   0:02 /usr/libexec/platform-python /usr/share/openvswitch/scripts/ovs-monitor-ipsec --pidfile=/var/run/openvswitch/ovs-monitor-ipsec.pid --ike-daemon=libreswan --no-restart-ike-daemon --log-file --detach --monitor unix:/var/run/openvswitch/db.sock

In a nutshell, ovs-monitor-ipsec reads OVS tables and configures pluto, and pluto configures Linux IPsec via XFRM in the OpenShift cluster.

4. Analysing IPSec configuration in Openshift cluster deployment

The status of IPsec can be inspected by running the OVS commands in the ovn-ipsec container. For example to show the ipsec tunnels configured in our cluster:

oc -n openshift-ovn-kubernetes exec -it ovn-ipsec-q4mwj -c ovn-ipsec -- bash
[root@worker0 ~]#

[root@worker0 ~]# ovs-appctl -t ovs-monitor-ipsec tunnels/show | grep CONFIGURED
Interface name: ovn-ab2a7c-0 v1 (CONFIGURED)
Interface name: ovn-04e0df-0 v1 (CONFIGURED)
Interface name: ovn-91fa72-0 v1 (CONFIGURED)
Interface name: ovn-9469f8-0 v1 (CONFIGURED)

If we can pay attention to one of the interfaces used in the ipsec tunneling, we can check tons of useful information:

[root@worker0 ~]# ovs-appctl -t ovs-monitor-ipsec tunnels/show
Interface name: ovn-ab2a7c-0 v1 (CONFIGURED)
  Tunnel Type:    geneve
  Local IP:       %defaultroute
  Remote IP:      192.168.7.23
  Address Family: IPv4
  SKB mark:       None
  Local cert:     /etc/openvswitch/keys/ipsec-cert.pem
  Local name:     2dc40cb7-845e-4326-9d1d-340f9961d76c
  Local key:      /etc/openvswitch/keys/ipsec-privkey.pem
  Remote cert:    None
  Remote name:    ab2a7cc3-15a3-420b-a81f-d066c38b1c02
  CA cert:        /etc/openvswitch/keys/ipsec-cacert.pem
  PSK:            None
  Ofport:         1
  CFM state:      Disabled
Kernel policies installed:
  src 192.168.7.11/32 dst 192.168.7.23/32 proto udp dport 6081
  src 192.168.7.11/32 dst 192.168.7.23/32 proto udp dport 6081
  src 192.168.7.11/32 dst 192.168.7.23/32 proto udp sport 6081
  src 192.168.7.11/32 dst 192.168.7.23/32 proto udp sport 6081
Kernel security associations installed:
  sel src 192.168.7.23/32 dst 192.168.7.11/32 proto udp sport 6081
  sel src 192.168.7.11/32 dst 192.168.7.23/32 proto udp dport 6081
  sel src 192.168.7.23/32 dst 192.168.7.11/32 proto udp sport 6081
  sel src 192.168.7.11/32 dst 192.168.7.23/32 proto udp dport 6081
  sel src 192.168.7.23/32 dst 192.168.7.11/32 proto udp dport 6081
  sel src 192.168.7.11/32 dst 192.168.7.23/32 proto udp sport 6081
IPsec connections that are active:
  000 #8: "ovn-ab2a7c-0-in-1" esp.30da143@192.168.7.23 esp.9b00b718@192.168.7.11 Traffic: ESPin=8MB ESPout=0B! ESPmax=0B
  000 #15: "ovn-ab2a7c-0-out-1" esp.c7a82264@192.168.7.23 esp.bf5d5971@192.168.7.11 Traffic: ESPin=0B ESPout=3MB! ESPmax=0B

in this case, we’re checking the ipsec connections that are active in this specific interface, among other useful information like the remote IP, the certs, the IKE specs, etc

Managed internally, the Cluster Network Operator creates a self-signed CA certificate and then handles certificate signing requests from each node when they are booted. Each signed cert is shared with the other nodes between which secure communication is allowed.

The status of the ipsec can also be checked with the ipsec/status option in the ovs-appctl command:

[root@worker0 ~]# ovs-appctl -t ovs-monitor-ipsec ipsec/status
{'ovn-04e0df-0': {'ovn-04e0df-0-in-1': '000 #4: "ovn-04e0df-0-in-1" esp.adf6cbe@192.168.7.21 esp.79a9c45@192.168.7.11 Traffic: ESPin=13MB ESPout=0B! ESPmax=0B', 'ovn-04e0df-0-out-1': '000 #13: "ovn-04e0df-0-out-1" esp.5b4ed2ae@192.168.7.21 esp.e4452c0a@192.168.7.11 Traffic: ESPin=0B ESPout=5MB! ESPmax=0B'}, 'ovn-91fa72-0': {'ovn-91fa72-0-in-1': '000 #2: "ovn-91fa72-0-in-1" esp.790d7cb5@192.168.7.22 esp.ad252497@192.168.7.11 Traffic: ESPin=20MB ESPout=0B! ESPmax=0B', 'ovn-91fa72-0-out-1': '000 #11: "ovn-91fa72-0-out-1" esp.375fb81c@192.168.7.22 esp.50e8f4f4@192.168.7.11 Traffic: ESPin=0B ESPout=2193MB! ESPmax=0B'}, 'ovn-9469f8-0': {'ovn-9469f8-0-in-1': '000 #20: "ovn-9469f8-0-in-1" esp.7441c038@192.168.7.12 esp.1fdc5612@192.168.7.11 Traffic: ESPin=24MB ESPout=0B! ESPmax=0B', 'ovn-9469f8-0-out-1': '000 #18: "ovn-9469f8-0-out-1" esp.bdc12ddd@192.168.7.12 esp.55222499@192.168.7.11 Traffic: ESPin=0B ESPout=127MB! ESPmax=0B'}, 'ovn-ab2a7c-0': {'ovn-ab2a7c-0-in-1': '000 #8: "ovn-ab2a7c-0-in-1" esp.30da143@192.168.7.23 esp.9b00b718@192.168.7.11 Traffic: ESPin=9MB ESPout=0B! ESPmax=0B', 'ovn-ab2a7c-0-out-1': '000 #15: "ovn-ab2a7c-0-out-1" esp.c7a82264@192.168.7.23 esp.bf5d5971@192.168.7.11 Traffic: ESPin=0B ESPout=3MB! ESPmax=0B'}}

We can get more information about the ipsec status with a typical libreswan command will also run in this container:

[root@worker0 ~]# ipsec status
000 using kernel interface: xfrm
000
000 interface lo UDP [::1]:500
000 interface lo UDP 127.0.0.1:4500
000 interface lo UDP 127.0.0.1:500
000 interface br-ex UDP 192.168.7.11:4500
000 interface br-ex UDP 192.168.7.11:500
000 interface ovn-k8s-mp0 UDP 10.254.3.2:4500
000 interface ovn-k8s-mp0 UDP 10.254.3.2:500
....
000 #8: "ovn-ab2a7c-0-in-1":500 STATE_V2_ESTABLISHED_CHILD_SA (IPsec SA established); EVENT_SA_REKEY in 25217s; newest IPSEC; eroute owner; isakmp#7; idle;
000 #8: "ovn-ab2a7c-0-in-1" esp.30da143@192.168.7.23 esp.9b00b718@192.168.7.11 Traffic: ESPin=9MB ESPout=0B! ESPmax=0B
000 #9: "ovn-ab2a7c-0-out-1":500 STATE_V2_ESTABLISHED_CHILD_SA (IPsec SA established); EVENT_SA_REKEY in 25482s; isakmp#7; idle;
000 #9: "ovn-ab2a7c-0-out-1" esp.dec7ae77@192.168.7.23 esp.8941d9a5@192.168.7.11 Traffic: ESPin=0B ESPout=0B! ESPmax=0B
000 #15: "ovn-ab2a7c-0-out-1":500 STATE_V2_ESTABLISHED_CHILD_SA (IPsec SA established); EVENT_SA_REKEY in 25902s; newest IPSEC; eroute owner; isakmp#7; idle;
000 #15: "ovn-ab2a7c-0-out-1" esp.c7a82264@192.168.7.23 esp.bf5d5971@192.168.7.11 Traffic: ESPin=0B ESPout=3MB! ESPmax=0B
000
000 Bare Shunt list:
000

NOTE: Libreswan is a free software implementation of the most widely supported and standardized VPN protocol using “IPsec” and the Internet Key Exchange (“IKE”), and it’s used in the “pluto” component that it’s a Libreswan IKE Daemon.

4.1 Certificates in IPSec

The Cluster Network Operator (CNO) generates a self-signed X.509 certificate authority (CA) that is used by IPsec for encryption. Certificate signing requests (CSRs) from each node are automatically fulfilled by the CNO.

The CA is valid for 10 years. The individual node certificates are valid for 5 years and are automatically rotated after 4 1/2 years elapse.

Local certificates and keys used by the IPSec can be found in the ovn-ipsec pod:

# oc -n openshift-ovn-kubernetes exec -it ovn-ipsec-q4mwj -c ovn-ipsec -- bash
[root@worker0 ~]# ovs-vsctl get open . other_config:ca_cert
"/etc/openvswitch/keys/ipsec-cacert.pem"

[root@worker0 ~]# ovs-vsctl get open . other_config:certificate
"/etc/openvswitch/keys/ipsec-cert.pem"

[root@worker0 ~]# ovs-vsctl get open . other_config:private_key
"/etc/openvswitch/keys/ipsec-privkey.pem"

[root@worker0 ~]# ls -al /etc/openvswitch/keys
total 24
drwxr-xr-x. 2 root root   98 Jul 29 15:01 .
drwxr-xr-x. 3 root root   18 Jul 29 15:01 ..
-rw-------. 1 root root 4392 Jul 29 15:01 ipsec-cacert.pem
-rw-------. 1 root root 4353 Jul 29 15:01 ipsec-cert.pem
-rw-------. 1 root root 1679 Jul 29 15:01 ipsec-privkey.pem
-rw-------. 1 root root 3697 Jul 29 15:01 ipsec-req.pem

To check the cert and the keys, and to see if are configured correctly you can list them with ovs-vsctl list open:

[root@worker0 ~]# ovs-vsctl list open .
_uuid               : 2e86cfd2-3d2f-4190-a910-b7995ffdd97b
bridges             : [b2971eed-d6a0-490d-a6f6-ddcb746bb764, c3a5ef44-66a0-4f42-968d-d0630a962ff3]
cur_cfg             : 64
datapath_types      : [netdev, system]
datapaths           : {}
db_version          : "8.2.0"
dpdk_initialized    : false
dpdk_version        : "DPDK 20.11.1"
external_ids        : {hostname=worker0.ocp4.rober.lab, ovn-bridge-mappings="physnet:br-ex", ovn-encap-ip="192.168.7.11", ovn-encap-type=geneve, ovn-monitor-all="true", ovn-openflow-probe-interval="180", ovn-remote="ssl:192.168.7.21:9642,ssl:192.168.7.22:9642,ssl:192.168.7.23:9642", ovn-remote-probe-interval="30000", rundir="/var/run/openvswitch", system-id="2dc40cb7-845e-4326-9d1d-340f9961d76c"}
iface_types         : [bareudp, erspan, geneve, gre, gtpu, internal, ip6erspan, ip6gre, lisp, patch, stt, system, tap, vxlan]
manager_options     : []
next_cfg            : 64
other_config        : {ca_cert="/etc/openvswitch/keys/ipsec-cacert.pem", certificate="/etc/openvswitch/keys/ipsec-cert.pem", private_key="/etc/openvswitch/keys/ipsec-privkey.pem"}
ovs_version         : "2.15.1"
ssl                 : 1321d39c-b29e-4e0d-809e-0676347a2849
statistics          : {}
system_type         : rhcos
system_version      : "4.8"

As you can check this command shows also interesting information about the versions used, the keys used, bridges and datapaths, etc

5. Traffic Encryption using IPSec in OpenShift

With IPSec enabled, all Geneve traffic is encrypted with AES_GCM_16_256 using IPsec transport mode. The AES_GCM_16_256 encryption used can only be enabled at installation time (FIPS compliance)

The logs in the ovn-ipsec container can also have useful information about this encryption and can be examined at:

[root@worker0 ~]# tail -f /var/log/openvswitch/libreswan.log
Jul 29 15:02:06.832185: "ovn-9469f8-0-in-1" #20: sent CREATE_CHILD_SA request for new IPsec SA
Jul 29 15:02:06.838331: "ovn-9469f8-0-in-1" #20: negotiated connection [192.168.7.11-192.168.7.11:6081-6081 17] -> [192.168.7.12-192.168.7.12:0-65535 17]
Jul 29 15:02:06.838351: "ovn-9469f8-0-in-1" #20: IPsec SA established transport mode {ESP=>0x7441c038 <0x1fdc5612 xfrm=AES_GCM_16_256-NONE-MODP2048 NATOA=none NATD=none DPD=passive}

5.1 Traffic Encryption Analysis with IPSec Enabled

To check that effectively the traffic between the nodes it’s encrypted we will perform a tcpdump in an OpenShift Cluster that have IPSec enabled.

When IPSec it’s enabled there should be ‘ESP’ traffic flowing between nodes and no “Geneve” traffic.

  • Let’s check in one OpenShift node the traffic flow with the tcpdump command:
[root@helper ~]# oc debug node/worker0.ocp4.rober.lab -- tcpdump esp
Starting pod/worker0ocp4roberlab-debug ...
To use host binaries, run `chroot /host`
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-ex, link-type EN10MB (Ethernet), capture size 262144 bytes
15:49:35.997554 IP worker0.ocp4.rober.lab > master2.ocp4.rober.lab: ESP(spi=0xc7a82264,seq=0x3e7a), length 1472
15:49:35.997618 IP worker0.ocp4.rober.lab > master2.ocp4.rober.lab: ESP(spi=0xc7a82264,seq=0x3e7b), length 432
15:49:35.997847 IP worker1.ocp4.rober.lab > worker0.ocp4.rober.lab: ESP(spi=0x1fdc5612,seq=0xf590), length 132
15:49:35.997996 IP worker0.ocp4.rober.lab > worker1.ocp4.rober.lab: ESP(spi=0xbdc12ddd,seq=0x2161b), length 132
15:49:35.998317 IP worker1.ocp4.rober.lab > worker0.ocp4.rober.lab: ESP(spi=0x1fdc5612,seq=0xf591), length 644
15:49:35.998402 IP worker0.ocp4.rober.lab > worker1.ocp4.rober.lab: ESP(spi=0xbdc12ddd,seq=0x2161c), length 124
15:49:35.998425 IP master2.ocp4.rober.lab > worker0.ocp4.rober.lab: ESP(spi=0x9b00b718,seq=0x4bfb), length 136

If you pay attention to the output of the tcpdump, we can see the esp in each communication between the nodes of our cluster (worker / masters).

ESP is the acronym of Encapsulating Security Payload. It provides origin authenticity through source authentication, data integrity through hash functions and confidentiality through encryption protection for IP packets. ESP also supports encryption-only and authentication-only configurations, but using encryption without authentication is strongly discouraged because it is insecure.

5.2 Traffic Analysis with IPSec NOT Enabled

In another cluster that have not enabled the IPSec perform the same tcpdump (but without filtering by ESP):

oc debug node/compute-0 -- tcpdump

15:51:13.515446 IP compute-0.compute.local.4194 > master-2.compute.local.geneve: Geneve, Flags [C], vni 0x4, options [8 bytes]: IP compute-0.38062 > 10.129.0.40.xmltec-xmlmail: Flags [S], seq 474331479, win 27200, options [mss 1360,sackOK,TS val 3574026533 ecr 0,nop,wscale 7], length 0
15:51:13.515651 IP master-2.compute.local.29614 > compute-0.compute.local.geneve: Geneve, Flags [C], vni 0xd, options [8 bytes]: IP 10.129.0.40.xmltec-xmlmail > compute-0.38062: Flags [S.], seq 944206554, ack 474331480, win 26960, options [mss 1360,sackOK,TS val 1969320340 ecr 3574026533,nop,wscale 7], length 0
15:51:13.515756 IP compute-0.compute.local.4194 > master-2.compute.local.geneve: Geneve, Flags [C], vni 0x4, options [8 bytes]: IP compute-0.38062 > 10.129.0.40.xmltec-xmlmail: Flags [P.], seq 1:518, ack 1, win 213, options [nop,nop,TS val 3574026533 ecr 1969320340], length 517

As we can see the traffic shows Geneve, that is not encrypted by default so we can see the traffic flows and we can not ensure that the traffic is not tampered.

And with that, we completed our analysis of IPSec in an OpenShift cluster!

NOTE: Opinions expressed in this blog are my own and do not necessarily reflect that of the company I work for.

Stay tuned and happy OpenShifting!