From bc2bb1ed9ac977c8ad540d7ccbfff8e9980277cf Mon Sep 17 00:00:00 2001 From: fett0 Date: Wed, 31 Jul 2024 18:21:25 +0000 Subject: [PATCH 1/5] OPENVPN: T6555: add server-bridge options in mode server (cherry picked from commit 4acad3eb8d9be173b76fecafc32b0c70eae9b192) --- data/templates/openvpn/server.conf.j2 | 4 +- .../interfaces_openvpn.xml.in | 56 +++++++++++++++++++ .../scripts/cli/test_interfaces_openvpn.py | 55 ++++++++++++++++++ src/conf_mode/interfaces_openvpn.py | 16 ++++++ 4 files changed, 130 insertions(+), 1 deletion(-) diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2 index 6ac5254437..b6e90ed180 100644 --- a/data/templates/openvpn/server.conf.j2 +++ b/data/templates/openvpn/server.conf.j2 @@ -90,7 +90,9 @@ server-ipv6 {{ subnet }} {% endif %} {% endfor %} {% endif %} - +{% if server.server_bridge is vyos_defined and server.server_bridge.disable is not vyos_defined %} +server-bridge {{ server.server_bridge.gateway }} {{ server.server_bridge.subnet_mask }} {{ server.server_bridge.start }} {{ server.server_bridge.stop if server.server_bridge.stop is vyos_defined }} +{% endif %} {% if server.client_ip_pool is vyos_defined and server.client_ip_pool.disable is not vyos_defined %} ifconfig-pool {{ server.client_ip_pool.start }} {{ server.client_ip_pool.stop }} {{ server.client_ip_pool.subnet_mask if server.client_ip_pool.subnet_mask is vyos_defined }} {% endif %} diff --git a/interface-definitions/interfaces_openvpn.xml.in b/interface-definitions/interfaces_openvpn.xml.in index 1860523c2b..46c22ac447 100644 --- a/interface-definitions/interfaces_openvpn.xml.in +++ b/interface-definitions/interfaces_openvpn.xml.in @@ -445,6 +445,62 @@ + + + Used with TAP device (layer 2) + + + #include + + + First IP address in the pool + + + + + ipv4 + IPv4 address + + + + + + Last IP address in the pool + + + + + ipv4 + IPv4 address + + + + + + Subnet mask pushed to dynamic clients. + + + + + ipv4 + IPv4 subnet mask + + + + + + Gateway IP address + + + + + ipv4 + IPv4 address + + + + + Pool of client IPv4 addresses diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index 9ca661e872..53cfa6cf53 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -627,5 +627,60 @@ def test_openvpn_site2site_interfaces_tun(self): self.assertNotIn(interface, interfaces()) + def test_openvpn_server_server_bridge(self): + # Create OpenVPN server interface using server-bridge. + # Validate configuration afterwards. + br_if = 'br0' + vtun_if = 'vtun5010' + auth_hash = 'sha256' + path = base_path + [vtun_if] + start_subnet = "192.168.0.100" + stop_subnet = "192.168.0.200" + mask_subnet = "255.255.255.0" + gw_subnet = "192.168.0.1" + + self.cli_set(['interfaces', 'bridge', br_if, 'member', 'interface', vtun_if]) + self.cli_set(path + ['device-type', 'tap']) + self.cli_set(path + ['encryption', 'data-ciphers', 'aes192']) + self.cli_set(path + ['hash', auth_hash]) + self.cli_set(path + ['mode', 'server']) + self.cli_set(path + ['server', 'server-bridge', 'gateway', gw_subnet]) + self.cli_set(path + ['server', 'server-bridge', 'start', start_subnet]) + self.cli_set(path + ['server', 'server-bridge', 'stop', stop_subnet]) + self.cli_set(path + ['server', 'server-bridge', 'subnet-mask', mask_subnet]) + self.cli_set(path + ['keep-alive', 'failure-count', '5']) + self.cli_set(path + ['keep-alive', 'interval', '5']) + self.cli_set(path + ['tls', 'ca-certificate', 'ovpn_test']) + self.cli_set(path + ['tls', 'certificate', 'ovpn_test']) + self.cli_set(path + ['tls', 'dh-params', 'ovpn_test']) + + self.cli_commit() + + + + config_file = f'/run/openvpn/{vtun_if}.conf' + config = read_file(config_file) + self.assertIn(f'dev {vtun_if}', config) + self.assertIn(f'dev-type tap', config) + self.assertIn(f'proto udp', config) # default protocol + self.assertIn(f'auth {auth_hash}', config) + self.assertIn(f'data-ciphers AES-192-CBC', config) + self.assertIn(f'mode server', config) + self.assertIn(f'server-bridge {gw_subnet} {mask_subnet} {start_subnet} {stop_subnet}', config) + elf.assertIn(f'keepalive 5 25', config) + + + + # TLS options + self.assertIn(f'ca /run/openvpn/{vtun_if}_ca.pem', config) + self.assertIn(f'cert /run/openvpn/{vtun_if}_cert.pem', config) + self.assertIn(f'key /run/openvpn/{vtun_if}_cert.key', config) + self.assertIn(f'dh /run/openvpn/{vtun_if}_dh.pem', config) + + # check that no interface remained after deleting them + self.cli_delete((['interfaces', 'bridge', br_if, 'member', 'interface', vtun_if]) + self.cli_delete(base_path) + self.cli_commit() + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py index 320ab7b7b5..fcbba30ce4 100755 --- a/src/conf_mode/interfaces_openvpn.py +++ b/src/conf_mode/interfaces_openvpn.py @@ -378,6 +378,22 @@ def verify(openvpn): if (client_v.get('ip') and len(client_v['ip']) > 1) or (client_v.get('ipv6_ip') and len(client_v['ipv6_ip']) > 1): raise ConfigError(f'Server client "{client_k}": cannot specify more than 1 IPv4 and 1 IPv6 IP') + if dict_search('server.server_bridge', openvpn): + # check if server-bridge is a tap interfaces + if not openvpn['device_type'] == 'tap' and dict_search('server.server_bridge', openvpn): + raise ConfigError('Must specify "device-type tap" with server-bridge mode') + elif not (dict_search('server.server_bridge.start', openvpn) and dict_search('server.server_bridge.stop', openvpn)): + raise ConfigError('Server server-bridge requires both start and stop addresses') + else: + v4PoolStart = IPv4Address(dict_search('server.server_bridge.start', openvpn)) + v4PoolStop = IPv4Address(dict_search('server.server_bridge.stop', openvpn)) + if v4PoolStart > v4PoolStop: + raise ConfigError(f'Server server-bridge start address {v4PoolStart} is larger than stop address {v4PoolStop}') + + v4PoolSize = int(v4PoolStop) - int(v4PoolStart) + if v4PoolSize >= 65536: + raise ConfigError(f'Server server_bridge is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.') + if dict_search('server.client_ip_pool', openvpn): if not (dict_search('server.client_ip_pool.start', openvpn) and dict_search('server.client_ip_pool.stop', openvpn)): raise ConfigError('Server client-ip-pool requires both start and stop addresses') From f94d7f081074f4debef2eb25855412c26a061ddf Mon Sep 17 00:00:00 2001 From: fett0 Date: Fri, 2 Aug 2024 14:10:51 +0000 Subject: [PATCH 2/5] OPENVPN: T6555: fix name to bridge (cherry picked from commit d5ae708581d453e2205ad4cf8576503f42e262b6) --- data/templates/openvpn/server.conf.j2 | 4 ++-- .../interfaces_openvpn.xml.in | 2 +- .../scripts/cli/test_interfaces_openvpn.py | 10 +++++----- src/conf_mode/interfaces_openvpn.py | 18 +++++++++--------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2 index b6e90ed180..bc3a6127f7 100644 --- a/data/templates/openvpn/server.conf.j2 +++ b/data/templates/openvpn/server.conf.j2 @@ -90,8 +90,8 @@ server-ipv6 {{ subnet }} {% endif %} {% endfor %} {% endif %} -{% if server.server_bridge is vyos_defined and server.server_bridge.disable is not vyos_defined %} -server-bridge {{ server.server_bridge.gateway }} {{ server.server_bridge.subnet_mask }} {{ server.server_bridge.start }} {{ server.server_bridge.stop if server.server_bridge.stop is vyos_defined }} +{% if server.bridge is vyos_defined and server.bridge.disable is not vyos_defined %} +server-bridge {{ server.bridge.gateway }} {{ server.bridge.subnet_mask }} {{ server.bridge.start }} {{ server.bridge.stop if server.bridge.stop is vyos_defined }} {% endif %} {% if server.client_ip_pool is vyos_defined and server.client_ip_pool.disable is not vyos_defined %} ifconfig-pool {{ server.client_ip_pool.start }} {{ server.client_ip_pool.stop }} {{ server.client_ip_pool.subnet_mask if server.client_ip_pool.subnet_mask is vyos_defined }} diff --git a/interface-definitions/interfaces_openvpn.xml.in b/interface-definitions/interfaces_openvpn.xml.in index 46c22ac447..2d880feefa 100644 --- a/interface-definitions/interfaces_openvpn.xml.in +++ b/interface-definitions/interfaces_openvpn.xml.in @@ -445,7 +445,7 @@ - + Used with TAP device (layer 2) diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index 53cfa6cf53..3f05dd81c7 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -628,7 +628,7 @@ def test_openvpn_site2site_interfaces_tun(self): def test_openvpn_server_server_bridge(self): - # Create OpenVPN server interface using server-bridge. + # Create OpenVPN server interface using bridge. # Validate configuration afterwards. br_if = 'br0' vtun_if = 'vtun5010' @@ -644,10 +644,10 @@ def test_openvpn_server_server_bridge(self): self.cli_set(path + ['encryption', 'data-ciphers', 'aes192']) self.cli_set(path + ['hash', auth_hash]) self.cli_set(path + ['mode', 'server']) - self.cli_set(path + ['server', 'server-bridge', 'gateway', gw_subnet]) - self.cli_set(path + ['server', 'server-bridge', 'start', start_subnet]) - self.cli_set(path + ['server', 'server-bridge', 'stop', stop_subnet]) - self.cli_set(path + ['server', 'server-bridge', 'subnet-mask', mask_subnet]) + self.cli_set(path + ['server', 'bridge', 'gateway', gw_subnet]) + self.cli_set(path + ['server', 'bridge', 'start', start_subnet]) + self.cli_set(path + ['server', 'bridge', 'stop', stop_subnet]) + self.cli_set(path + ['server', 'bridge', 'subnet-mask', mask_subnet]) self.cli_set(path + ['keep-alive', 'failure-count', '5']) self.cli_set(path + ['keep-alive', 'interval', '5']) self.cli_set(path + ['tls', 'ca-certificate', 'ovpn_test']) diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py index fcbba30ce4..fe6a8fb63e 100755 --- a/src/conf_mode/interfaces_openvpn.py +++ b/src/conf_mode/interfaces_openvpn.py @@ -378,21 +378,21 @@ def verify(openvpn): if (client_v.get('ip') and len(client_v['ip']) > 1) or (client_v.get('ipv6_ip') and len(client_v['ipv6_ip']) > 1): raise ConfigError(f'Server client "{client_k}": cannot specify more than 1 IPv4 and 1 IPv6 IP') - if dict_search('server.server_bridge', openvpn): + if dict_search('server.bridge', openvpn): # check if server-bridge is a tap interfaces - if not openvpn['device_type'] == 'tap' and dict_search('server.server_bridge', openvpn): - raise ConfigError('Must specify "device-type tap" with server-bridge mode') - elif not (dict_search('server.server_bridge.start', openvpn) and dict_search('server.server_bridge.stop', openvpn)): - raise ConfigError('Server server-bridge requires both start and stop addresses') + if not openvpn['device_type'] == 'tap' and dict_search('server.bridge', openvpn): + raise ConfigError('Must specify "device-type tap" with server bridge mode') + elif not (dict_search('server.bridge.start', openvpn) and dict_search('server.bridge.stop', openvpn)): + raise ConfigError('Server server bridge requires both start and stop addresses') else: - v4PoolStart = IPv4Address(dict_search('server.server_bridge.start', openvpn)) - v4PoolStop = IPv4Address(dict_search('server.server_bridge.stop', openvpn)) + v4PoolStart = IPv4Address(dict_search('server.bridge.start', openvpn)) + v4PoolStop = IPv4Address(dict_search('server.bridge.stop', openvpn)) if v4PoolStart > v4PoolStop: - raise ConfigError(f'Server server-bridge start address {v4PoolStart} is larger than stop address {v4PoolStop}') + raise ConfigError(f'Server server bridge start address {v4PoolStart} is larger than stop address {v4PoolStop}') v4PoolSize = int(v4PoolStop) - int(v4PoolStart) if v4PoolSize >= 65536: - raise ConfigError(f'Server server_bridge is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.') + raise ConfigError(f'Server bridge is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.') if dict_search('server.client_ip_pool', openvpn): if not (dict_search('server.client_ip_pool.start', openvpn) and dict_search('server.client_ip_pool.stop', openvpn)): From 48c4e500a1452a6df8c718209b6c93a83dbbd04e Mon Sep 17 00:00:00 2001 From: fett0 Date: Fri, 2 Aug 2024 14:27:56 +0000 Subject: [PATCH 3/5] OPENVPN: T6555: fix name to bridge (cherry picked from commit 0162a27952d2166583a9e6aee2cd77b9c693062b) --- src/conf_mode/interfaces_openvpn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py index fe6a8fb63e..d2665d9e5d 100755 --- a/src/conf_mode/interfaces_openvpn.py +++ b/src/conf_mode/interfaces_openvpn.py @@ -379,16 +379,16 @@ def verify(openvpn): raise ConfigError(f'Server client "{client_k}": cannot specify more than 1 IPv4 and 1 IPv6 IP') if dict_search('server.bridge', openvpn): - # check if server-bridge is a tap interfaces + # check if server bridge is a tap interfaces if not openvpn['device_type'] == 'tap' and dict_search('server.bridge', openvpn): raise ConfigError('Must specify "device-type tap" with server bridge mode') elif not (dict_search('server.bridge.start', openvpn) and dict_search('server.bridge.stop', openvpn)): - raise ConfigError('Server server bridge requires both start and stop addresses') + raise ConfigError('Server bridge requires both start and stop addresses') else: v4PoolStart = IPv4Address(dict_search('server.bridge.start', openvpn)) v4PoolStop = IPv4Address(dict_search('server.bridge.stop', openvpn)) if v4PoolStart > v4PoolStop: - raise ConfigError(f'Server server bridge start address {v4PoolStart} is larger than stop address {v4PoolStop}') + raise ConfigError(f'Server bridge start address {v4PoolStart} is larger than stop address {v4PoolStop}') v4PoolSize = int(v4PoolStop) - int(v4PoolStart) if v4PoolSize >= 65536: From a075d17b37161d02e7e168f3cc6bba94d96cc256 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 5 Aug 2024 05:45:00 +0000 Subject: [PATCH 4/5] smoketest: T6555: openvpn: SyntaxError: '(' was never closed (cherry picked from commit 2fd817e51532c6428c95704233e62585e76b2ad8) --- smoketest/scripts/cli/test_interfaces_openvpn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index 3f05dd81c7..48ea318ef8 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -678,7 +678,7 @@ def test_openvpn_server_server_bridge(self): self.assertIn(f'dh /run/openvpn/{vtun_if}_dh.pem', config) # check that no interface remained after deleting them - self.cli_delete((['interfaces', 'bridge', br_if, 'member', 'interface', vtun_if]) + self.cli_delete(['interfaces', 'bridge', br_if, 'member', 'interface', vtun_if]) self.cli_delete(base_path) self.cli_commit() From 01da8d9cd91b9c35dcbbc9160f6377655d5c8983 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 5 Aug 2024 20:22:25 +0200 Subject: [PATCH 5/5] smoketest: T6555: openvpn: NameError: name 'elf' is not defined (cherry picked from commit 9bd2c196fe238a38f4fd0977efd1727333e7770e) --- smoketest/scripts/cli/test_interfaces_openvpn.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index 48ea318ef8..d24ce831ce 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -656,8 +656,6 @@ def test_openvpn_server_server_bridge(self): self.cli_commit() - - config_file = f'/run/openvpn/{vtun_if}.conf' config = read_file(config_file) self.assertIn(f'dev {vtun_if}', config) @@ -667,9 +665,7 @@ def test_openvpn_server_server_bridge(self): self.assertIn(f'data-ciphers AES-192-CBC', config) self.assertIn(f'mode server', config) self.assertIn(f'server-bridge {gw_subnet} {mask_subnet} {start_subnet} {stop_subnet}', config) - elf.assertIn(f'keepalive 5 25', config) - - + self.assertIn(f'keepalive 5 25', config) # TLS options self.assertIn(f'ca /run/openvpn/{vtun_if}_ca.pem', config)