summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOleksandr Kuchmystyi <o.kuchmystyi@vyos.io>2026-05-01 16:51:32 +0300
committerOleksandr Kuchmystyi <o.kuchmystyi@vyos.io>2026-05-25 15:39:27 +0300
commit464fdf6da24c03ef725eeb6c52a9a1849a788ffd (patch)
tree27f372a07927aa1bc44c6844286394b7020de513 /src
parentdb23d7201c9df78c91aeeeade6963f2cc12b50b1 (diff)
downloadvyos-1x-464fdf6da24c03ef725eeb6c52a9a1849a788ffd.tar.gz
vyos-1x-464fdf6da24c03ef725eeb6c52a9a1849a788ffd.zip
openvpn: T6478: Skip restart service when only client config changed
OpenVPN server reads per-client files from `--client-config-dir` at connect time. When only `interfaces openvpn vtunN server client <client>` entries are modified there is no need to send SIGHUP or restart the service - the new CCD files written by `generate()` will be picked up automatically on the next client connection.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/interfaces_openvpn.py30
1 files changed, 29 insertions, 1 deletions
diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py
index ab27fe66e..d6b63ae2a 100755
--- a/src/conf_mode/interfaces_openvpn.py
+++ b/src/conf_mode/interfaces_openvpn.py
@@ -78,6 +78,28 @@ otp_file = '/config/auth/openvpn/{ifname}-otp-secrets'
secret_chars = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
service_file = '/run/systemd/system/openvpn@{ifname}.service.d/20-override.conf'
+def _only_client_config_changed(conf, base, ifname):
+ """
+ Return True when the sole diff under this interface is a change to
+ `server.client` entries (i.e. CCD files).
+ """
+
+ iface_path = base + [ifname]
+ diff = get_config_diff(conf)
+
+ def _has_only_changes(path, node):
+ changes = diff.node_changed_children(path)
+ return len(changes) == 1 and changes[0] == node
+
+ # Something outside of 'server' also changed - not a CCD-only change
+ if _has_only_changes(iface_path, 'server'):
+ # Something outside of 'server.client' also changed - not a CCD-only change
+ if _has_only_changes(iface_path + ['server'], 'client'):
+ return True
+
+ return False
+
+
def get_config(config=None):
"""
Retrieve CLI config as dictionary. Dictionary can never be empty, as at least the
@@ -117,6 +139,12 @@ def get_config(config=None):
if is_node_changed(conf, base + [ifname, 'enable-dco']):
openvpn.update({'restart_required': {}})
+ # Detect changes that are limited to per-client CCD entries (T6478).
+ # OpenVPN reads client-config-dir files at connect time, so adding or
+ # updating them requires neither a SIGHUP nor a service restart.
+ if 'restart_required' not in openvpn and openvpn['mode'] == 'server':
+ openvpn['client_only_changed'] = _only_client_config_changed(conf, base, ifname)
+
# We have to get the dict using 'get_config_dict' instead of 'get_interface_dict'
# as 'get_interface_dict' merges the defaults in, so we can not check for defaults in there.
tmp = conf.get_config_dict(base + [openvpn['ifname']], get_first_key=True)
@@ -818,7 +846,7 @@ def apply(openvpn):
# No matching OpenVPN process running - maybe it got killed or none
# existed - nevertheless, spawn new OpenVPN process
- if not openvpn.get('no_restart_crl'):
+ if not openvpn.get('no_restart_crl') and not openvpn.get('client_only_changed'):
action = 'reload-or-restart'
if 'restart_required' in openvpn:
action = 'restart'