Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OPENVPN: T6555: add server-bridge options in mode server #3920

Merged
merged 3 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion data/templates/openvpn/server.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Expand Down
56 changes: 56 additions & 0 deletions interface-definitions/interfaces_openvpn.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
55 changes: 55 additions & 0 deletions smoketest/scripts/cli/test_interfaces_openvpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 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)
16 changes: 16 additions & 0 deletions src/conf_mode/interfaces_openvpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Loading