From f7834324d3d9edd7e161e7f2f3868452997c9c81 Mon Sep 17 00:00:00 2001 From: Lucas Christian Date: Thu, 28 Dec 2023 22:11:26 -0800 Subject: T5872: ipsec remote access VPN: support dhcp-interface. --- data/templates/ipsec/swanctl/remote_access.j2 | 2 +- interface-definitions/vpn_ipsec.xml.in | 1 + smoketest/scripts/cli/test_vpn_ipsec.py | 61 +++++++++++++++++++++++++++ src/conf_mode/vpn_ipsec.py | 38 ++++++++++++++++- 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2 index bce8684fe..af7f2994e 100644 --- a/data/templates/ipsec/swanctl/remote_access.j2 +++ b/data/templates/ipsec/swanctl/remote_access.j2 @@ -4,7 +4,7 @@ {% set esp = esp_group[rw_conf.esp_group] %} ra-{{ name }} { remote_addrs = %any - local_addrs = {{ rw_conf.local_address if rw_conf.local_address is vyos_defined else '%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(',') }} version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }} send_certreq = no diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index 44ca1c7a0..833019d68 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -825,6 +825,7 @@ #include #include #include + #include #include #include diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 09e10a2c4..00f9e8f46 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -898,5 +898,66 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.tearDownPKI() + def test_remote_access_dhcp_fail_handling(self): + # Skip process check - connection is not created for this test + self.skip_process_check = True + + # Interface for dhcp-interface + self.cli_set(ethernet_path + [interface, 'vif', vif, 'address', 'dhcp']) # Use VLAN to avoid getting IP from qemu dhcp server + + # This is a known to be good configuration for Microsoft Windows 10 and Apple iOS 17 + self.setupPKI() + + ike_group = 'IKE-RW' + esp_group = 'ESP-RW' + + conn_name = 'vyos-rw' + ip_pool_name = 'ra-rw-ipv4' + username = 'vyos' + password = 'secret' + ike_lifetime = '7200' + eap_lifetime = '3600' + local_id = 'ipsec.vyos.net' + + name_server = '172.16.254.100' + prefix = '172.16.250.0/28' + + # IKE + self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) + self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime]) + self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'dh-group', '14']) + self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'encryption', 'aes256']) + self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'hash', 'sha512']) + + # ESP + self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', eap_lifetime]) + self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes256']) + self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha512']) + + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-id', local_id]) + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-users', 'username', username, 'password', password]) + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'server-mode', 'x509']) + + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'certificate', peer_name]) + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', ca_name]) + + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'esp-group', esp_group]) + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'ike-group', ike_group]) + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'dhcp-interface', f'{interface}.{vif}']) + self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'pool', ip_pool_name]) + self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'name-server', name_server]) + self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'prefix', prefix]) + + self.cli_commit() + + self.assertTrue(os.path.exists(dhcp_waiting_file)) + + dhcp_waiting = read_file(dhcp_waiting_file) + self.assertIn(f'{interface}.{vif}', dhcp_waiting) # Ensure dhcp-failed interface was added for dhclient hook + + self.cli_delete(ethernet_path + [interface, 'vif', vif, 'address']) + + self.tearDownPKI() + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 388f2a709..64d0f6d9d 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -229,6 +229,30 @@ def verify(ipsec): if 'remote_access' in ipsec: if 'connection' in ipsec['remote_access']: for name, ra_conf in ipsec['remote_access']['connection'].items(): + if 'local_address' not in ra_conf and 'dhcp_interface' not in ra_conf: + raise ConfigError(f"Missing local-address or dhcp-interface on remote-access connection {name}") + + if 'dhcp_interface' in ra_conf: + dhcp_interface = ra_conf['dhcp_interface'] + + verify_interface_exists(dhcp_interface) + dhcp_base = directories['isc_dhclient_dir'] + + if not os.path.exists(f'{dhcp_base}/dhclient_{dhcp_interface}.conf'): + raise ConfigError(f"Invalid dhcp-interface on remote-access connection {name}") + + address = get_dhcp_address(dhcp_interface) + count = 0 + while not address and count < dhcp_wait_attempts: + address = get_dhcp_address(dhcp_interface) + count += 1 + sleep(dhcp_wait_sleep) + + if not address: + ipsec['dhcp_no_address'][f'ra_{name}'] = dhcp_interface + print(f"Failed to get address from dhcp-interface on remote-access connection {name} -- skipped") + continue + if 'esp_group' in ra_conf: if 'esp_group' not in ipsec or ra_conf['esp_group'] not in ipsec['esp_group']: raise ConfigError(f"Invalid esp-group on {name} remote-access config") @@ -394,7 +418,7 @@ def verify(ipsec): sleep(dhcp_wait_sleep) if not address: - ipsec['dhcp_no_address'][peer] = dhcp_interface + ipsec['dhcp_no_address'][f'peer_{peer}'] = dhcp_interface print(f"Failed to get address from dhcp-interface on site-to-site peer {peer} -- skipped") continue @@ -522,13 +546,23 @@ def generate(ipsec): if 'remote_access' in ipsec and 'connection' in ipsec['remote_access']: for rw, rw_conf in ipsec['remote_access']['connection'].items(): + if f'ra_{rw}' in ipsec['dhcp_no_address']: + continue + + local_ip = '' + if 'local_address' in rw_conf: + local_ip = rw_conf['local_address'] + elif 'dhcp_interface' in rw_conf: + local_ip = get_dhcp_address(rw_conf['dhcp_interface']) + + ipsec['remote_access']['connection'][rw]['local_address'] = local_ip if 'authentication' in rw_conf and 'x509' in rw_conf['authentication']: generate_pki_files_x509(ipsec['pki'], rw_conf['authentication']['x509']) if 'site_to_site' in ipsec and 'peer' in ipsec['site_to_site']: for peer, peer_conf in ipsec['site_to_site']['peer'].items(): - if peer in ipsec['dhcp_no_address']: + if f'peer_{peer}' in ipsec['dhcp_no_address']: continue if peer_conf['authentication']['mode'] == 'x509': -- cgit v1.2.3 From cd8ef21f280f726955f537132e3fab2bcb3c286f Mon Sep 17 00:00:00 2001 From: Lucas Christian Date: Thu, 8 Feb 2024 22:04:16 -0800 Subject: T5872: fix ipsec dhclient exit hook --- data/templates/ipsec/swanctl/peer.j2 | 2 +- data/templates/ipsec/swanctl/remote_access.j2 | 2 +- .../dhclient-exit-hooks.d/99-ipsec-dhclient-hook | 27 ++++++++-------------- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2 index 58f0199fa..f38691884 100644 --- a/data/templates/ipsec/swanctl/peer.j2 +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -8,7 +8,7 @@ {% 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 '%any' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} + 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' }} reset:{{ name }} 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 diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2 index af7f2994e..d1d6d2478 100644 --- a/data/templates/ipsec/swanctl/remote_access.j2 +++ b/data/templates/ipsec/swanctl/remote_access.j2 @@ -4,7 +4,7 @@ {% set esp = esp_group[rw_conf.esp_group] %} ra-{{ name }} { 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' }} + 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' }} reset:ra-{{ name }} proposals = {{ ike_group[rw_conf.ike_group] | get_esp_ike_cipher | join(',') }} version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }} send_certreq = no diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook index e6edc1ac3..4dc52c6db 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook @@ -43,39 +43,30 @@ from vyos.utils.file import write_file SWANCTL_CONF="/etc/swanctl/swanctl.conf" -def ipsec_down(ip_address): - # This prevents the need to restart ipsec and kill all active connections, only the stale connection is closed - status = cmd('sudo ipsec statusall') - connection_name = None - for line in status.split("\n"): - if line.find(ip_address) > 0: - regex_match = re.search(r'(peer_[^:\[]+)', line) - if regex_match: - connection_name = regex_match[1] - break - if connection_name: - call(f'sudo ipsec down {connection_name}') - if __name__ == '__main__': interface = os.getenv('interface') new_ip = os.getenv('new_ip_address') old_ip = os.getenv('old_ip_address') if os.path.exists(SWANCTL_CONF): - conf_lines = read_file(SWANCTL_CONF) + conf_lines = read_file(SWANCTL_CONF).split("\n") found = False + reset_conns = set() to_match = f'# dhcp:{interface}' for i, line in enumerate(conf_lines): if line.find(to_match) > 0: conf_lines[i] = line.replace(old_ip, new_ip) found = True + regex_match = re.search(r'#.* reset:([-_a-zA-Z0-9|@]+)', line) + if regex_match: + connection_name = regex_match[1] + reset_conns.add(connection_name) if found: - write_file(SWANCTL_CONF, conf_lines) - ipsec_down(old_ip) - call('sudo ipsec rereadall') - call('sudo ipsec reload') + write_file(SWANCTL_CONF, "\n".join(conf_lines)) + for connection_name in reset_conns: + call(f'sudo swanctl -t -i {connection_name}') call('sudo swanctl -q') exit(0) -- cgit v1.2.3 From 92012a0b3db8e93b10db4137414073f0371ed8cc Mon Sep 17 00:00:00 2001 From: Lucas Christian Date: Sat, 10 Feb 2024 11:26:47 -0800 Subject: T5872: further fixes to ipsec dhcp exit hook --- src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook index 4dc52c6db..3f0c9cb7a 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook @@ -14,22 +14,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -if [ "$reason" == "REBOOT" ] || [ "$reason" == "EXPIRE" ]; then - return 0 -fi - DHCP_HOOK_IFLIST="/tmp/ipsec_dhcp_waiting" if [ -f $DHCP_HOOK_IFLIST ] && [ "$reason" == "BOUND" ]; then if grep -qw $interface $DHCP_HOOK_IFLIST; then sudo rm $DHCP_HOOK_IFLIST sudo /usr/libexec/vyos/conf_mode/vpn_ipsec.py - return 0 + exit 0 fi fi -if [ "$old_ip_address" == "$new_ip_address" ] && [ "$reason" == "BOUND" ]; then - return 0 +if [ "$reason" == "RENEW" ] || [ "$reason" == "REBIND" ]; then + if [ "$old_ip_address" == "$new_ip_address" ]; then + exit 0 + fi +else + exit 0 fi python3 - < Date: Sun, 10 Mar 2024 11:39:19 -0700 Subject: T5872: re-write exit hook to always regenerate config --- data/templates/ipsec/swanctl/peer.j2 | 2 +- data/templates/ipsec/swanctl/remote_access.j2 | 2 +- smoketest/scripts/cli/test_vpn_ipsec.py | 14 +++--- src/conf_mode/vpn_ipsec.py | 11 ++-- .../dhclient-exit-hooks.d/99-ipsec-dhclient-hook | 58 ++++++---------------- 5 files changed, 32 insertions(+), 55 deletions(-) diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2 index f38691884..58f0199fa 100644 --- a/data/templates/ipsec/swanctl/peer.j2 +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -8,7 +8,7 @@ {% 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 '%any' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} reset:{{ name }} + 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 diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2 index d1d6d2478..af7f2994e 100644 --- a/data/templates/ipsec/swanctl/remote_access.j2 +++ b/data/templates/ipsec/swanctl/remote_access.j2 @@ -4,7 +4,7 @@ {% set esp = esp_group[rw_conf.esp_group] %} ra-{{ name }} { 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' }} reset:ra-{{ name }} + 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(',') }} version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }} send_certreq = no diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 00f9e8f46..1a5e47144 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -31,7 +31,7 @@ nhrp_path = ['protocols', 'nhrp'] base_path = ['vpn', 'ipsec'] charon_file = '/etc/strongswan.d/charon.conf' -dhcp_waiting_file = '/tmp/ipsec_dhcp_waiting' +dhcp_interfaces_file = '/tmp/ipsec_dhcp_interfaces' swanctl_file = '/etc/swanctl/swanctl.conf' peer_ip = '203.0.113.45' @@ -178,10 +178,10 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_commit() - self.assertTrue(os.path.exists(dhcp_waiting_file)) + self.assertTrue(os.path.exists(dhcp_interfaces_file)) - dhcp_waiting = read_file(dhcp_waiting_file) - self.assertIn(f'{interface}.{vif}', dhcp_waiting) # Ensure dhcp-failed interface was added for dhclient hook + dhcp_interfaces = read_file(dhcp_interfaces_file) + self.assertIn(f'{interface}.{vif}', dhcp_interfaces) # Ensure dhcp interface was added for dhclient hook self.cli_delete(ethernet_path + [interface, 'vif', vif, 'address']) @@ -950,10 +950,10 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_commit() - self.assertTrue(os.path.exists(dhcp_waiting_file)) + self.assertTrue(os.path.exists(dhcp_interfaces_file)) - dhcp_waiting = read_file(dhcp_waiting_file) - self.assertIn(f'{interface}.{vif}', dhcp_waiting) # Ensure dhcp-failed interface was added for dhclient hook + dhcp_interfaces = read_file(dhcp_interfaces_file) + self.assertIn(f'{interface}.{vif}', dhcp_interfaces) # Ensure dhcp interface was added for dhclient hook self.cli_delete(ethernet_path + [interface, 'vif', vif, 'address']) diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 64d0f6d9d..ebfb21903 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -75,7 +75,7 @@ KEY_PATH = f'{swanctl_dir}/private/' CA_PATH = f'{swanctl_dir}/x509ca/' CRL_PATH = f'{swanctl_dir}/x509crl/' -DHCP_HOOK_IFLIST = '/tmp/ipsec_dhcp_waiting' +DHCP_HOOK_IFLIST = '/tmp/ipsec_dhcp_interfaces' def get_config(config=None): if config: @@ -94,6 +94,7 @@ def get_config(config=None): with_recursive_defaults=True, with_pki=True) + ipsec['dhcp_interfaces'] = set() ipsec['dhcp_no_address'] = {} ipsec['install_routes'] = 'no' if conf.exists(base + ["options", "disable-route-autoinstall"]) else default_install_routes ipsec['interface_change'] = leaf_node_changed(conf, base + ['interface']) @@ -241,6 +242,8 @@ def verify(ipsec): if not os.path.exists(f'{dhcp_base}/dhclient_{dhcp_interface}.conf'): raise ConfigError(f"Invalid dhcp-interface on remote-access connection {name}") + ipsec['dhcp_interfaces'].add(dhcp_interface) + address = get_dhcp_address(dhcp_interface) count = 0 while not address and count < dhcp_wait_attempts: @@ -410,6 +413,8 @@ def verify(ipsec): if not os.path.exists(f'{dhcp_base}/dhclient_{dhcp_interface}.conf'): raise ConfigError(f"Invalid dhcp-interface on site-to-site peer {peer}") + ipsec['dhcp_interfaces'].add(dhcp_interface) + address = get_dhcp_address(dhcp_interface) count = 0 while not address and count < dhcp_wait_attempts: @@ -527,9 +532,9 @@ def generate(ipsec): render(charon_conf, 'ipsec/charon.j2', {'install_routes': default_install_routes}) return - if ipsec['dhcp_no_address']: + if ipsec['dhcp_interfaces']: with open(DHCP_HOOK_IFLIST, 'w') as f: - f.write(" ".join(ipsec['dhcp_no_address'].values())) + f.write(" ".join(ipsec['dhcp_interfaces'])) elif os.path.exists(DHCP_HOOK_IFLIST): os.unlink(DHCP_HOOK_IFLIST) diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook index 3f0c9cb7a..ebb100e8b 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook @@ -14,60 +14,32 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -DHCP_HOOK_IFLIST="/tmp/ipsec_dhcp_waiting" +DHCP_HOOK_IFLIST="/tmp/ipsec_dhcp_interfaces" -if [ -f $DHCP_HOOK_IFLIST ] && [ "$reason" == "BOUND" ]; then - if grep -qw $interface $DHCP_HOOK_IFLIST; then - sudo rm $DHCP_HOOK_IFLIST - sudo /usr/libexec/vyos/conf_mode/vpn_ipsec.py - exit 0 - fi +if ! { [ -f $DHCP_HOOK_IFLIST ] && grep -qw $interface $DHCP_HOOK_IFLIST; }; then + exit 0 fi +# Re-generate the config on the following events: +# - BOUND: always re-generate +# - RENEW: re-generate if the IP address changed +# - REBIND: re-generate if the IP address changed if [ "$reason" == "RENEW" ] || [ "$reason" == "REBIND" ]; then if [ "$old_ip_address" == "$new_ip_address" ]; then exit 0 fi -else +elif [ "$reason" != "BOUND" ]; then exit 0 fi -python3 - < 0: - conf_lines[i] = line.replace(old_ip, new_ip) - found = True - regex_match = re.search(r'#.* reset:([-_a-zA-Z0-9|@]+)', line) - if regex_match: - connection_name = regex_match[1] - reset_conns.add(connection_name) - - if found: - write_file(SWANCTL_CONF, "\n".join(conf_lines)) - for connection_name in reset_conns: - call(f'sudo swanctl -t -i {connection_name}') - call('sudo swanctl -q') - + wait_for_commit_lock() exit(0) PYEND + +# Now re-generate the config +sudo /usr/libexec/vyos/conf_mode/vpn_ipsec.py -- cgit v1.2.3