diff --git a/data/templates/ipsec/swanctl/l2tp.j2 b/data/templates/ipsec/swanctl/l2tp.j2 index 7eaf9087de4..da3ff40d526 100644 --- a/data/templates/ipsec/swanctl/l2tp.j2 +++ b/data/templates/ipsec/swanctl/l2tp.j2 @@ -2,7 +2,7 @@ {% set l2tp_ike = ike_group[l2tp.ike_group] if l2tp.ike_group is vyos_defined else None %} {% set l2tp_esp = esp_group[l2tp.esp_group] if l2tp.esp_group is vyos_defined else None %} l2tp_remote_access { - proposals = {{ l2tp_ike | get_esp_ike_cipher | join(',') if l2tp_ike else l2tp_ike_default }} + proposals = {{ l2tp_ike | get_esp_ike_cipher(esn=False) | join(',') if l2tp_ike else l2tp_ike_default }} local_addrs = {{ l2tp_outside_address }} dpd_delay = 15s dpd_timeout = 45s diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2 index 96f761ccee5..ee2efcc508b 100644 --- a/data/templates/ipsec/swanctl/peer.j2 +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -19,7 +19,7 @@ {% if peer_conf.childless is vyos_defined %} childless = {{ peer_conf.childless }} {% endif %} - proposals = {{ ike | get_esp_ike_cipher | join(',') }} + proposals = {{ ike | get_esp_ike_cipher(esn=False) | 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(', ') }} diff --git a/data/templates/ipsec/swanctl/profile.j2 b/data/templates/ipsec/swanctl/profile.j2 index 10fe0a8eca3..1aec635fd3d 100644 --- a/data/templates/ipsec/swanctl/profile.j2 +++ b/data/templates/ipsec/swanctl/profile.j2 @@ -5,7 +5,7 @@ {% if profile_conf.bind.tunnel is vyos_defined %} {% for interface in profile_conf.bind.tunnel %} dmvpn-{{ name }}-{{ interface }} { - proposals = {{ ike_group[profile_conf.ike_group] | get_esp_ike_cipher | join(',') }} + proposals = {{ ike_group[profile_conf.ike_group] | get_esp_ike_cipher(esn=False) | join(',') }} version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }} rekey_time = {{ ike.lifetime }}s keyingtries = 0 diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2 index 639248caf8f..7d89ced8b03 100644 --- a/data/templates/ipsec/swanctl/remote_access.j2 +++ b/data/templates/ipsec/swanctl/remote_access.j2 @@ -14,7 +14,7 @@ {% endif %} remote_addrs = %any local_addrs = {{ rw_conf.local_address if rw_conf.local_address is not vyos_defined('any') else '%any' }} # dhcp:{{ rw_conf.dhcp_interface if rw_conf.dhcp_interface is vyos_defined else 'no' }} - proposals = {{ ike_group[rw_conf.ike_group] | get_esp_ike_cipher | join(',') }} + proposals = {{ ike_group[rw_conf.ike_group] | get_esp_ike_cipher(esn=False) | join(',') }} version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }} send_certreq = no {% if rw_conf.authentication.always_send_cert is vyos_defined %} diff --git a/python/vyos/template.py b/python/vyos/template.py index 5b228603cf5..8c83821416f 100755 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -483,7 +483,16 @@ def get_first_ike_dh_group(ike_group): return 'dh-group2' # Fallback on dh-group2 @register_filter('get_esp_ike_cipher') -def get_esp_ike_cipher(group_config, ike_group=None): +def get_esp_ike_cipher(group_config, ike_group=None, esn=True): + """Render strongSwan proposal strings. + + esn=True : ESP/CHILD_SA proposals, where ESN transforms are meaningful + esn=False : IKE_SA proposals. ESN is a CHILD_SA transform (RFC 7296 + section 3.3.2, Transform Type 5) and has no meaning in an + IKE_SA proposal. Emitting it there breaks interoperability + with implementations that reject the malformed payload + without replying at all (observed with Cisco FTD, T9254). + """ pfs_lut = { 'dh-group1' : 'modp768', 'dh-group2' : 'modp1024', @@ -533,7 +542,7 @@ def get_esp_ike_cipher(group_config, ike_group=None): # For 'optional' and 'disabled' we need two values as # proposal without '-esn'/'-noesn' is incompatible with # proposals with any of them. - if 'esn' in proposal: + if esn and 'esn' in proposal: if proposal['esn'] == 'required': tmp += '-esn' elif proposal['esn'] == 'optional': diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index a73cd0b2bcb..252e881df85 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -252,7 +252,7 @@ def test_site_to_site(self): f'life_bytes = {life_bytes}', f'life_packets = {life_packets}', f'rekey_time = 28800s', # default value - f'proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', + f'proposals = aes128-sha1-modp1024', f'esp_proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', f'life_time = 3600s', # default value f'local_addrs = {local_address} # dhcp:no', @@ -406,7 +406,7 @@ def test_site_to_site_with_default_ts(self): f'life_bytes = {life_bytes}', f'life_packets = {life_packets}', f'rekey_time = 28800s', # default value - f'proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', + f'proposals = aes128-sha1-modp1024', f'esp_proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', f'life_time = 3600s', # default value f'local_addrs = {local_address} # dhcp:no', @@ -482,7 +482,7 @@ def test_site_to_site_gre_over_ipsec(self): swanctl_conf_lines = [ 'version = 2', 'auth = psk', - 'proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024,aes256-sha1-modp1536-noesn,aes256-sha1-modp1536', + 'proposals = aes128-sha1-modp1024,aes256-sha1-modp1536', 'esp_proposals = aes128-sha1-modp2048-noesn,aes128-sha1-modp2048,aes256-sha1-modp2048-noesn,aes256-sha1-modp2048', 'life_time = 3600s', 'mode = transport', # ensure transport mode is used @@ -549,7 +549,7 @@ def test_site_to_site_vti(self): swanctl_conf_lines = [ f'version = 2', f'auth = psk', - f'proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', + f'proposals = aes128-sha1-modp1024', f'esp_proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', f'local_addrs = {local_address} # dhcp:no', f'mobike = no', @@ -625,7 +625,7 @@ def test_site_to_site_vti_ts_afi(self): swanctl_conf_lines = [ f'version = 2', f'auth = psk', - f'proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', + f'proposals = aes128-sha1-modp1024', f'esp_proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', f'local_addrs = {local_address} # dhcp:no', f'mobike = no', @@ -765,7 +765,7 @@ def test_site_to_site_nist_800_77_cnsa_1_with_ppk(self): f'version = 2', f'auth = psk', f'rekey_time = 86400s', - f'proposals = aes256gcm128-sha384-prfsha384-ecp384-noesn,aes256gcm128-sha384-prfsha384-ecp384', + f'proposals = aes256gcm128-sha384-prfsha384-ecp384', f'esp_proposals = aes256gcm128-sha384-ecp384-noesn,aes256gcm128-sha384-ecp384', f'life_time = 28800s', # default value f'local_addrs = {local_address} # dhcp:no', @@ -869,7 +869,7 @@ def test_dmvpn(self): swanctl_conf = read_file(swanctl_file) swanctl_lines = [ - f'proposals = aes256-sha1-prfsha1-modp1024-noesn,aes256-sha1-prfsha1-modp1024', + f'proposals = aes256-sha1-prfsha1-modp1024', f'version = 1', f'rekey_time = {ike_lifetime}s', f'rekey_time = {esp_lifetime}s', @@ -932,7 +932,7 @@ def test_site_to_site_x509(self): f'id = "{peer_name}"', f'auth = pubkey', f'certs = {peer_name}.pem', - f'proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', + f'proposals = aes128-sha1-modp1024', f'esp_proposals = aes128-sha1-modp1024-noesn,aes128-sha1-modp1024', f'local_addrs = {local_address} # dhcp:no', f'remote_addrs = {peer_ip}', @@ -1188,7 +1188,7 @@ def test_remote_access(self): f'{conn_name}', f'remote_addrs = %any', f'local_addrs = {local_address}', - f'proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048,aes256-sha256-modp2048-noesn,aes256-sha256-modp2048,aes256-sha256-modp1024-noesn,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048-noesn,aes128gcm128-sha256-modp2048', + f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048', f'version = 2', f'send_certreq = no', f'rekey_time = {ike_lifetime}s', @@ -1308,7 +1308,7 @@ def test_remote_access_eap_tls(self): f'{conn_name}', f'remote_addrs = %any', f'local_addrs = {local_address}', - f'proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048,aes256-sha256-modp2048-noesn,aes256-sha256-modp2048,aes256-sha256-modp1024-noesn,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048-noesn,aes128gcm128-sha256-modp2048', + f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048', f'version = 2', f'send_certreq = no', f'rekey_time = {ike_lifetime}s', @@ -1424,7 +1424,7 @@ def test_remote_access_x509(self): f'{conn_name}', f'remote_addrs = %any', f'local_addrs = {local_address}', - f'proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048,aes256-sha256-modp2048-noesn,aes256-sha256-modp2048,aes256-sha256-modp1024-noesn,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048-noesn,aes128gcm128-sha256-modp2048', + f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048', f'version = 2', f'send_certreq = no', f'rekey_time = {ike_lifetime}s', @@ -1622,7 +1622,7 @@ def test_remote_access_no_rekey(self): f'{conn_name}', f'remote_addrs = %any', f'local_addrs = {local_address}', - f'proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048,aes256-sha256-modp2048-noesn,aes256-sha256-modp2048,aes256-sha256-modp1024-noesn,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048-noesn,aes128gcm128-sha256-modp2048', + f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048', f'version = 2', f'send_certreq = no', f'rekey_time = 0s', @@ -1735,7 +1735,7 @@ def test_remote_access_pool_range(self): f'{conn_name}', f'remote_addrs = %any', f'local_addrs = {local_address}', - f'proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048,aes256-sha256-modp2048-noesn,aes256-sha256-modp2048,aes256-sha256-modp1024-noesn,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048-noesn,aes128gcm128-sha256-modp2048', + f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048', f'version = 2', f'send_certreq = no', f'rekey_time = {ike_lifetime}s', @@ -1868,7 +1868,7 @@ def test_remote_access_vti(self): f'{conn_name}', f'remote_addrs = %any', f'local_addrs = {local_address}', - f'proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048,aes256-sha256-modp2048-noesn,aes256-sha256-modp2048,aes256-sha256-modp1024-noesn,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048-noesn,aes128gcm128-sha256-modp2048', + f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048', f'version = 2', f'send_certreq = no', f'rekey_time = {ike_lifetime}s', @@ -2010,7 +2010,7 @@ def test_esn_settings(self): # esn - default, disabled swanctl_conf = read_file(swanctl_file) swanctl_conf_lines = [ - f'proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048', + f'proposals = aes256-sha512-modp2048', f'esp_proposals = aes256-sha512-modp2048-noesn,aes256-sha512-modp2048', ] for line in swanctl_conf_lines: @@ -2023,7 +2023,7 @@ def test_esn_settings(self): swanctl_conf = read_file(swanctl_file) swanctl_conf_lines = [ - f'proposals = aes256-sha512-modp2048-esn-noesn,aes256-sha512-modp2048', + f'proposals = aes256-sha512-modp2048', f'esp_proposals = aes256-sha512-modp2048-esn-noesn,aes256-sha512-modp2048', ] for line in swanctl_conf_lines: @@ -2036,7 +2036,7 @@ def test_esn_settings(self): swanctl_conf = read_file(swanctl_file) swanctl_conf_lines = [ - f'proposals = aes256-sha512-modp2048-esn', + f'proposals = aes256-sha512-modp2048', f'esp_proposals = aes256-sha512-modp2048-esn', ] for line in swanctl_conf_lines: