Skip to content

Commit

Permalink
Merge pull request #3943 from vyos/mergify/bp/circinus/pr-3920
Browse files Browse the repository at this point in the history
OPENVPN: T6555: add server-bridge options in mode server (backport #3920)
  • Loading branch information
c-po authored Aug 6, 2024
2 parents f516575 + 01da8d9 commit 260f6d9
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
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
51 changes: 51 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,56 @@ 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)
self.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

0 comments on commit 260f6d9

Please sign in to comment.