diff --git a/NEWS b/NEWS index ab664ef7eca..9c074c6d47a 100644 --- a/NEWS +++ b/NEWS @@ -47,6 +47,10 @@ Post-v3.4.0 - Tunnels: * LISP and STT tunnel port types are deprecated and will be removed in the next release. + - IPsec: + * New option '--root-ipsec-conf' for ovs-monitor-ipsec with Libreswan + to allow cases where '--ipsec-conf' is not the main ipsec.conf, but + included from it. The value should be the path to the main ipsec.conf. v3.4.0 - 15 Aug 2024 diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in index 6c60c07e3f9..41ed2392055 100755 --- a/ipsec/ovs-monitor-ipsec.in +++ b/ipsec/ovs-monitor-ipsec.in @@ -425,19 +425,24 @@ conn prevent_unencrypted_vxlan class LibreSwanHelper(object): """This class does LibreSwan specific configurations.""" - CONF_HEADER = """%s + CONF_DEFAULT_HEADER = """\ config setup uniqueids=yes -conn %%default - keyingtries=%%forever +conn %default +""" + + CONN_CONF_BASE = """\ + keyingtries=%forever type=transport auto=route +""" + + CONN_CONF_CRYPTO = """\ ike=aes_gcm256-sha2_256 esp=aes_gcm256 ikev2=insist - -""" % (FILE_HEADER) +""" SHUNT_POLICY = """conn prevent_unencrypted_gre type=drop @@ -524,6 +529,9 @@ conn prevent_unencrypted_vxlan else "/run/pluto/pluto.ctl") self.IPSEC_CONF = libreswan_root_prefix + ipsec_conf + self.ROOT_IPSEC_CONF = self.IPSEC_CONF + if args.root_ipsec_conf: + self.ROOT_IPSEC_CONF = args.root_ipsec_conf self.IPSEC_SECRETS = libreswan_root_prefix + ipsec_secrets self.IPSEC_D = "sql:" + libreswan_root_prefix + ipsec_d self.IPSEC_CTL = libreswan_root_prefix + ipsec_ctl @@ -531,8 +539,10 @@ conn prevent_unencrypted_vxlan self.conns_not_active = set() self.last_refresh = time.time() self.secrets_file = None + self.use_default_conn = self.IPSEC_CONF == self.ROOT_IPSEC_CONF vlog.dbg("Using: " + self.IPSEC) vlog.dbg("Configuration file: " + self.IPSEC_CONF) + vlog.dbg("Root configuration file: " + self.ROOT_IPSEC_CONF) vlog.dbg("Secrets file: " + self.IPSEC_SECRETS) vlog.dbg("ipsec.d: " + self.IPSEC_D) vlog.dbg("Pluto socket: " + self.IPSEC_CTL) @@ -543,7 +553,12 @@ conn prevent_unencrypted_vxlan self._nss_clear_database() f = open(self.IPSEC_CONF, "w") - f.write(self.CONF_HEADER) + f.write(FILE_HEADER) + if self.use_default_conn: + f.write(self.CONF_DEFAULT_HEADER) + f.write(self.CONN_CONF_BASE) + f.write(self.CONN_CONF_CRYPTO) + f.write("\n") f.close() f = open(self.IPSEC_SECRETS, "w") @@ -556,7 +571,13 @@ conn prevent_unencrypted_vxlan def config_init(self): self.conf_file = open(self.IPSEC_CONF, "w") self.secrets_file = open(self.IPSEC_SECRETS, "w") - self.conf_file.write(self.CONF_HEADER) + self.conf_file.write(FILE_HEADER) + if self.use_default_conn: + self.conf_file.write(self.CONF_DEFAULT_HEADER) + self.conf_file.write(self.CONN_CONF_BASE) + self.conf_file.write(self.CONN_CONF_CRYPTO) + self.conf_file.write("\n") + self.secrets_file.write(FILE_HEADER) def config_global(self, monitor): @@ -614,6 +635,10 @@ conn prevent_unencrypted_vxlan if tunnel.conf["address_family"] == "IPv6": auth_section = self.IPV6_CONN + auth_section + if not self.use_default_conn: + auth_section = self.CONN_CONF_BASE + auth_section + auth_section = self.CONN_CONF_CRYPTO + auth_section + if "custom_options" in tunnel.conf: for key, value in tunnel.conf["custom_options"].items(): auth_section += "\n " + key + "=" + value @@ -638,7 +663,7 @@ conn prevent_unencrypted_vxlan def refresh(self, monitor): vlog.info("Refreshing LibreSwan configuration") run_command(self.IPSEC_AUTO + ["--ctlsocket", self.IPSEC_CTL, - "--config", self.IPSEC_CONF, + "--config", self.ROOT_IPSEC_CONF, "--rereadsecrets"], "re-read secrets") @@ -708,43 +733,43 @@ conn prevent_unencrypted_vxlan if monitor.conf_in_use["skb_mark"] != monitor.conf["skb_mark"]: if monitor.conf["skb_mark"]: run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", "--asynchronous", "prevent_unencrypted_gre"]) run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", "--asynchronous", "prevent_unencrypted_geneve"]) run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", "--asynchronous", "prevent_unencrypted_stt"]) run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", "--asynchronous", "prevent_unencrypted_vxlan"]) else: run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_gre"]) run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_geneve"]) run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_stt"]) run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_vxlan"]) @@ -838,13 +863,13 @@ conn prevent_unencrypted_vxlan self.conns_not_active.discard(conn) run_command(self.IPSEC_AUTO + ["--ctlsocket", self.IPSEC_CTL, - "--config", self.IPSEC_CONF, + "--config", self.ROOT_IPSEC_CONF, "--delete", conn], "delete %s" % conn) def _start_ipsec_connection(self, conn, action): asynchronous = [] if action == "add" else ["--asynchronous"] ret, pout, perr = run_command(self.IPSEC_AUTO + - ["--config", self.IPSEC_CONF, + ["--config", self.ROOT_IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--" + action, *asynchronous, conn], @@ -1388,7 +1413,11 @@ def main(): help="Don't restart the IKE daemon on startup.") parser.add_argument("--ipsec-conf", metavar="IPSEC-CONF", help="Use DIR/IPSEC-CONF as location for " - " ipsec.conf (libreswan only).") + " ipsec.conf to overwrite (libreswan only).") + parser.add_argument("--root-ipsec-conf", metavar="ROOT-IPSEC-CONF", + help="The read-only root configuration file that" + " 'include's the one provided in --ipsec-conf. Will" + " be used to call ipsec commands (libreswan only).") parser.add_argument("--ipsec-d", metavar="IPSEC-D", help="Use DIR/IPSEC-D as location for " " ipsec.d (libreswan only).") diff --git a/tests/system-ipsec.at b/tests/system-ipsec.at index 4ab384d89c5..9bae7279942 100644 --- a/tests/system-ipsec.at +++ b/tests/system-ipsec.at @@ -20,7 +20,8 @@ m4_define([START_PLUTO], [ --rundir $ovs_base/$1], [0], [], [stderr]) ]) -dnl IPSEC_ADD_NODE([namespace], [device], [address], [peer address])) +dnl IPSEC_ADD_NODE([namespace], [device], [address], [peer address], +dnl [custom-ipsec-conf]) dnl dnl Creates a dummy host that acts as an IPsec endpoint. Creates host in dnl 'namespace' and attaches a veth 'device' to 'namespace' to act as the host @@ -28,7 +29,10 @@ dnl NIC. Assigns 'address' to 'device' and adds the other end of veth 'device' t dnl 'br0' which is an OVS bridge in the default namespace acting as an underlay dnl switch. Sets the default gateway of 'namespace' to 'peer address'. dnl -dnl Starts all daemons in 'namespace' that are required for IPsec +dnl Starts all daemons in 'namespace' that are required for IPsec. +dnl +dnl If 'custom-ipsec-conf' is provided, then it will be used as --ipsec-conf +dnl and the ipsec.conf will be used as --root-ipsec-conf. m4_define([IPSEC_ADD_NODE], [ADD_NAMESPACES($1) dnl Disable DAD. We know we wont get duplicates on this underlay network. @@ -56,6 +60,12 @@ m4_define([IPSEC_ADD_NODE], [0], [], [stderr]) on_exit "kill_ovs_vswitchd `cat $ovs_base/$1/vswitchd.pid`" + m4_if([$5], [], [], [ + AT_CHECK([echo "## A read-only root config. ##" > $ovs_base/$1/ipsec.conf]) + AT_CHECK([echo "include $ovs_base/$1/$5" >> $ovs_base/$1/ipsec.conf]) + ]) + AT_CHECK + dnl Start pluto START_PLUTO([$1]) on_exit 'kill $(cat $ovs_base/$1/pluto.pid)' @@ -63,7 +73,9 @@ m4_define([IPSEC_ADD_NODE], dnl Start ovs-monitor-ipsec NS_CHECK_EXEC([$1], [ovs-monitor-ipsec unix:${OVS_RUNDIR}/$1/db.sock\ --pidfile=${OVS_RUNDIR}/$1/ovs-monitor-ipsec.pid --ike-daemon=libreswan\ - --ipsec-conf=$ovs_base/$1/ipsec.conf --ipsec-d=$ovs_base/$1/ipsec.d \ + --ipsec-conf=$ovs_base/$1/m4_if([$5], [], [ipsec.conf], [$5]) \ + m4_if([$5], [], [], [--root-ipsec-conf=$ovs_base/$1/ipsec.conf]) \ + --ipsec-d=$ovs_base/$1/ipsec.d \ --ipsec-secrets=$ovs_base/$1/secrets \ --log-file=$ovs_base/$1/ovs-monitor-ipsec.log \ --ipsec-ctl=$ovs_base/$1/pluto.ctl \ @@ -75,8 +87,10 @@ m4_define([IPSEC_ADD_NODE], [ovs-vsctl --db unix:$ovs_base/$1/db.sock add-br br-ipsec \ -- set-controller br-ipsec punix:$ovs_base/br-ipsec.$1.mgmt])] ) -m4_define([IPSEC_ADD_NODE_LEFT], [IPSEC_ADD_NODE(left, p0, $1, $2)]) -m4_define([IPSEC_ADD_NODE_RIGHT], [IPSEC_ADD_NODE(right, p1, $1, $2)]) +m4_define([IPSEC_ADD_NODE_LEFT], + [IPSEC_ADD_NODE(left, p0, $1, $2, [$3])]) +m4_define([IPSEC_ADD_NODE_RIGHT], + [IPSEC_ADD_NODE(right, p1, $1, $2, [$3])]) dnl OVS_VSCTL([namespace], [sub-command]) dnl @@ -411,6 +425,40 @@ CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP +AT_SETUP([IPsec -- Libreswan (ipv4, geneve, custom conf)]) +AT_KEYWORDS([ipsec libreswan ipv4 geneve psk custom conf]) +dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 +dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 + +CHECK_LIBRESWAN() +OVS_TRAFFIC_VSWITCHD_START() +IPSEC_SETUP_UNDERLAY() + +dnl Set up hosts. +IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2, [custom.conf]) +IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1, [custom.conf]) + +dnl Set up IPsec tunnel on 'left' host. +IPSEC_ADD_TUNNEL_LEFT([geneve], + [options:remote_ip=10.1.1.2 options:psk=swordfish]) + +dnl Set up IPsec tunnel on 'right' host. +IPSEC_ADD_TUNNEL_RIGHT([geneve], + [options:remote_ip=10.1.1.1 options:psk=swordfish]) +CHECK_ESP_TRAFFIC + +dnl Check that custom.conf doesn't include default section, but has +dnl ike and esp configuration per connection. +AT_CHECK([grep -q "conn %default" $ovs_base/left/custom.conf], [1]) +AT_CHECK([grep -c -E "(ike|ikev2|esp)=" $ovs_base/left/custom.conf], [0], [6 +]) +AT_CHECK([grep -q "conn %default" $ovs_base/right/custom.conf], [1]) +AT_CHECK([grep -c -E "(ike|ikev2|esp)=" $ovs_base/right/custom.conf], [0], [6 +]) + +OVS_TRAFFIC_VSWITCHD_STOP() +AT_CLEANUP + AT_SETUP([IPsec -- Libreswan NxN geneve tunnels + reconciliation]) AT_KEYWORDS([ipsec libreswan scale reconciliation]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645