Openshift IPSEC N/S
Contents
Note
In this POC, we will cover how to configure an IPSEC communication from OCP to a RHEL node
Configuration RHEL node side
Requirements
butane libreswan
dns install -y butane libreswan
Create CA and certs
Note
For simplicty, we’ell use the same certificate with SAN matching both side
mkdir ca certs private
openssl genrsa -out ca/ca.key.pem 2048
openssl req -x509 -new -nodes -key ca/ca.key.pem -sha256 -days 3650 -out ca/ca.crt.pem -subj "/CN=IPsec Test CA/O=MyOrg/OU=MyUnit/L=MyCity/ST=MyState/C=US"
openssl genrsa -out private/hosts.key.pem 2048
cat > openssl.cnf <<EOF
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[ req_distinguished_name ]
C = US
ST = MyState
L = MyCity
O = MyOrg
OU = MyUnit
CN = worker-n.example.com
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = worker-n.example.com #CHANGEME
DNS.2 = rhel-remote.example.com #CHANGEME
EOF
openssl req -new -key private/hosts.key.pem -out certs/hosts.csr.pem -config openssl.cnf
openssl x509 -req -in certs/hosts.csr.pem -CA ca/ca.crt.pem -CAkey ca/ca.key.pem -CAcreateserial -out certs/hosts.crt.pem -days 365 -sha256 -extfile <(printf "subjectAltName=DNS:openshift-host.example.com,DNS:rhel-host.example.com")
openssl pkcs12 -export -out certs/hosts.p12 -inkey private/hosts.key.pem -in certs/hosts.crt.pem -certfile ca/ca.crt.pem -name "allnodes"
Import cert on RHEL node
ipsec initnss
ipsec import certs/hosts.p12
# Verify import is successfull with the name provided ; here "allnodes"
certutil -L -d sql:/var/lib/ipsec/nss/
Certificate Nickname Trust Attributes
SSL,S/MIME,JAR/XPI
allnodes u,u,u
IPsec Test CA - MyOrg CT,
IPSEC conf
cat > /etc/ipsec.d/poc.conf << EOF
conn my-host-to-host-vpn
left=10.8.109.144 # CAHNGEME This is the RHEL node IP
leftid=%fromcert
leftcert="allnodes"
right=10.8.51.222 # CHANGEME This is the OCP node IP
rightid=%fromcert
authby=rsasig
ikev2=yes
ike=aes256-sha2
ikelifetime=8h
esp=aes_gcm256
auto=start
type=transport
EOF
Start ipsec.service
systemctl start ipsec.service
Configuration OCP side
Requirements
Note
Install nmstate operator
NNCP
cat > ipsec_nncp.yaml << EOF
apiVersion: nmstate.io/v1
kind: NodeNetworkConfigurationPolicy
metadata:
name: ipsec-config
spec:
nodeSelector:
kubernetes.io/hostname: mmayeras-tqhkk-worker-0-b4ktk
desiredState:
interfaces:
- name: test-ipsec
type: ipsec
libreswan:
left: worker-n.example.com # CHANGEME
leftid: '%fromcert'
leftrsasigkey: '%cert'
leftcert: left_server
leftmodecfgclient: false
right: rhel-host.example.com # CHANGEME
rightid: '%fromcert'
rightrsasigkey: '%cert'
rightsubnet: 10.x.x.x/xx # CHANGEME
ikev2: insist
type: transport
esp: aes_gcm256
ike: aes256-sha2;dh20
EOF
oc apply -f ipsec_nncp.yaml
Machine config for certificate import
cat > 99-ipsec-worker-endpoint-config.bu << EOF
variant: openshift
version: 4.18.0
metadata:
name: 99-worker-import-certs
labels:
machineconfiguration.openshift.io/role: worker
systemd:
units:
- name: ipsec-import.service
enabled: true
contents: |
[Unit]
Description=Import external certs into ipsec NSS
Before=ipsec.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ipsec-addcert.sh
RemainAfterExit=false
StandardOutput=journal
[Install]
WantedBy=multi-user.target
storage:
files:
- path: /etc/pki/certs/ca.pem
mode: 0400
overwrite: true
contents:
local: ca/ca.crt.pem
- path: /etc/pki/certs/hosts.p12
mode: 0400
overwrite: true
contents:
local: certs/hosts.p12
- path: /usr/local/bin/ipsec-addcert.sh
mode: 0740
overwrite: true
contents:
inline: |
#!/bin/bash -e
echo "importing cert to NSS"
certutil -A -n "CA" -t "CT,C,C" -d /var/lib/ipsec/nss/ -i /etc/pki/certs/ca.pem
pk12util -W "" -i /etc/pki/certs/hosts.p12 -d /var/lib/ipsec/nss/
certutil -M -n "allnodes" -t "u,u,u" -d /var/lib/ipsec/nss/
EOF
butane -d . 99-ipsec-worker-endpoint-config.bu -o ./99-ipsec-worker-endpoint-config.yaml
oc apply -f
Warning
Ensure thne name used in the certutil command matches the name used in the openssl pkcs12 -export command used previously
Note
Waint until end of machine config rollout
Check config
Ensure tunnel is active
After rollout OCP side, the tunnel should be up
[root@bastion ipsec]# ipsec status
000 Total IPsec connections: loaded 1, active 1
000
000 State Information: DDoS cookies not required, Accepting new IKE connections
000 IKE SAs: total(1), half-open(0), open(0), authenticated(1), anonymous(0)
000 IPsec SAs: total(2), authenticated(2), anonymous(0)
000
000 #1: "my-host-to-host-vpn":500 STATE_V2_ESTABLISHED_IKE_SA (established IKE SA); REKEY in 27456s; REPLACE in 28087s; newest; idle;
000 #2: "my-host-to-host-vpn":500 STATE_V2_ESTABLISHED_CHILD_SA (established Child SA); REKEY in 27499s; REPLACE in 28087s; IKE SA #1; idle;
000 #2: "my-host-to-host-vpn" esp.248d1071@10.8.51.222 esp.4e68b7a9@10.8.109.144 Traffic: ESPin=0B ESPout=0B ESPmax=2^63B
000 #3: "my-host-to-host-vpn":500 STATE_V2_ESTABLISHED_CHILD_SA (established Child SA); REKEY in 27080s; REPLACE in 28100s; newest; eroute owner; IKE SA #1; idle;
000 #3: "my-host-to-host-vpn" esp.a3a6c6eb@10.8.51.222 esp.8fd9bdcd@10.8.109.144 Traffic: ESPin=128B ESPout=128B ESPmax=2^63B
000
000 Bare Shunt list:
000
[root@bastion ipsec]# ipsec trafficstatus
006 #2: "my-host-to-host-vpn", type=ESP, add_time=1750173085, inBytes=0, outBytes=0, maxBytes=2^63B, id='C=US, ST=MyState, L=MyCity, O=MyOrg, OU=MyUnit, CN=worker-n'
006 #3: "my-host-to-host-vpn", type=ESP, add_time=1750173098, inBytes=128, outBytes=128, maxBytes=2^63B, id='C=US, ST=MyState, L=MyCity, O=MyOrg, OU=MyUnit, CN=worker-n'
If not, try to enable it again until 000 Total IPsec connections: loaded 1, active 1
ipsec auto --up my-host-to-host-vpn
ipsec auto --status
Verify with a packet capture
Note
The Encapsulation Security Payload (ESP) defined in RFC 4303 has IP protocol number 50
Run a tcpdump capture on one of the nodes (OCP node or RHEL node) Then ping from node to the other
tcpdump -i any -n -s 0 'ip proto 50'
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
17:45:53.567216 ens192 In IP 10.8.51.222 > 10.8.109.144: ESP(spi=0x4c5a8df6,seq=0xc), length 100
17:45:53.567316 ens192 Out IP 10.8.109.144 > 10.8.51.222: ESP(spi=0xca19c2c5,seq=0xc), length 100
17:45:54.568259 ens192 In IP 10.8.51.222 > 10.8.109.144: ESP(spi=0x4c5a8df6,seq=0xd), length 100
17:45:54.568334 ens192 Out IP 10.8.109.144 > 10.8.51.222: ESP(spi=0xca19c2c5,seq=0xd), length 100
^C