..
      Licensed under the Apache License, Version 2.0 (the "License"); you may
      not use this file except in compliance with the License. You may obtain
      a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
      License for the specific language governing permissions and limitations
      under the License.

      Convention for heading levels in Open vSwitch documentation:

      =======  Heading 0 (reserved for the title in a document)
      -------  Heading 1
      ~~~~~~~  Heading 2
      +++++++  Heading 3
      '''''''  Heading 4

      Avoid deeper levels because they do not render well.

==================
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 :doc:`/howto/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).

.. _install-ovs-ipsec:

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.

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.

   a) 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.

   b) 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.)

   c) 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 :doc:`/internals/security`.

If bug does not have security implications, then report it according to
instructions in :doc:`/internals/bugs`.

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