diff options
-rw-r--r-- | data/templates/openvpn/server.conf.j2 | 4 | ||||
-rw-r--r-- | interface-definitions/interfaces_openvpn.xml.in | 56 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_interfaces_openvpn.py | 55 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_openvpn.py | 16 | ||||
-rwxr-xr-x | src/conf_mode/protocols_static_multicast.py | 6 | ||||
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 1 |
6 files changed, 132 insertions, 6 deletions
diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2 index f69519697..408103558 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.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 }} {% endif %} diff --git a/interface-definitions/interfaces_openvpn.xml.in b/interface-definitions/interfaces_openvpn.xml.in index 13ef3ae5b..3563caef2 100644 --- a/interface-definitions/interfaces_openvpn.xml.in +++ b/interface-definitions/interfaces_openvpn.xml.in @@ -445,6 +445,62 @@ </leafNode> </children> </tagNode> + <node name="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..5584501c5 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 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', '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']) + 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..9105ce1f8 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.bridge', openvpn): + # 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 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 bridge start address {v4PoolStart} is larger than stop address {v4PoolStop}') + + v4PoolSize = int(v4PoolStop) - int(v4PoolStart) + if v4PoolSize >= 65536: + 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)): raise ConfigError('Server client-ip-pool requires both start and stop addresses') diff --git a/src/conf_mode/protocols_static_multicast.py b/src/conf_mode/protocols_static_multicast.py index 2bf794042..d323ceb4f 100755 --- a/src/conf_mode/protocols_static_multicast.py +++ b/src/conf_mode/protocols_static_multicast.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -14,7 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os from ipaddress import IPv4Address from sys import exit @@ -22,8 +21,7 @@ from sys import exit from vyos import ConfigError from vyos import frr from vyos.config import Config -from vyos.utils.process import call -from vyos.template import render, render_to_string +from vyos.template import render_to_string from vyos import airbag airbag.enable() diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index e8a0bc414..b3e05a814 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -25,7 +25,6 @@ from ipaddress import ip_address from netaddr import IPNetwork from netaddr import IPRange -from vyos.base import Warning from vyos.config import Config from vyos.config import config_dict_merge from vyos.configdep import set_dependents |