# 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 with XFRM support. 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:

       # 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.

0. 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

1. 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

2. 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.

3. 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.

4. 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.