Since this patch moved to using strongswan’s vici-based configuration, 
strongswan-swanctl becomes a dependency to openvswitch-ipsec package hence I 
will be resending this patch as a series of two patches; this patch in addition 
to a dependency update patch.

From: Mohammad Heib <mh...@redhat.com>
Sent: Thursday, March 17, 2022 10:15 AM
To: Emeel Hakim <eha...@nvidia.com>; d...@openvswitch.org
Cc: Roi Dayan <r...@nvidia.com>; Raed Salem <ra...@nvidia.com>
Subject: Re: [PATCH v2 1/1] ovs-monitor-ipsec: Migration from ipsec.conf to 
swanctl.conf

External email: Use caution opening links or attachments



On 2/15/22 14:13, Emeel Hakim wrote:

As strongswan moved to the modern vici-based interface,this patch

modifies ovs-monitor-ipsec to use strongswan's vici-based

configuration instead of the legacy stroke-based configuration.



Reviewed-by: Raed Salem <ra...@nvidia.com><mailto:ra...@nvidia.com>

Signed-off-by: Emeel Hakim <eha...@nvidia.com><mailto:eha...@nvidia.com>

---



v2:

fixed Reviewed-by in the commit message.

addressed comments from Mohammad Heib 
<mh...@redhat.com><mailto:mh...@redhat.com>



 ipsec/ovs-monitor-ipsec.in | 459 ++++++++++++++++++++++++++-----------

 1 file changed, 325 insertions(+), 134 deletions(-)



diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in

index a8b0705d9..582c96132 100755

--- a/ipsec/ovs-monitor-ipsec.in

+++ b/ipsec/ovs-monitor-ipsec.in

@@ -32,52 +32,6 @@ import ovs.vlog





 FILE_HEADER = "# Generated by ovs-monitor-ipsec...do not modify by hand!\n\n"

-transp_tmpl = {"gre": Template("""\

-conn $ifname-$version

-$auth_section

-    leftprotoport=gre

-    rightprotoport=gre

-

-"""), "gre64": Template("""\

-conn $ifname-$version

-$auth_section

-    leftprotoport=gre

-    rightprotoport=gre

-

-"""), "geneve": Template("""\

-conn $ifname-in-$version

-$auth_section

-    leftprotoport=udp/6081

-    rightprotoport=udp

-

-conn $ifname-out-$version

-$auth_section

-    leftprotoport=udp

-    rightprotoport=udp/6081

-

-"""), "stt": Template("""\

-conn $ifname-in-$version

-$auth_section

-    leftprotoport=tcp/7471

-    rightprotoport=tcp

-

-conn $ifname-out-$version

-$auth_section

-    leftprotoport=tcp

-    rightprotoport=tcp/7471

-

-"""), "vxlan": Template("""\

-conn $ifname-in-$version

-$auth_section

-    leftprotoport=udp/4789

-    rightprotoport=udp

-

-conn $ifname-out-$version

-$auth_section

-    leftprotoport=udp

-    rightprotoport=udp/4789

-

-""")}

 vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")

 exiting = False

 monitor = None

@@ -160,72 +114,249 @@ charon {

 }

 """ % (FILE_HEADER)



-    CONF_HEADER = """%s

-config setup

-    uniqueids=yes

+    SWANCTL_CONF_HEADER = """%s

+conn-defaults {

+      unique = replace

+      reauth_time = 0

+      version = 2

+      proposals = aes128-sha256-x25519

+}



-conn %%default

-    keyingtries=%%forever

-    type=transport

-    keyexchange=ikev2

-    auto=route

-    ike=aes256gcm16-sha256-modp2048

-    esp=aes256gcm16-modp2048

+child-defaults {

+      esp_proposals = aes256gcm16-modp2048-esn

+      mode = transport

+      policies_fwd_out = yes

+      start_action = start

+}



 """ % (FILE_HEADER)



-    CA_SECTION = """ca ca_auth

-    cacert=%s

+    CA_SECTION = """authorities {

+    ca_auth {

+        cacert=%s

+    }

+}



 """



-    SHUNT_POLICY = """conn prevent_unencrypted_gre

-    type=drop

-    leftprotoport=gre

-    mark={0}

+    SHUNT_POLICY = """connections {{

+   shunts {{

+      children {{

+         prevent_unencrypted_gre {{

+            local_ts = 0.0.0.0/0 [gre]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+         prevent_unencrypted_gre_ipv6 {{

+            local_ts = ::/0 [gre]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+         prevent_unencrypted_geneve {{

+            local_ts = 0.0.0.0/0 [udp/6081]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+         prevent_unencrypted_geneve_ipv6 {{

+            local_ts = ::/0 [udp/6081]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+         prevent_unencrypted_stt {{

+            local_ts = 0.0.0.0/0 [tcp/7471]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+         prevent_unencrypted_stt_ipv6 {{

+            local_ts = ::/0 [tcp/7471]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+         prevent_unencrypted_vxlan {{

+            local_ts = 0.0.0.0/0 [udp/4789]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+         prevent_unencrypted_vxlan_ipv6 {{

+            local_ts = ::/0 [udp/4789]

+            mark_in = {0}

+            mark_out = {0}

+            mode = drop

+            start_action = trap

+         }}

+      }}

+   }}

+}}

+"""

+    auth_tmpl = {"psk": Template("""\

+local {

+            auth = psk

+            id = $local_ip

+         }

+         remote {

+            auth = psk

+            id = $remote_ip

+         }"""),

+                 "pki_remote": Template("""\

+local {

+            auth = pubkey

+            id = $local_name

+            certs = $certificate

+        }

+        remote {

+           auth = pubkey

+           id = $remote_name

+           certs = $remote_cert

+        }"""),

+                 "pki_ca": Template("""\

+local {

+            auth = pubkey

+            id = $local_name

+            certs = $certificate

+        }

+        remote {

+          auth = pubkey

+          id = $remote_name

+        }""")}

+

+    SECRETS_SECTION = """secrets {

+        ike-$ifname {

+            id = $local_ip

+            secret = $psk

+        }

+}



-conn prevent_unencrypted_geneve

-    type=drop

-    leftprotoport=udp/6081

-    mark={0}

+"""

+    transp_tmpl = {"gre": Template("""\

+connections {

+    $ifname-$version : conn-defaults{

+        local_addrs  = $local_addrs

+        remote_addrs = $remote_ip

+

+        $auth_section

+

+        children {

+            $ifname-$version : child-defaults {

+                local_ts = $local_ip/$subnet [gre]

+                remote_ts = $remote_ip/$subnet [gre]

+            }

+        }

+    }

+}



-conn prevent_unencrypted_stt

-    type=drop

-    leftprotoport=tcp/7471

-    mark={0}

+"""), "gre64": Template("""\

+connections {

+    $ifname-$version : conn-defaults{

+        local_addrs  = $local_addrs

+        remote_addrs = $remote_ip

+

+        $auth_section

+

+        children {

+            $ifname-$version : child-defaults {

+                local_ts = $local_ip/$subnet [gre]

+                remote_ts = $remote_ip/$subnet [gre]

+            }

+        }

+    }

+}



-conn prevent_unencrypted_vxlan

-    type=drop

-    leftprotoport=udp/4789

-    mark={0}

+"""), "geneve": Template("""\

+connections {

+    $ifname-$version : conn-defaults{

+        local_addrs  = $local_addrs

+        remote_addrs = $remote_ip

+

+        $auth_section

+

+        children {

+            $ifname-in-$version : child-defaults {

+                local_ts = $local_ip/$subnet [udp/6081]

+                remote_ts = $remote_ip/$subnet [udp]

+            }

+            $ifname-out-$version : child-defaults {

+                local_ts = $local_ip/$subnet [udp]

+                remote_ts = $remote_ip/$subnet [udp/6081]

+            }

+        }



-"""

+    }

+}



-    auth_tmpl = {"psk": Template("""\

-    left=%any

-    right=$remote_ip

-    authby=psk"""),

-                 "pki_remote": Template("""\

-    left=%any

-    right=$remote_ip

-    leftid=$local_name

-    rightid=$remote_name

-    leftcert=$certificate

-    rightcert=$remote_cert"""),

-                 "pki_ca": Template("""\

-    left=%any

-    right=$remote_ip

-    leftid=$local_name

-    rightid=$remote_name

-    leftcert=$certificate""")}

+"""), "stt": Template("""\

+connections {

+    $ifname-$version : conn-defaults{

+        local_addrs  = $local_addrs

+        remote_addrs = $remote_ip

+

+        $auth_section

+

+        children {

+            $ifname-in-$version : child-defaults {

+                local_ts = $local_ip/$subnet [tcp/7471]

+                remote_ts = $remote_ip/$subnet [tcp]

+            }

+            $ifname-out-$version : child-defaults {

+                local_ts = $local_ip/$subnet [tcp]

+                remote_ts = $remote_ip/$subnet [tcp/7471]

+            }

+        }

+    }

+}

+

+"""), "vxlan": Template("""\

+connections {

+    $ifname-$version : conn-defaults{

+        local_addrs  = $local_addrs

+        remote_addrs = $remote_ip

+

+        $auth_section

+

+        children {

+            $ifname-in-$version : child-defaults {

+                local_ts = $local_ip/$subnet [udp/4789]

+                remote_ts = $remote_ip/$subnet [udp]

+            }

+            $ifname-out-$version : child-defaults {

+                local_ts = $local_ip/$subnet [udp]

+                remote_ts = $remote_ip/$subnet [udp/4789]

+            }

+        }

+    }

+}

+

+""")}



     def __init__(self, root_prefix):

-        self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf"

-        self.IPSEC = root_prefix + "/usr/sbin/ipsec"

-        self.IPSEC_CONF = root_prefix + "/etc/ipsec.conf"

-        self.IPSEC_SECRETS = root_prefix + "/etc/ipsec.secrets"

+        if os.path.exists(root_prefix + "/etc/strongswan.d/"):

+            self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf"

+        else:

+            self.CHARON_CONF = (root_prefix +

+                               "/etc/strongswan/strongswan.d/ovs.conf")

+        if os.path.exists(root_prefix + "/etc/swanctl/conf.d"):

+            self.SWANCTL_CONF = (root_prefix +

+                                "/etc/swanctl/conf.d/ovs-swanctl.conf")

+        else:

+            self.SWANCTL_CONF = (root_prefix +

+                                "/etc/strongswan/swanctl/conf.d/" +

+                                "ovs-swanctl.conf")

+        self.SYSTEMCTL = root_prefix + "/usr/bin/systemctl"

+        self.SWANCTL = root_prefix + "/usr/sbin/swanctl"

         self.conf_file = None

-        self.secrets_file = None



     def restart_ike_daemon(self):

         """This function restarts 
StrongSwan."""<mailto:@@-233,26+364,24@@connprevent_unencrypted_vxlanf.write(self.STRONGSWAN_CONF)f.close()-f=open(self.IPSEC_CONF,>

@@ -233,26 +364,24 @@ conn 
prevent_unencrypted_vxlan<mailto:@@-233,26+364,24@@connprevent_unencrypted_vxlanf.write(self.STRONGSWAN_CONF)f.close()-f=open(self.IPSEC_CONF,>

         
f.write(self.STRONGSWAN_CONF)<mailto:@@-233,26+364,24@@connprevent_unencrypted_vxlanf.write(self.STRONGSWAN_CONF)f.close()-f=open(self.IPSEC_CONF,>

         
f.close()<mailto:@@-233,26+364,24@@connprevent_unencrypted_vxlanf.write(self.STRONGSWAN_CONF)f.close()-f=open(self.IPSEC_CONF,>

 
<mailto:@@-233,26+364,24@@connprevent_unencrypted_vxlanf.write(self.STRONGSWAN_CONF)f.close()-f=open(self.IPSEC_CONF,>

-        f = open(self.IPSEC_CONF, 
"<mailto:@@-233,26+364,24@@connprevent_unencrypted_vxlanf.write(self.STRONGSWAN_CONF)f.close()-f=open(self.IPSEC_CONF,>w")

-        f.write(self.CONF_HEADER)

-        f.close()

-

-        f = open(self.IPSEC_SECRETS, "w")

-        f.write(FILE_HEADER)

+        f = open(self.SWANCTL_CONF, "w")

+        f.write(self.SWANCTL_CONF_HEADER)

         f.close()



         vlog.info("Restarting StrongSwan")

-        subprocess.call([self.IPSEC, "restart"])

+        subprocess.call((self.SYSTEMCTL +

+                       " restart strongswan-starter.service").split())



     def get_active_conns(self):

-        """This function parses output from 'ipsec status' command.

+        """This function parses output from 'swanctl --list-conns' command.

         It returns dictionary where <key> is interface name (as in OVSDB)

         and <value> is another dictionary.  This another dictionary

         uses strongSwan connection name as <key> and more detailed

         sample line from the parsed outpus as <value>. """



         conns = {}

-        proc = subprocess.Popen([self.IPSEC, 'status'], stdout=subprocess.PIPE)

+        proc = subprocess.Popen([self.SWANCTL, '--list-conns'],

+                               stdout=subprocess.PIPE)



         while True:

             line = proc.stdout.readline().strip().decode()

@@ -272,10 +401,8 @@ conn prevent_unencrypted_vxlan

         return conns



     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.secrets_file.write(FILE_HEADER)

+        self.conf_file = open(self.SWANCTL_CONF, "w")

+        self.conf_file.write(self.SWANCTL_CONF_HEADER)



     def config_global(self, monitor):

         """Configure the global state of IPsec 
tunnels."""<mailto:@@-299,13+426,10@@connprevent_unencrypted_vxlandefconfig_tunnel(self,tunnel):iftunnel.conf[>

@@ -299,13 +426,10 @@ conn 
prevent_unencrypted_vxlan<mailto:@@-299,13+426,10@@connprevent_unencrypted_vxlandefconfig_tunnel(self,tunnel):iftunnel.conf[>

 
<mailto:@@-299,13+426,10@@connprevent_unencrypted_vxlandefconfig_tunnel(self,tunnel):iftunnel.conf[>

     def config_tunnel(self, 
tunnel):<mailto:@@-299,13+426,10@@connprevent_unencrypted_vxlandefconfig_tunnel(self,tunnel):iftunnel.conf[>

         if 
tunnel.conf["<mailto:@@-299,13+426,10@@connprevent_unencrypted_vxlandefconfig_tunnel(self,tunnel):iftunnel.conf[>psk"]:

-            self.secrets_file.write('%%any %s : PSK "%s"\n' %

-                            (tunnel.conf["remote_ip"], tunnel.conf["psk"]))

             auth_section = self.auth_tmpl["psk"].substitute(tunnel.conf)

+            secrets = Template(self.SECRETS_SECTION).substitute(tunnel.conf)

         else:

-            self.secrets_file.write("%%any %s : RSA %s\n" %

-                                        (tunnel.conf["remote_ip"],

-                                        tunnel.conf["private_key"]))

+            secrets = None

             if tunnel.conf["remote_cert"]:

                 tmpl = self.auth_tmpl["pki_remote"]

                 auth_section = tmpl.substitute(tunnel.conf)

@@ -316,38 +440,43 @@ conn prevent_unencrypted_vxlan

         vals = tunnel.conf.copy()

         vals["auth_section"] = auth_section

         vals["version"] = tunnel.version

-        conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals)

+        if tunnel.conf["address_family"] == "IPv6":

+            vals["local_addrs"] = "::/0"

+            vals["subnet"] = "64"

+        else:

+            vals["local_addrs"] = "0.0.0.0/0"

+            vals["subnet"] = "32"

+        if vals["local_ip"] == "%defaultroute":

+            if tunnel.conf["address_family"] == "IPv6":

+                vals["local_ip"] = "::/0"

+            else:

+                vals["local_ip"] = "0.0.0.0/0"

+        conf_text = self.transp_tmpl[tunnel.conf[

+                                    "tunnel_type"]].substitute(vals)

         self.conf_file.write(conf_text)



+        if secrets is not None:

+            self.conf_file.write(secrets)

+

     def config_fini(self):

-        self.secrets_file.close()

         self.conf_file.close()

-        self.secrets_file = None

         self.conf_file = None



     def refresh(self, monitor):

         """This functions refreshes strongSwan configuration.  Behind the

         scenes this function calls:

-        1. once "ipsec update" command that tells strongSwan to load

-           all new tunnels from "ipsec.conf"; and

-        2. once "ipsec rereadsecrets" command that tells strongswan to load

-           secrets from "ipsec.conf" file

-        3. for every removed tunnel "ipsec stroke down-nb <tunnel>" command

+        1. once "swanctl --load-all" command that tells strongSwan to load

+           all new tunnels from "swanctl.conf"; and

+        2. for every removed tunnel "swanctl -t --child <tunnel>" command

            that removes old tunnels.

         Once strongSwan vici bindings will be distributed with major

         Linux distributions this function could be simplified."""

         vlog.info("Refreshing StrongSwan configuration")

-        subprocess.call([self.IPSEC, "update"])

-        subprocess.call([self.IPSEC, "rereadsecrets"])

-        # "ipsec update" command does not remove those tunnels that were

-        # updated or that disappeared from the ipsec.conf file.  So, we have

-        # to manually remove them by calling "ipsec stroke down-nb <tunnel>"

+        # "swanctl --load-all" command does not remove those tunnels that were

+        # updated or that disappeared from the swanctl.conf files.  So, we have

+        # to manually remove them by calling "swanctl -t --child  <tunnel>"

         # command.  We use <version> number to tell apart tunnels that

         # were just updated.

-        # "ipsec down-nb" command is designed to be non-blocking (opposed

-        # to "ipsec down" command).  This means that we should not be concerned

-        # about possibility of ovs-monitor-ipsec to block for each tunnel

-        # while strongSwan sends IKE messages over Internet.

         conns_dict = self.get_active_conns()

         for ifname, conns in conns_dict.items():

             tunnel = monitor.tunnels.get(ifname)

@@ -371,7 +500,21 @@ conn prevent_unencrypted_vxlan



                 if not tunnel or tunnel.version != ver:

                     vlog.info("%s is outdated %u" % (conn, ver))

-                    subprocess.call([self.IPSEC, "stroke", "down-nb", conn])

+                    self.terminate_ipsec_connection(conn)

+

+        self.update_ipsec_connections()

+

+    def update_ipsec_connections(self):

+        process = subprocess.Popen((self.SWANCTL + " --load-all").split(),

+                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)

+        err = str(process.stderr.read())

+        if re.match(r".*Error.*", err, re.IGNORECASE) is not None:

+            vlog.err(err)

+

+    def terminate_ipsec_connection(self, conn_name):

+        subprocess.Popen((self.SWANCTL + " -t --child " +

+                        conn_name).split(), stdout=subprocess.PIPE)

+        vlog.info("IPsec connection terminated for " + conn_name)





 class LibreSwanHelper(object):

@@ -442,6 +585,53 @@ conn prevent_unencrypted_vxlan

     leftrsasigkey=%cert

     rightca=%same""")}



+    transp_tmpl = {"gre": Template("""\

+    conn $ifname-$version

+    $auth_section

+        leftprotoport=gre

+        rightprotoport=gre

+

+    """), "gre64": Template("""\

+    conn $ifname-$version

+    $auth_section

+        leftprotoport=gre

+        rightprotoport=gre

+

+    """), "geneve": Template("""\

+    conn $ifname-in-$version

+    $auth_section

+        leftprotoport=udp/6081

+        rightprotoport=udp

+

+    conn $ifname-out-$version

+    $auth_section

+        leftprotoport=udp

+        rightprotoport=udp/6081

+

+    """), "stt": Template("""\

+    conn $ifname-in-$version

+    $auth_section

+        leftprotoport=tcp/7471

+        rightprotoport=tcp

+

+    conn $ifname-out-$version

+    $auth_section

+        leftprotoport=tcp

+        rightprotoport=tcp/7471

+

+    """), "vxlan": Template("""\

+    conn $ifname-in-$version

+    $auth_section

+        leftprotoport=udp/4789

+        rightprotoport=udp

+

+    conn $ifname-out-$version

+    $auth_section

+        leftprotoport=udp

+        rightprotoport=udp/4789

+

+    """)}

+

     CERT_PREFIX = "ovs_cert_"

     CERTKEY_PREFIX = "ovs_certkey_"



@@ -546,7 +736,8 @@ conn prevent_unencrypted_vxlan

         vals = tunnel.conf.copy()

         vals["auth_section"] = auth_section

         vals["version"] = tunnel.version

-        conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals)

+        conf_text = self.transp_tmpl[tunnel.conf[

+                                    "tunnel_type"]].substitute(vals)

         self.conf_file.write(conf_text)



     def config_fini(self):



Acked-by: Mohammad Heib


_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to