summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas Christian <lucas@lucasec.com>2024-03-10 11:39:19 -0700
committerLucas Christian <lucas@lucasec.com>2024-03-11 23:08:40 -0700
commit679b78356cbda4de15f96a7f22d4a98037dbeea4 (patch)
treece5c7efbe58e11ba702000ce1eb2fb42823b5d33
parent92012a0b3db8e93b10db4137414073f0371ed8cc (diff)
downloadvyos-1x-679b78356cbda4de15f96a7f22d4a98037dbeea4.tar.gz
vyos-1x-679b78356cbda4de15f96a7f22d4a98037dbeea4.zip
T5872: re-write exit hook to always regenerate config
-rw-r--r--data/templates/ipsec/swanctl/peer.j22
-rw-r--r--data/templates/ipsec/swanctl/remote_access.j22
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py14
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py11
-rwxr-xr-xsrc/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook58
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 <http://www.gnu.org/licenses/>.
-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 - <<PYEND
-import os
-import re
-
-from vyos.utils.process import call
-from vyos.utils.process import cmd
-from vyos.utils.file import read_file
-from vyos.utils.file import write_file
-
-SWANCTL_CONF="/etc/swanctl/swanctl.conf"
+# Best effort wait for any active commit to finish
+sudo python3 - <<PYEND
+from vyos.utils.commit import wait_for_commit_lock
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).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, "\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