summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2026-05-26 17:47:15 +0300
committerGitHub <noreply@github.com>2026-05-26 17:47:15 +0300
commitf333e72b999c296565fc23e79b3bf651dc85afb0 (patch)
tree9af1e5784a514c83b66abd36acfb24d53ca8537b
parent4574af601e2642f1b0437763195d94b9a4486b8b (diff)
parentb6d24956bd4c7a5e4cb89c0b4331264a96667184 (diff)
downloadvyos-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.j210
-rw-r--r--interface-definitions/vpn_ipsec.xml.in2
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py71
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py16
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]: