From 0751065ffa2161bedd040197dd51ad6ece5ab19b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 28 Jun 2021 22:58:24 +0200 Subject: ipsec: T1441: switch from vti to xfrm interfaces XFRM interfaces are similar to VTI devices in their basic functionality but offer several advantages: * No tunnel endpoint addresses have to be configured on the interfaces. Compared to VTIs, which are layer 3 tunnel devices with mandatory endpoints, this resolves issues with wildcard addresses (only one VTI with wildcard endpoints is supported), avoids a 1:1 mapping between SAs and interfaces, and easily allows SAs with multiple peers to share the same interface. * Because there are no endpoint addresses, IPv4 and IPv6 SAs are supported on the same interface (VTI devices only support one address family). * IPsec modes other than tunnel are supported (VTI devices only support tunnel mode). * No awkward configuration via GRE keys and XFRM marks. Instead, a new identifier (XFRM interface ID) links policies and SAs with XFRM interfaces. --- data/templates/ipsec/swanctl.conf.tmpl | 2 +- data/templates/ipsec/swanctl/peer.tmpl | 10 +++++----- python/vyos/ifconfig/vti.py | 6 ++---- src/conf_mode/interfaces-vti.py | 31 ------------------------------- src/conf_mode/vpn_ipsec.py | 18 ++++++------------ 5 files changed, 14 insertions(+), 53 deletions(-) diff --git a/data/templates/ipsec/swanctl.conf.tmpl b/data/templates/ipsec/swanctl.conf.tmpl index ea6d85743..d082729cb 100644 --- a/data/templates/ipsec/swanctl.conf.tmpl +++ b/data/templates/ipsec/swanctl.conf.tmpl @@ -18,7 +18,7 @@ connections { {% set peer_ike = ike_group[peer_conf.ike_group] %} {% set peer_esp = esp_group[peer_conf.default_esp_group] if peer_conf.default_esp_group is defined else None %} {% set auth_type = authby[peer_conf.authentication.mode] %} -{{ peer_tmpl.conn(peer_conn_name, peer, peer_conf, peer_ike, peer_esp, ciphers, esp_group, auth_type, marks) }} +{{ peer_tmpl.conn(peer_conn_name, peer, peer_conf, peer_ike, peer_esp, ciphers, esp_group, auth_type) }} {% endfor %} {% endif %} } diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index 0d01cd546..68284d7d9 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -1,4 +1,4 @@ -{% macro conn(name, peer, peer_conf, ike, esp, ciphers, esp_group, auth_type, marks) %} +{% macro conn(name, peer, peer_conf, ike, esp, ciphers, esp_group, auth_type) %} peer_{{ name }} { proposals = {{ ciphers.ike[peer_conf.ike_group] }} version = {{ ike['key_exchange'][4:] if "key_exchange" in ike else "0" }} @@ -61,8 +61,8 @@ local_ts = 0.0.0.0/0,::/0 remote_ts = 0.0.0.0/0,::/0 updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" - mark_in = {{ marks[peer_conf.vti.bind] }} - mark_out = {{ marks[peer_conf.vti.bind] }} + if_id_in = {{ peer_conf.vti.bind | replace('vti', '') }} + if_id_out = {{ peer_conf.vti.bind | replace('vti', '') }} ipcomp = {{ 'yes' if "compression" in vti_esp and vti_esp.compression == 'enable' else 'no' }} mode = {{ vti_esp.mode if "mode" in vti_esp else "tunnel" }} {% if peer[0:1] == '@' %} @@ -117,8 +117,8 @@ {% endif %} {% if peer_conf.vti is defined and peer_conf.vti.bind is defined %} updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" - mark_in = {{ marks[peer_conf.vti.bind] }} - mark_out = {{ marks[peer_conf.vti.bind] }} + if_id_in = {{ peer_conf.vti.bind | replace('vti', '') }} + if_id_out = {{ peer_conf.vti.bind | replace('vti', '') }} {% endif %} } {% if tunnel_conf.passthrough is defined and tunnel_conf.passthrough %} diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py index 9eafcd11b..a217d28ea 100644 --- a/python/vyos/ifconfig/vti.py +++ b/python/vyos/ifconfig/vti.py @@ -33,13 +33,11 @@ class VTIIf(Interface): # - https://man7.org/linux/man-pages/man8/ip-link.8.html # - https://man7.org/linux/man-pages/man8/ip-tunnel.8.html mapping = { - 'source_address' : 'local', 'source_interface' : 'dev', - 'remote' : 'remote', - 'key' : 'key', } - cmd = 'ip link add {ifname} type vti' + if_id = self.ifname.lstrip('vti') + cmd = f'ip link add {self.ifname} type xfrm if_id {if_id}' for vyos_key, iproute2_key in mapping.items(): # dict_search will return an empty dict "{}" for valueless nodes like # "parameters.nolearning" - thus we need to test the nodes existence diff --git a/src/conf_mode/interfaces-vti.py b/src/conf_mode/interfaces-vti.py index 6ff23ae59..1b38304c1 100755 --- a/src/conf_mode/interfaces-vti.py +++ b/src/conf_mode/interfaces-vti.py @@ -36,40 +36,9 @@ def get_config(config=None): conf = Config() base = ['interfaces', 'vti'] vti = get_interface_dict(conf, base) - - # VTI is more then an interface - we retrieve the "real" configuration from - # the IPsec peer configuration which binds this VTI - conf.set_level([]) - vti['ipsec'] = conf.get_config_dict(['vpn', 'ipsec', 'site-to-site', 'peer'], - key_mangling=('-', '_'), get_first_key=True, - no_tag_node_value_mangle=True) - - for peer, peer_config in vti['ipsec'].items(): - if dict_search('vti.bind', peer_config) == vti['ifname']: - vti['remote'] = peer - if 'local_address' in peer_config: - vti['source_address'] = peer_config['local_address'] - # we also need to "calculate" a per vti individual key - base = 0x900000 - vti['key'] = base + int(vti['ifname'].lstrip('vti')) - return vti def verify(vti): - if 'deleted' in vti: - return None - - ifname = vti['ifname'] - found = False - for peer, peer_config in vti['ipsec'].items(): - if dict_search('vti.bind', peer_config) == ifname: - found = True - # we can now stop processing the for loop - break - if not found: - tmp = vti['ifname'] - raise ConfigError(f'Interface "{ifname}" not referenced in any VPN configuration!') - return None def generate(vti): diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index d598ff6da..83c99798c 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -79,8 +79,6 @@ esp_ciphers = {} dhcp_wait_attempts = 2 dhcp_wait_sleep = 1 -mark_base = 0x900000 - swanctl_dir = '/etc/swanctl' ipsec_conf = '/etc/ipsec.conf' ipsec_secrets = '/etc/ipsec.secrets' @@ -321,8 +319,13 @@ def verify(ipsec): raise ConfigError(f"Local/remote prefix cannot be used with ESP transport mode on tunnel {tunnel} for site-to-site peer {peer}") def generate(ipsec): - data = {} + if not ipsec: + for config_file in [ipsec_conf, ipsec_secrets, interface_conf, swanctl_conf]: + if os.path.isfile(config_file): + os.unlink(config_file) + return + data = {} if ipsec: if ipsec['dhcp_no_address']: with open(DHCP_HOOK_IFLIST, 'w') as f: @@ -331,7 +334,6 @@ def generate(ipsec): data = ipsec data['authby'] = authby_translate data['ciphers'] = {'ike': ike_ciphers, 'esp': esp_ciphers} - data['marks'] = {} data['rsa_local_key'] = verify_rsa_local_key(ipsec) if 'site_to_site' in data and 'peer' in data['site_to_site']: @@ -361,10 +363,6 @@ def generate(ipsec): data['site_to_site']['peer'][peer]['local_address'] = local_ip - if 'vti' in peer_conf and 'bind' in peer_conf['vti']: - vti_interface = peer_conf['vti']['bind'] - data['marks'][vti_interface] = get_mark(vti_interface) - if 'tunnel' in peer_conf: for tunnel, tunnel_conf in peer_conf['tunnel'].items(): local_prefixes = dict_search('local.prefix', tunnel_conf) @@ -436,10 +434,6 @@ def apply(ipsec): resync_l2tp(ipsec) resync_nhrp(ipsec) -def get_mark(vti_interface): - vti_num = int(vti_interface.lstrip('vti')) - return mark_base + vti_num - if __name__ == '__main__': try: ipsec = get_config() -- cgit v1.2.3