diff options
| author | Viacheslav Hletenko <v.gletenko@vyos.io> | 2026-05-26 17:47:15 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-26 17:47:15 +0300 |
| commit | f333e72b999c296565fc23e79b3bf651dc85afb0 (patch) | |
| tree | 9af1e5784a514c83b66abd36acfb24d53ca8537b | |
| parent | 4574af601e2642f1b0437763195d94b9a4486b8b (diff) | |
| parent | b6d24956bd4c7a5e4cb89c0b4331264a96667184 (diff) | |
| download | vyos-1x-f333e72b999c296565fc23e79b3bf651dc85afb0.tar.gz vyos-1x-f333e72b999c296565fc23e79b3bf651dc85afb0.zip | |
Merge pull request #5197 from alexandr-san4ez/T7555-current
ipsec: T7555: Implement `ikev2-reauth` for site-to-site peers
| -rw-r--r-- | data/templates/ipsec/swanctl/peer.j2 | 10 | ||||
| -rw-r--r-- | interface-definitions/vpn_ipsec.xml.in | 2 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_vpn_ipsec.py | 71 | ||||
| -rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 16 |
4 files changed, 98 insertions, 1 deletions
diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2 index 24d037fe1..96f761cce 100644 --- a/data/templates/ipsec/swanctl/peer.j2 +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -3,6 +3,13 @@ {# peer needs to reference the global IKE configuration for certain values #} {% set ike = ike_group[peer_conf.ike_group] %} {{ name }} { +{# Resolve effective reauth setting: peer overrides ike-group #} +{% set reauth = peer_conf.ikev2_reauth if peer_conf.ikev2_reauth is vyos_defined else '' %} +{% if reauth == 'inherit' %} +{% set reauth = ike.ikev2_reauth is vyos_defined %} +{% else %} +{% set reauth = reauth == 'yes' %} +{% endif %} {% if peer_conf.authentication.ppk.id is vyos_defined %} ppk_id = {{ peer_conf.authentication.ppk.id }} {% endif %} @@ -36,6 +43,9 @@ reauth_time = 0 {% elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %} keyingtries = 0 +{% if reauth and ike.key_exchange is vyos_defined('ikev2') %} + reauth_time = {{ ike.lifetime }}s +{% endif %} {% elif peer_conf.connection_type is vyos_defined('trap') %} keyingtries = 1 {% endif %} diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index d141a43bd..403031131 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -1246,7 +1246,7 @@ </completionHelp> <valueHelp> <format>yes</format> - <description>Enable remote host re-authentication during an IKE re-key. Currently broken due to a strong swan bug</description> + <description>Enable remote host re-authentication during an IKE re-key</description> </valueHelp> <valueHelp> <format>no</format> diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 3e60e51b2..68aef845e 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -961,6 +961,77 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): # Disable PKI self.tearDownPKI() + def test_site_to_site_ikev2_reauth(self): + # T7555: Verify ikev2-reauth is correctly written to swanctl.conf + # and that invalid combinations are rejected by validation + + local_address = '192.0.2.10' + ike_lifetime = '1800' + + # Base PSK auth used across all sub-tests + psk_base_path = base_path + ['authentication', 'psk', connection_name] + self.cli_set(psk_base_path + ['id', local_id]) + self.cli_set(psk_base_path + ['id', remote_id]) + self.cli_set(psk_base_path + ['id', local_address]) + self.cli_set(psk_base_path + ['id', peer_ip]) + self.cli_set(psk_base_path + ['secret', secret]) + + 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 + ['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', '10.0.0.0/24'], + ) + self.cli_set( + peer_base_path + ['tunnel', '1', 'remote', 'prefix', '10.1.0.0/24'], + ) + + # ikev2-reauth on an IKEv1-only ike-group must be rejected + 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(peer_base_path + ['ike-group', ike_group]) + self.cli_set(peer_base_path + ['ikev2-reauth', 'yes']) + + err_msg = 'ikev2-reauth requires key-exchange ikev2 in IKE group' + with self.assertRaisesRegex(ConfigSessionError, err_msg): + self.cli_commit() + + # Switch to IKEv2, enable reauth on the ike-group (valueless flag) + self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) + self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth']) + self.cli_set(peer_base_path + ['ikev2-reauth', 'inherit']) + self.cli_commit() + + swanctl_conf = read_file(swanctl_file) + self.assertIn(f'reauth_time = {ike_lifetime}s', swanctl_conf) + + # ikev2-reauth = yes on peer overrides group + self.cli_delete(base_path + ['ike-group', ike_group, 'ikev2-reauth']) + self.cli_set(peer_base_path + ['ikev2-reauth', 'yes']) + self.cli_commit() + + swanctl_conf = read_file(swanctl_file) + self.assertIn(f'reauth_time = {ike_lifetime}s', swanctl_conf) + + # ikev2-reauth = no suppresses group flag + self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth']) + self.cli_set(peer_base_path + ['ikev2-reauth', 'no']) + self.cli_commit() + + swanctl_conf = read_file(swanctl_file) + self.assertNotIn(f'reauth_time = {ike_lifetime}s', swanctl_conf) + + # connection-type trap: reauth must be suppressed + self.cli_set(peer_base_path + ['connection-type', 'trap']) + self.cli_set(peer_base_path + ['ikev2-reauth', 'yes']) + self.cli_commit() + + swanctl_conf = read_file(swanctl_file) + self.assertNotIn(f'reauth_time = {ike_lifetime}s', swanctl_conf) + self.assertIn('keyingtries = 1', swanctl_conf) + def test_flex_vpn_vips(self): local_address = '192.0.2.5' diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 53cf4ed36..35b00dada 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -710,6 +710,22 @@ def verify(ipsec): f'Childless IKE SAs be used with IKEv2! Please configure IKEv2 key-exchange in ike-group "{ike}".' ) + # Get the referenced IKE group config + ike_group_name = peer_conf.get('ike_group') + ike_group = ipsec['ike_group'].get(ike_group_name, {}) + + # 'ikev2-reauth' only valid for IKEv2 + peer_reauth = peer_conf.get('ikev2_reauth') + reauth_ike_group_configured = ( + peer_reauth == 'inherit' and 'ikev2_reauth' in ike_group + ) + if peer_reauth == 'yes' or reauth_ike_group_configured: + if ike_group.get('key_exchange') != 'ikev2': + raise ConfigError( + 'ikev2-reauth requires key-exchange ikev2 in IKE group! ' + f'Please configure IKEv2 key-exchange in ike-group "{ike_group_name}".' + ) + def cleanup_pki_files(): for path in [CERT_PATH, CA_PATH, CRL_PATH, KEY_PATH, PUBKEY_PATH]: |
