On Tue, Mar 22, 2022 at 10:09 AM Emeel Hakim via dev
<[email protected]> 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 <[email protected]>
> Signed-off-by: Emeel Hakim <[email protected]>
> ---
>  ipsec/ovs-monitor-ipsec.in | 466 ++++++++++++++++++++++++++-----------
>  1 file changed, 325 insertions(+), 141 deletions(-)
>
> diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
> index c9f3cc5a1..8c72563e1 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."""
> @@ -233,26 +364,24 @@ conn prevent_unencrypted_vxlan
>          f.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."""
> @@ -299,13 +426,10 @@ conn prevent_unencrypted_vxlan
>
>      def config_tunnel(self, tunnel):
>          if tunnel.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,45 +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")
> -        proc = subprocess.Popen([self.IPSEC, "update"],
> -                        stdout=subprocess.PIPE,
> -                        stderr=subprocess.PIPE)
> -        outs, errs = proc.communicate()
> -        if proc.returncode != 0:
> -            vlog.err("StrongSwan failed to update configuration:\n"
> -                           "%s \n %s" % (str(outs), str(errs)))
> -
> -        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)
> @@ -378,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):
> @@ -449,6 +585,53 @@ conn prevent_unencrypted_vxlan
>      leftrsasigkey=%cert
>      rightca=%same""")}
>

The following templates actually introduce a syntax error for me.

I had to make the following sort of change to get ipsec working again:
-    conn $ifname-$version
+conn $ifname-$version
     $auth_section
-        leftprotoport=gre
-        rightprotoport=gre
+    leftprotoport=gre
+    rightprotoport=gre

WIthout that, pluto doesn't seem to parse the config file properly.

Cheers,
M


> +    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_"
>
> @@ -553,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):
> --
> 2.21.3
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to