diff options
| -rw-r--r-- | data/templates/ipsec/swanctl.conf.j2 | 12 | ||||
| -rw-r--r-- | data/templates/ipsec/swanctl/peer.j2 | 24 | ||||
| -rw-r--r-- | interface-definitions/include/ipsec/authentication-id.xml.i | 6 | ||||
| -rw-r--r-- | interface-definitions/include/ipsec/remote-address.xml.i | 30 | ||||
| -rw-r--r-- | interface-definitions/include/version/ipsec-version.xml.i | 2 | ||||
| -rw-r--r-- | interface-definitions/vpn-ipsec.xml.in | 93 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_vpn_ipsec.py | 50 | ||||
| -rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 11 | ||||
| -rwxr-xr-x | src/migration-scripts/ipsec/9-to-10 | 123 | 
9 files changed, 232 insertions, 119 deletions
| diff --git a/data/templates/ipsec/swanctl.conf.j2 b/data/templates/ipsec/swanctl.conf.j2 index bf6b8259c..38d7981c6 100644 --- a/data/templates/ipsec/swanctl.conf.j2 +++ b/data/templates/ipsec/swanctl.conf.j2 @@ -63,9 +63,11 @@ secrets {  {%             if peer_conf.local_address is vyos_defined %}          id-local = {{ peer_conf.local_address }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }}  {%             endif %} -        id-remote = {{ peer }} -{%             if peer_conf.authentication.id is vyos_defined %} -        id-localid = {{ peer_conf.authentication.id }} +{%             for address in peer_conf.remote_address %} +        id-remote_{{ address | dot_colon_to_dash }} = {{ address }} +{%             endfor %} +{%             if peer_conf.authentication.local_id is vyos_defined %} +        id-localid = {{ peer_conf.authentication.local_id }}  {%             endif %}  {%             if peer_conf.authentication.remote_id is vyos_defined %}          id-remoteid = {{ peer_conf.authentication.remote_id }} @@ -93,8 +95,8 @@ secrets {  {%     for ra, ra_conf in remote_access.connection.items() if ra_conf.disable is not vyos_defined %}  {%         if ra_conf.authentication.server_mode is vyos_defined('pre-shared-secret') %}      ike_{{ ra }} { -{%             if ra_conf.authentication.id is vyos_defined %} -        id = "{{ ra_conf.authentication.id }}" +{%             if ra_conf.authentication.local_id is vyos_defined %} +        id = "{{ ra_conf.authentication.local_id }}"  {%             elif ra_conf.local_address is vyos_defined %}          id = "{{ ra_conf.local_address }}"  {%             endif %} diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2 index 90d2c774f..d097a04fc 100644 --- a/data/templates/ipsec/swanctl/peer.j2 +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -2,14 +2,14 @@  {% set name = peer.replace("@", "") | dot_colon_to_dash %}  {# peer needs to reference the global IKE configuration for certain values #}  {% set ike = ike_group[peer_conf.ike_group] %} -    peer_{{ name }} { +    {{ name }} {          proposals = {{ ike | get_esp_ike_cipher | join(',') }}          version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }}  {% if peer_conf.virtual_address is vyos_defined %}          vips = {{ peer_conf.virtual_address | join(', ') }}  {% endif %} -        local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '0.0.0.0/0' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} -        remote_addrs = {{ peer if peer not in ['any', '0.0.0.0'] and peer[0:1] != '@' else '0.0.0.0/0' }} +        local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '%any' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} +        remote_addrs = {{ peer_conf.remote_address | join(",") if peer_conf.remote_address is vyos_defined and 'any' not in peer_conf.remote_address else '%any' }}  {% if peer_conf.authentication.mode is vyos_defined('x509') %}          send_cert = always  {% endif %} @@ -21,7 +21,7 @@          aggressive = yes  {% endif %}          rekey_time = {{ ike.lifetime }}s -        mobike = {{ "yes" if ike.mobike is not defined or ike.mobike == "enable" else "no" }} +        mobike = {{ "no" if ike.disable_mobike is defined else "yes" }}  {% if peer[0:1] == '@' %}          keyingtries = 0          reauth_time = 0 @@ -30,12 +30,12 @@  {% elif peer_conf.connection_type is vyos_defined('respond') %}          keyingtries = 1  {% endif %} -{% if peer_conf.force_encapsulation is vyos_defined('enable') %} +{% if peer_conf.force_udp_encapsulation is vyos_defined %}          encap = yes  {% endif %}          local { -{% if peer_conf.authentication.id is vyos_defined %} -            id = "{{ peer_conf.authentication.id }}" +{% if peer_conf.authentication.local_id is vyos_defined %} +            id = "{{ peer_conf.authentication.local_id }}"  {% endif %}              auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }}  {% if peer_conf.authentication.mode == 'x509' %} @@ -58,7 +58,7 @@          children {  {% if peer_conf.vti.bind is vyos_defined and peer_conf.tunnel is not vyos_defined %}  {%     set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is vyos_defined else esp_group[ peer_conf.default_esp_group ] %} -            peer_{{ name }}_vti { +            {{ name }}-vti {                  esp_proposals = {{ vti_esp | get_esp_ike_cipher(ike) | join(',') }}  {%     if vti_esp.life_bytes is vyos_defined %}                  life_bytes = {{ vti_esp.life_bytes }} @@ -75,7 +75,7 @@  {%     set if_id = peer_conf.vti.bind | replace('vti', '') | int + 1 %}                  if_id_in = {{ if_id }}                  if_id_out = {{ if_id }} -                ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined('enable') else 'no' }} +                ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined else 'no' }}                  mode = {{ vti_esp.mode }}  {%     if peer[0:1] == '@' %}                  start_action = none @@ -101,7 +101,7 @@  {%         set local_suffix = '[{0}/{1}]'.format(proto, local_port) if proto or local_port else '' %}  {%         set remote_port = tunnel_conf.remote.port if tunnel_conf.remote.port is vyos_defined else '' %}  {%         set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %} -            peer_{{ name }}_tunnel_{{ tunnel_id }} { +            {{ name }}-tunnel-{{ tunnel_id }} {                  esp_proposals = {{ tunnel_esp | get_esp_ike_cipher(ike) | join(',') }}  {%         if tunnel_esp.life_bytes is vyos_defined %}                  life_bytes = {{ tunnel_esp.life_bytes }} @@ -126,7 +126,7 @@                  local_ts = {{ peer_conf.local_address }}{{ local_suffix }}                  remote_ts = {{ peer }}{{ remote_suffix }}  {%         endif %} -                ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined('enable') else 'no' }} +                ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined else 'no' }}                  mode = {{ tunnel_esp.mode }}  {%         if peer[0:1] == '@' %}                  start_action = none @@ -152,7 +152,7 @@  {%         endif %}              }  {%         if tunnel_conf.passthrough is vyos_defined %} -            peer_{{ name }}_tunnel_{{ tunnel_id }}_passthrough { +            {{ name }}-tunnel-{{ tunnel_id }}-passthrough {                  local_ts = {{ tunnel_conf.passthrough | join(",") }}                  remote_ts = {{ tunnel_conf.passthrough | join(",") }}                  start_action = trap diff --git a/interface-definitions/include/ipsec/authentication-id.xml.i b/interface-definitions/include/ipsec/authentication-id.xml.i index 4967782ec..4e0b848c3 100644 --- a/interface-definitions/include/ipsec/authentication-id.xml.i +++ b/interface-definitions/include/ipsec/authentication-id.xml.i @@ -1,10 +1,10 @@  <!-- include start from ipsec/authentication-id.xml.i --> -<leafNode name="id"> +<leafNode name="local-id">    <properties> -    <help>ID for peer authentication</help> +    <help>Local ID for peer authentication</help>      <valueHelp>        <format>txt</format> -      <description>ID used for peer authentication</description> +      <description>Local ID used for peer authentication</description>      </valueHelp>    </properties>  </leafNode> diff --git a/interface-definitions/include/ipsec/remote-address.xml.i b/interface-definitions/include/ipsec/remote-address.xml.i new file mode 100644 index 000000000..ba96290d0 --- /dev/null +++ b/interface-definitions/include/ipsec/remote-address.xml.i @@ -0,0 +1,30 @@ +<!-- include start from ipsec/remote-address.xml.i --> +<leafNode name="remote-address"> +  <properties> +    <help>IPv4 or IPv6 address of the remote peer</help> +    <valueHelp> +      <format>ipv4</format> +      <description>IPv4 address of the remote peer</description> +    </valueHelp> +    <valueHelp> +      <format>ipv6</format> +      <description>IPv6 address of the remote peer</description> +    </valueHelp> +    <valueHelp> +      <format>hostname</format> +      <description>Fully qualified domain name of the remote peer</description> +    </valueHelp> +    <valueHelp> +      <format>any</format> +      <description>Allow any IP address of the remote peer</description> +    </valueHelp> +    <constraint> +      <validator name="ipv4-address"/> +      <validator name="ipv6-address"/> +      <validator name="fqdn"/> +      <regex>(any)</regex> +    </constraint> +    <multi/> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/version/ipsec-version.xml.i b/interface-definitions/include/version/ipsec-version.xml.i index 59295cc91..1c978e8e6 100644 --- a/interface-definitions/include/version/ipsec-version.xml.i +++ b/interface-definitions/include/version/ipsec-version.xml.i @@ -1,3 +1,3 @@  <!-- include start from include/version/ipsec-version.xml.i --> -<syntaxVersion component='ipsec' version='9'></syntaxVersion> +<syntaxVersion component='ipsec' version='10'></syntaxVersion>  <!-- include end --> diff --git a/interface-definitions/vpn-ipsec.xml.in b/interface-definitions/vpn-ipsec.xml.in index d36fbb024..5887a349f 100644 --- a/interface-definitions/vpn-ipsec.xml.in +++ b/interface-definitions/vpn-ipsec.xml.in @@ -24,23 +24,9 @@              <children>                <leafNode name="compression">                  <properties> -                  <help>ESP compression</help> -                  <completionHelp> -                    <list>disable enable</list> -                  </completionHelp> -                  <valueHelp> -                    <format>disable</format> -                    <description>Disable ESP compression</description> -                  </valueHelp> -                  <valueHelp> -                    <format>enable</format> -                    <description>Enable ESP compression</description> -                  </valueHelp> -                  <constraint> -                    <regex>(disable|enable)</regex> -                  </constraint> +                  <help>Enable ESP compression</help> +                  <valueless/>                  </properties> -                <defaultValue>disable</defaultValue>                </leafNode>                <leafNode name="lifetime">                  <properties> @@ -309,20 +295,7 @@                <leafNode name="ikev2-reauth">                  <properties>                    <help>Re-authentication of the remote peer during an IKE re-key (IKEv2 only)</help> -                  <completionHelp> -                    <list>yes no</list> -                  </completionHelp> -                  <valueHelp> -                    <format>yes</format> -                    <description>Enable remote host re-authentication during an IKE rekey (currently broken due to a strongswan bug)</description> -                  </valueHelp> -                  <valueHelp> -                    <format>no</format> -                    <description>Disable remote host re-authenticaton during an IKE rekey</description> -                  </valueHelp> -                  <constraint> -                    <regex>(yes|no)</regex> -                  </constraint> +                  <valueless/>                  </properties>                </leafNode>                <leafNode name="key-exchange"> @@ -357,25 +330,11 @@                  </properties>                  <defaultValue>28800</defaultValue>                </leafNode> -              <leafNode name="mobike"> +              <leafNode name="disable-mobike">                  <properties> -                  <help>Enable MOBIKE Support (IKEv2 only)</help> -                  <completionHelp> -                    <list>enable disable</list> -                  </completionHelp> -                  <valueHelp> -                    <format>enable</format> -                    <description>Enable MOBIKE</description> -                  </valueHelp> -                  <valueHelp> -                    <format>disable</format> -                    <description>Disable MOBIKE</description> -                  </valueHelp> -                  <constraint> -                    <regex>(enable|disable)</regex> -                  </constraint> +                  <help>Disable MOBIKE Support (IKEv2 only)</help> +                  <valueless/>                  </properties> -                <defaultValue>enable</defaultValue>                </leafNode>                <leafNode name="mode">                  <properties> @@ -929,23 +888,15 @@              <children>                <tagNode name="peer">                  <properties> -                  <help>VPN peer</help> -                  <valueHelp> -                    <format>ipv4</format> -                    <description>IPv4 address of the peer</description> -                  </valueHelp> -                  <valueHelp> -                    <format>ipv6</format> -                    <description>IPv6 address of the peer</description> -                  </valueHelp> +                  <help>Connection name of the peer</help>                    <valueHelp>                      <format>txt</format> -                    <description>Hostname of the peer</description> -                  </valueHelp> -                  <valueHelp> -                    <format><@text></format> -                    <description>ID of the peer</description> +                    <description>Connection name of the peer</description>                    </valueHelp> +                  <constraint> +                    <regex>[-_a-zA-Z0-9|@]+</regex> +                  </constraint> +                  <constraintErrorMessage>Peer connection name must be alphanumeric and can contain hyphen and underscores</constraintErrorMessage>                  </properties>                  <children>                    #include <include/generic-disable-node.xml.i> @@ -1031,23 +982,10 @@                    </leafNode>                    #include <include/generic-description.xml.i>                    #include <include/dhcp-interface.xml.i> -                  <leafNode name="force-encapsulation"> +                  <leafNode name="force-udp-encapsulation">                      <properties> -                      <help>Force UDP Encapsulation for ESP payloads</help> -                      <completionHelp> -                        <list>enable disable</list> -                      </completionHelp> -                      <valueHelp> -                        <format>enable</format> -                        <description>Force UDP encapsulation</description> -                      </valueHelp> -                      <valueHelp> -                        <format>disable</format> -                        <description>Do not force UDP encapsulation</description> -                      </valueHelp> -                      <constraint> -                        <regex>(enable|disable)</regex> -                      </constraint> +                      <help>Force UDP encapsulation</help> +                      <valueless/>                      </properties>                    </leafNode>                    #include <include/ipsec/ike-group.xml.i> @@ -1075,6 +1013,7 @@                      </properties>                    </leafNode>                    #include <include/ipsec/local-address.xml.i> +                  #include <include/ipsec/remote-address.xml.i>                    <tagNode name="tunnel">                      <properties>                        <help>Peer tunnel</help> diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 8a6514d57..bd242104f 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 VyOS maintainers and contributors  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License version 2 or later as @@ -33,6 +33,7 @@ dhcp_waiting_file = '/tmp/ipsec_dhcp_waiting'  swanctl_file = '/etc/swanctl/swanctl.conf'  peer_ip = '203.0.113.45' +connection_name = 'main-branch'  interface = 'eth1'  vif = '100'  esp_group = 'MyESPGroup' @@ -150,7 +151,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(ethernet_path + [interface, 'vif', vif, 'address', 'dhcp']) # Use VLAN to avoid getting IP from qemu dhcp server          # Site to site -        peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] +        peer_base_path = base_path + ['site-to-site', 'peer', connection_name]          self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])          self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])          self.cli_set(peer_base_path + ['ike-group', ike_group]) @@ -173,7 +174,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          priority = '20'          life_bytes = '100000'          life_packets = '2000000' -        peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] +        peer_base_path = base_path + ['site-to-site', 'peer', connection_name]          self.cli_set(base_path + ['esp-group', esp_group, 'life-bytes', life_bytes])          self.cli_set(base_path + ['esp-group', esp_group, 'life-packets', life_packets]) @@ -183,6 +184,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(peer_base_path + ['ike-group', ike_group])          self.cli_set(peer_base_path + ['default-esp-group', esp_group])          self.cli_set(peer_base_path + ['local-address', local_address]) +        self.cli_set(peer_base_path + ['remote-address', peer_ip])          self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'tcp'])          self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.10.0/24'])          self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.11.0/24']) @@ -211,11 +213,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):              f'local_addrs = {local_address} # dhcp:no',              f'remote_addrs = {peer_ip}',              f'mode = tunnel', -            f'peer_{peer_ip.replace(".","-")}_tunnel_1', +            f'{connection_name}-tunnel-1',              f'local_ts = 172.16.10.0/24[tcp/443],172.16.11.0/24[tcp/443]',              f'remote_ts = 172.17.10.0/24[tcp/443],172.17.11.0/24[tcp/443]',              f'mode = tunnel', -            f'peer_{peer_ip.replace(".","-")}_tunnel_2', +            f'{connection_name}-tunnel-2',              f'local_ts = 10.1.0.0/16',              f'remote_ts = 10.2.0.0/16',              f'priority = {priority}', @@ -226,7 +228,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          swanctl_secrets_lines = [              f'id-local = {local_address} # dhcp:no', -            f'id-remote = {peer_ip}', +            f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}',              f'secret = "{secret}"'          ]          for line in swanctl_secrets_lines: @@ -236,18 +238,24 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):      def test_03_site_to_site_vti(self):          local_address = '192.0.2.10'          vti = 'vti10' +        # IKE +        self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) +        self.cli_set(base_path + ['ike-group', ike_group, 'disable-mobike']) +        # ESP +        self.cli_set(base_path + ['esp-group', esp_group, 'compression'])          # VTI interface          self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24']) -        self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])          # Site to site -        peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] +        peer_base_path = base_path + ['site-to-site', 'peer', connection_name]          self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])          self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])          self.cli_set(peer_base_path + ['connection-type', 'none']) +        self.cli_set(peer_base_path + ['force-udp-encapsulation'])          self.cli_set(peer_base_path + ['ike-group', ike_group])          self.cli_set(peer_base_path + ['default-esp-group', esp_group])          self.cli_set(peer_base_path + ['local-address', local_address]) +        self.cli_set(peer_base_path + ['remote-address', peer_ip])          self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.10.0/24'])          self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.11.0/24'])          self.cli_set(peer_base_path + ['tunnel', '1', 'remote', 'prefix', '172.17.10.0/24']) @@ -269,10 +277,12 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):              f'proposals = aes128-sha1-modp1024',              f'esp_proposals = aes128-sha1-modp1024',              f'local_addrs = {local_address} # dhcp:no', +            f'mobike = no',              f'remote_addrs = {peer_ip}',              f'mode = tunnel',              f'local_ts = 172.16.10.0/24,172.16.11.0/24',              f'remote_ts = 172.17.10.0/24,172.17.11.0/24', +            f'ipcomp = yes',              f'start_action = none',              f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one              f'if_id_out = {if_id}', @@ -283,7 +293,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          swanctl_secrets_lines = [              f'id-local = {local_address} # dhcp:no', -            f'id-remote = {peer_ip}', +            f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}',              f'secret = "{secret}"'          ]          for line in swanctl_secrets_lines: @@ -311,7 +321,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'shortcut'])          # IKE/ESP Groups -        self.cli_set(base_path + ['esp-group', esp_group, 'compression', 'disable'])          self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', esp_lifetime])          self.cli_set(base_path + ['esp-group', esp_group, 'mode', 'transport'])          self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'dh-group2']) @@ -320,7 +329,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', '3des'])          self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'md5']) -        self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no'])          self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev1'])          self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])          self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '2']) @@ -366,10 +374,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(vti_path + [vti, 'address', '192.168.0.1/31'])          peer_ip = '172.18.254.202' +        connection_name = 'office'          local_address = '172.18.254.201' -        peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] +        peer_base_path = base_path + ['site-to-site', 'peer', connection_name] -        self.cli_set(peer_base_path + ['authentication', 'id', peer_name]) +        self.cli_set(peer_base_path + ['authentication', 'local-id', peer_name])          self.cli_set(peer_base_path + ['authentication', 'mode', 'x509'])          self.cli_set(peer_base_path + ['authentication', 'remote-id', 'peer2'])          self.cli_set(peer_base_path + ['authentication', 'x509', 'ca-certificate', ca_name]) @@ -378,6 +387,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(peer_base_path + ['ike-group', ike_group])          self.cli_set(peer_base_path + ['ikev2-reauth', 'inherit'])          self.cli_set(peer_base_path + ['local-address', local_address]) +        self.cli_set(peer_base_path + ['remote-address', peer_ip])          self.cli_set(peer_base_path + ['vti', 'bind', vti])          self.cli_set(peer_base_path + ['vti', 'esp-group', esp_group]) @@ -391,7 +401,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          # to also support a vti0 interface          if_id = str(int(if_id) +1)          swanctl_lines = [ -            f'peer_{tmp}', +            f'{connection_name}',              f'version = 0', # key-exchange not set - defaulting to 0 for ikev1 and ikev2              f'send_cert = always',              f'mobike = yes', @@ -416,7 +426,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):              self.assertIn(line, swanctl_conf)          swanctl_secrets_lines = [ -            f'peer_{tmp}', +            f'{connection_name}',              f'file = {peer_name}.pem',          ]          for line in swanctl_secrets_lines: @@ -430,7 +440,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          local_address = '192.0.2.5'          local_id = 'vyos-r1'          remote_id = 'vyos-r2' -        peer_base_path = base_path + ['site-to-site', 'peer', peer_ip] +        peer_base_path = base_path + ['site-to-site', 'peer', connection_name]          self.cli_set(tunnel_path + ['tun1', 'encapsulation', 'gre'])          self.cli_set(tunnel_path + ['tun1', 'source-address', local_address]) @@ -438,10 +448,9 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(base_path + ['interface', interface])          self.cli_set(base_path + ['options', 'flexvpn'])          self.cli_set(base_path + ['options', 'interface', 'tun1']) -        self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no'])          self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) -        self.cli_set(peer_base_path + ['authentication', 'id', local_id]) +        self.cli_set(peer_base_path + ['authentication', 'local-id', local_id])          self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])          self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])          self.cli_set(peer_base_path + ['authentication', 'remote-id', remote_id]) @@ -449,6 +458,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(peer_base_path + ['ike-group', ike_group])          self.cli_set(peer_base_path + ['default-esp-group', esp_group])          self.cli_set(peer_base_path + ['local-address', local_address]) +        self.cli_set(peer_base_path + ['remote-address', peer_ip])          self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'gre'])          self.cli_set(peer_base_path + ['virtual-address', '203.0.113.55']) @@ -464,7 +474,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):              f'life_time = 3600s', # default value              f'local_addrs = {local_address} # dhcp:no',              f'remote_addrs = {peer_ip}', -            f'peer_{peer_ip.replace(".","-")}_tunnel_1', +            f'{connection_name}-tunnel-1',              f'mode = tunnel',          ] @@ -473,7 +483,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          swanctl_secrets_lines = [              f'id-local = {local_address} # dhcp:no', -            f'id-remote = {peer_ip}', +            f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}',              f'id-localid = {local_id}',              f'id-remoteid = {remote_id}',              f'secret = "{secret}"', diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 5ca32d23e..c9061366d 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 VyOS maintainers and contributors  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License version 2 or later as @@ -16,6 +16,7 @@  import ipaddress  import os +import re  from sys import exit  from time import sleep @@ -348,6 +349,14 @@ def verify(ipsec):      if 'site_to_site' in ipsec and 'peer' in ipsec['site_to_site']:          for peer, peer_conf in ipsec['site_to_site']['peer'].items():              has_default_esp = False +            # Peer name it is swanctl connection name and shouldn't contain dots or colons, T4118 +            if bool(re.search(':|\.', peer)): +                raise ConfigError(f'Incorrect peer name "{peer}" ' +                                  f'Peer name can contain alpha-numeric letters, hyphen and underscore') + +            if 'remote_address' not in peer_conf: +                print(f'You should set correct remote-address "peer {peer} remote-address x.x.x.x"\n') +              if 'default_esp_group' in peer_conf:                  has_default_esp = True                  if 'esp_group' not in ipsec or peer_conf['default_esp_group'] not in ipsec['esp_group']: diff --git a/src/migration-scripts/ipsec/9-to-10 b/src/migration-scripts/ipsec/9-to-10 new file mode 100755 index 000000000..98e0aede4 --- /dev/null +++ b/src/migration-scripts/ipsec/9-to-10 @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +import re + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 + + +if (len(argv) < 1): +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +base = ['vpn', 'ipsec'] +config = ConfigTree(config_file) + +if not config.exists(base): +    # Nothing to do +    exit(0) + +# IKE changes, T4118: +if config.exists(base + ['ike-group']): +    for ike_group in config.list_nodes(base + ['ike-group']): +        # replace 'ipsec ike-group <tag> mobike disable' +        #      => 'ipsec ike-group <tag> disable-mobike' +        mobike = base + ['ike-group', ike_group, 'mobike'] +        if config.exists(mobike): +            if config.return_value(mobike) == 'disable': +                config.set(base + ['ike-group', ike_group, 'disable-mobike']) +            config.delete(mobike) + +        # replace 'ipsec ike-group <tag> ikev2-reauth yes' +        #      => 'ipsec ike-group <tag> ikev2-reauth' +        reauth = base + ['ike-group', ike_group, 'ikev2-reauth'] +        if config.exists(reauth): +            if config.return_value(reauth) == 'yes': +                config.delete(reauth) +                config.set(reauth) +            else: +                config.delete(reauth) + +# ESP changes +# replace 'ipsec esp-group <tag> compression enable' +#      => 'ipsec esp-group <tag> compression' +if config.exists(base + ['esp-group']): +    for esp_group in config.list_nodes(base + ['esp-group']): +        compression = base + ['esp-group', esp_group, 'compression'] +        if config.exists(compression): +            if config.return_value(compression) == 'enable': +                config.delete(compression) +                config.set(compression) +            else: +                config.delete(compression) + +# PEER changes +if config.exists(base + ['site-to-site', 'peer']): +    for peer in config.list_nodes(base + ['site-to-site', 'peer']): +        # replace: 'peer <tag> id x' +        #       => 'peer <tag> local-id x' +        if config.exists(base + ['site-to-site', 'peer', peer, 'authentication', 'id']): +            config.rename(base + ['site-to-site', 'peer', peer, 'authentication', 'id'], 'local-id') + +        # For the peer '@foo' set remote-id 'foo' if remote-id is not defined +        if peer.startswith('@'): +            if not config.exists(base + ['site-to-site', 'peer', peer, 'authentication', 'remote-id']): +                tmp = peer.replace('@', '') +                config.set(base + ['site-to-site', 'peer', peer, 'authentication', 'remote-id'], value=tmp) + +        # replace: 'peer <tag> force-encapsulation enable' +        #       => 'peer <tag> force-udp-encapsulation' +        force_enc = base + ['site-to-site', 'peer', peer, 'force-encapsulation'] +        if config.exists(force_enc): +            if config.return_value(force_enc) == 'enable': +                config.delete(force_enc) +                config.set(base + ['site-to-site', 'peer', peer, 'force-udp-encapsulation']) +            else: +                config.delete(force_enc) + +        # add option: 'peer <tag> remote-address x.x.x.x' +        remote_address = peer +        if peer.startswith('@'): +            remote_address = 'any' +        config.set(base + ['site-to-site', 'peer', peer, 'remote-address', remote_address]) +        # Peer name it is swanctl connection name and shouldn't contain dots or colons +        # rename peer: +        #   peer 192.0.2.1   => peer peer_192-0-2-1 +        #   peer 2001:db8::2 => peer peer_2001-db8--2 +        #   peer @foo        => peer peer_foo +        re_peer_name = re.sub(':|\.', '-', peer) +        if re_peer_name.startswith('@'): +            re_peer_name = re.sub('@', '', re_peer_name) +        new_peer_name = f'peer_{re_peer_name}' + +        config.rename(base + ['site-to-site', 'peer', peer], new_peer_name) + +try: +    with open(file_name, 'w') as f: +        f.write(config.to_string()) +except OSError as e: +    print(f'Failed to save the modified config: {e}') +    exit(1) | 
