summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfett0 <fernando.gmaidana@gmail.com>2024-07-31 18:21:25 +0000
committerfett0 <fernando.gmaidana@gmail.com>2024-07-31 18:21:25 +0000
commit4acad3eb8d9be173b76fecafc32b0c70eae9b192 (patch)
tree1bede28b5fde36fc26e717986a416d6d50e3be7a
parent7724a5f58b515fbc094b4f211d455c1bd5071a74 (diff)
downloadvyos-1x-4acad3eb8d9be173b76fecafc32b0c70eae9b192.tar.gz
vyos-1x-4acad3eb8d9be173b76fecafc32b0c70eae9b192.zip
OPENVPN: T6555: add server-bridge options in mode server
-rw-r--r--data/templates/openvpn/server.conf.j24
-rw-r--r--interface-definitions/interfaces_openvpn.xml.in56
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py55
-rwxr-xr-xsrc/conf_mode/interfaces_openvpn.py16
4 files changed, 130 insertions, 1 deletions
diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2
index f69519697..a8674ec35 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 13ef3ae5b..a988bf36c 100644
--- a/interface-definitions/interfaces_openvpn.xml.in
+++ b/interface-definitions/interfaces_openvpn.xml.in
@@ -445,6 +445,62 @@
</leafNode>
</children>
</tagNode>
+ <node name="server-bridge">
+ <properties>
+ <help>Used with TAP device (layer 2)</help>
+ </properties>
+ <children>
+ #include <include/generic-disable-node.xml.i>
+ <leafNode name="start">
+ <properties>
+ <help>First IP address in the pool</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="stop">
+ <properties>
+ <help>Last IP address in the pool</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="subnet-mask">
+ <properties>
+ <help>Subnet mask pushed to dynamic clients.</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 subnet mask</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="gateway">
+ <properties>
+ <help>Gateway IP address</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<node name="client-ip-pool">
<properties>
<help>Pool of client IPv4 addresses</help>
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index ca47c3218..fe04f7a20 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -627,5 +627,60 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
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 a03bd5959..46bd6acf7 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')