OVS IPsec Tutorial

This document provides a step-by-step guide for running IPsec tunnel in Open vSwitch. A more detailed description on OVS IPsec tunnel and its configuration modes can be found in Encrypt Open vSwitch Tunnels with IPsec.

Requirements

OVS IPsec tunnel requires Linux kernel (>= v3.10.0) and OVS out-of-tree kernel module. The compatible IKE daemons are LibreSwan (>= v3.23) and StrongSwan (>= v5.3.5).

Installing OVS and IPsec Packages

OVS IPsec has .deb and .rpm packages. You should use the right package based on your Linux distribution. This tutorial uses Ubuntu 22.04 and Fedora 32 as examples.

Ubuntu

  1. Install the related packages:

    # apt-get install openvswitch-ipsec
    

    If the installation is successful, you should be able to see the ovs-monitor-ipsec daemon is running in your system.

Fedora

  1. Install the related packages. Fedora 32 does not require installation of the out-of-tree kernel module:

    # dnf install python3-openvswitch libreswan \
                  openvswitch openvswitch-ipsec
    
  2. Install firewall rules to allow ESP and IKE traffic:

    # systemctl start firewalld
    # firewall-cmd --add-service ipsec
    

    Or to make permanent:

    # systemctl enable firewalld
    # firewall-cmd --permanent --add-service ipsec
    
  3. Run the openvswitch-ipsec service:

    # systemctl start openvswitch-ipsec.service
    

    Note

    The SELinux policies might prevent openvswitch-ipsec.service to access certain resources. You can configure SELinux to remove such restrictions.

Configuring IPsec tunnel

Suppose you want to build an IPsec tunnel between two hosts. Assume host_1’s external IP is 1.1.1.1, and host_2’s external IP is 2.2.2.2. Make sure host_1 and host_2 can ping each other via these external IPs.

  1. Set up some variables to make life easier. On both hosts, set ip_1 and ip_2 variables, e.g.:

    # ip_1=1.1.1.1
    # ip_2=2.2.2.2
    
  2. Set up OVS bridges in both hosts.

    In host_1:

    # ovs-vsctl add-br br-ipsec
    # ip addr add 192.0.0.1/24 dev br-ipsec
    # ip link set br-ipsec up
    

    In host_2:

    # ovs-vsctl add-br br-ipsec
    # ip addr add 192.0.0.2/24 dev br-ipsec
    # ip link set br-ipsec up
    
  3. Set up IPsec tunnel.

    There are three authentication methods. Choose one method to set up your IPsec tunnel and follow the steps below.

    1. Using pre-shared key:

      In host_1:

      # ovs-vsctl add-port br-ipsec tun -- \
                  set interface tun type=gre \
                                options:remote_ip=$ip_2 \
                                options:psk=swordfish
      

      In host_2:

      # ovs-vsctl add-port br-ipsec tun -- \
                  set interface tun type=gre \
                                options:remote_ip=$ip_1 \
                                options:psk=swordfish
      

      Note

      Pre-shared key (PSK) based authentication is easy to set up but less secure compared with other authentication methods. You should use it cautiously in production systems.

    2. Using self-signed certificate:

      Generate self-signed certificate in both host_1 and host_2. Then copy the certificate of host_1 to host_2 and the certificate of host_2 to host_1.

      In host_1:

      # ovs-pki req -u host_1
      # ovs-pki self-sign host_1
      # scp host_1-cert.pem $ip_2:/etc/keys/host_1-cert.pem
      

      In host_2:

      # ovs-pki req -u host_2
      # ovs-pki self-sign host_2
      # scp host_2-cert.pem $ip_1:/etc/keys/host_2-cert.pem
      

      Note

      If you use StrongSwan as IKE daemon, please move the host certificates to /etc/ipsec.d/certs/ and private key to /etc/ipsec.d/private/ so that StrongSwan has permission to access those files.

      Configure IPsec tunnel to use self-signed certificates.

      In host_1:

      # ovs-vsctl set Open_vSwitch . \
                 other_config:certificate=/etc/keys/host_1-cert.pem \
                 other_config:private_key=/etc/keys/host_1-privkey.pem
      # ovs-vsctl add-port br-ipsec tun -- \
                  set interface tun type=gre \
                         options:remote_ip=$ip_2 \
                         options:remote_cert=/etc/keys/host_2-cert.pem
      

      In host_2:

      # ovs-vsctl set Open_vSwitch . \
                 other_config:certificate=/etc/keys/host_2-cert.pem \
                 other_config:private_key=/etc/keys/host_2-privkey.pem
      # ovs-vsctl add-port br-ipsec tun -- \
                  set interface tun type=gre \
                         options:remote_ip=$ip_1 \
                         options:remote_cert=/etc/keys/host_1-cert.pem
      

      Note

      The confidentiality of the private key is very critical. Don’t copy it to places where it might be compromised. (The certificate need not be kept confidential.)

    3. Using CA-signed certificate:

      First you need to establish a public key infrastructure (PKI). Suppose you choose host_1 to host PKI.

      In host_1:

      # ovs-pki init
      

      Generate certificate requests and copy the certificate request of host_2 to host_1.

      In host_1:

      # ovs-pki req -u host_1
      

      In host_2:

      # ovs-pki req -u host_2
      # scp host_2-req.pem $ip_1:/etc/keys/host_2-req.pem
      

      Sign the certificate requests with the CA key. Copy host_2’s signed certificate and the CA certificate to host_2.

      In host_1:

      # ovs-pki sign host_1 switch
      # ovs-pki sign host_2 switch
      # scp host_2-cert.pem $ip_2:/etc/keys/host_2-cert.pem
      # scp /var/lib/openvswitch/pki/switchca/cacert.pem \
                $ip_2:/etc/keys/cacert.pem
      

      Note

      If you use StrongSwan as IKE daemon, please move the host certificates to /etc/ipsec.d/certs/, CA certificate to /etc/ipsec.d/cacerts/, and private key to /etc/ipsec.d/private/ so that StrongSwan has permission to access those files.

      Configure IPsec tunnel to use CA-signed certificate.

      In host_1:

      # ovs-vsctl set Open_vSwitch . \
              other_config:certificate=/etc/keys/host_1-cert.pem \
              other_config:private_key=/etc/keys/host_1-privkey.pem \
              other_config:ca_cert=/etc/keys/cacert.pem
      # ovs-vsctl add-port br-ipsec tun -- \
               set interface tun type=gre \
                             options:remote_ip=$ip_2 \
                             options:remote_name=host_2
      

      In host_2:

      # ovs-vsctl set Open_vSwitch . \
              other_config:certificate=/etc/keys/host_2-cert.pem \
              other_config:private_key=/etc/keys/host_2-privkey.pem \
              other_config:ca_cert=/etc/keys/cacert.pem
      # ovs-vsctl add-port br-ipsec tun -- \
               set interface tun type=gre \
                             options:remote_ip=$ip_1 \
                             options:remote_name=host_1
      

      Note

      remote_name is the common name (CN) of the signed-certificate. It must match the name given as the argument to the ovs-pki sign command. It ensures that only certificate with the expected CN can be authenticated; otherwise, any certificate signed by the CA would be accepted.

  4. Set the local_ip field in the Interface table (Optional)

    Make sure that the local_ip field in the Interface table is set to the NIC used for egress traffic.

    On host 1:

    # ovs-vsctl set Interface tun options:local_ip=$ip_1
    

    Similarly, on host 2:

    # ovs-vsctl set Interface tun options:local_ip=$ip_2
    

    Note

    It is not strictly necessary to set the local_ip field if your system only has one NIC or the default gateway interface is set to the NIC used for egress traffic.

  5. Test IPsec tunnel.

    Now you should have an IPsec GRE tunnel running between two hosts. To verify it, in host_1:

    # ping 192.0.0.2 &
    # tcpdump -ni any net $ip_2
    

    You should be able to see that ESP packets are being sent from host_1 to host_2.

Custom options

Any parameter prefixed with ipsec_ will be added to the connection profile. For example:

# ovs-vsctl set interface tun options:ipsec_encapsulation=yes

Will result in:

#  ovs-appctl -t ovs-monitor-ipsec tunnels/show
Interface name: tun v7 (CONFIGURED)
Tunnel Type:    vxlan
Local IP:       192.0.0.1
Remote IP:      192.0.0.2
Address Family: IPv4
SKB mark:       None
Local cert:     None
Local name:     None
Local key:      None
Remote cert:    None
Remote name:    None
CA cert:        None
PSK:            swordfish
Custom Options: {'encapsulation': 'yes'}

And in the following connection profiles:

conn tun-in-7
    left=192.0.0.1
    right=192.0.0.2
    authby=secret
    encapsulation=yes
    leftprotoport=udp/4789
    rightprotoport=udp

conn tun-out-7
    left=192.0.0.1
    right=192.0.0.2
    authby=secret
    encapsulation=yes
    leftprotoport=udp
    rightprotoport=udp/4789

Troubleshooting

The ovs-monitor-ipsec daemon manages and monitors the IPsec tunnel state. Use the following ovs-appctl command to view ovs-monitor-ipsec internal representation of tunnel configuration:

# ovs-appctl -t ovs-monitor-ipsec tunnels/show

If there is misconfiguration, then ovs-appctl should indicate why. For example:

Interface name: gre0 v5 (CONFIGURED) <--- Should be set to CONFIGURED.
                                          Otherwise, error message will
                                          be provided
Tunnel Type:    gre
Local IP:       %defaultroute
Remote IP:      2.2.2.2
SKB mark:       None
Local cert:     None
Local name:     None
Local key:      None
Remote cert:    None
Remote name:    None
CA cert:        None
PSK:            swordfish
Custom Options: {}
Ofport:         1          <--- Whether ovs-vswitchd has assigned Ofport
                                number to this Tunnel Port
CFM state:      Up         <--- Whether CFM declared this tunnel healthy
Kernel policies installed:
...                          <--- IPsec policies for this OVS tunnel in
                                  Linux Kernel installed by strongSwan
Kernel security associations installed:
...                          <--- IPsec security associations for this OVS
                                  tunnel in Linux Kernel installed by
                                  strongswan
IPsec connections that are active:
...                          <--- IPsec "connections" for this OVS
                                  tunnel

If you don’t see any active connections, try to run the following command to refresh the ovs-monitor-ipsec daemon:

# ovs-appctl -t ovs-monitor-ipsec refresh

You can also check the logs of the ovs-monitor-ipsec daemon and the IKE daemon to locate issues. ovs-monitor-ipsec outputs log messages to /var/log/openvswitch/ovs-monitor-ipsec.log.

Bug Reporting

If you think you may have found a bug with security implications, like

  1. IPsec protected tunnel accepted packets that came unencrypted; OR

  2. IPsec protected tunnel allowed packets to leave unencrypted;

Then report such bugs according to Security Process.

If bug does not have security implications, then report it according to instructions in Reporting Bugs.

If you have suggestions to improve this tutorial, please send a email to ovs-discuss@openvswitch.org.