Hi Emeel,
Thank you for submitting this patch.
please see few comments below
On 2/14/22 16:21, 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.
Signed-off-by: Emeel Hakim<[email protected]>
Reviewed-by: Nick Desaulniers <[email protected]
---
ipsec/ovs-monitor-ipsec.in | 454 ++++++++++++++++++++++++++-----------
1 file changed, 321 insertions(+), 133 deletions(-)
diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
index a8b0705d9..dc070f36e 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,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
+ 1. once "swanctl --load-all" command that tells strongSwan to load
i think this must be swanctl.conf instead of ipsec.conf
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
+ 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,18 @@ 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):
can you please check the return value of this subprocess and
update the ovs logs in case of failures, that will be very
helpful when debugging
connection errors.
+ subprocess.Popen((self.SWANCTL + " --load-all").split(),
+ stdout=subprocess.PIPE)
+
+ 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 +582,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 +733,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):
Thanks,
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev