VPN: OpenVPN: Instances - add "server-bridge" directive for tap instances. closes https://github.com/opnsense/core/issues/7302

fix some validation regressions introduced in 59ce2706a1 which lead to unexpected validation issues.
This commit is contained in:
Ad Schellevis 2024-03-10 14:26:57 +01:00
parent f037dc01e2
commit ac4bbb359f
3 changed files with 92 additions and 45 deletions

View File

@ -107,6 +107,22 @@
The server itself will take the next base address (+1) of the given network for use as the server-side endpoint of the local TUN/TAP interface
</help>
</field>
<field>
<id>instance.bridge_gateway</id>
<label>Bridge gateway</label>
<type>text</type>
<style>role role_server_tap</style>
<help>
This directive will set up an OpenVPN server which will allocate addresses to clients out of the given network pool.
</help>
</field>
<field>
<id>instance.bridge_pool</id>
<label>Bridge DHCP pool</label>
<type>text</type>
<style>role role_server_tap</style>
<help>Specify an ip range which should be used to offer IPv4 addresses to the client.</help>
</field>
<field>
<id>instance.topology</id>
<label>Topology</label>

View File

@ -72,36 +72,60 @@ class OpenVPN extends BaseModel
));
}
} elseif ($instance->role == 'server') {
if (
$instance->dev_type == 'tun' &&
empty((string)$instance->server) &&
empty((string)$instance->server_ipv6)
) {
$messages->appendMessage(
new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server')
);
$messages->appendMessage(
new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server_ipv6')
);
}
if (!empty((string)$instance->server) && strpos((string)$instance->server, '/') !== false) {
if (
explode('/', (string)$instance->server)[1] > 29 && !(
(string)$instance->dev_type == 'tun' && (string)$instance->topology == 'p2p'
)
) {
/* tun + p2p is the exceptions here */
$msg = gettext(
'Server directive must define a subnet of /29 or lower unless topology equals p2p.'
if ($instance->dev_type == 'tun') {
if (empty((string)$instance->server) && empty((string)$instance->server_ipv6)) {
$messages->appendMessage(
new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server')
);
$messages->appendMessage(new Message($msg, $key . '.server'));
$messages->appendMessage(
new Message(gettext('At least one IPv4 or IPv6 tunnel network is required.'), $key . '.server_ipv6')
);
}
if (!empty((string)$instance->server) && strpos((string)$instance->server, '/') !== false) {
if (
explode('/', (string)$instance->server)[1] > 29 && !(
(string)$instance->dev_type == 'tun' && (string)$instance->topology == 'p2p'
)
) {
/* tun + p2p is the exceptions here */
$msg = gettext(
'Server directive must define a subnet of /29 or lower unless topology equals p2p.'
);
$messages->appendMessage(new Message($msg, $key . '.server'));
}
}
} elseif ($instance->dev_type == 'tap') {
if (!(empty((string)$instance->bridge_gateway) xor ((string)$instance->bridge_pool))) {
$messages->appendMessage(new Message(
gettext('When specifying a bridge gateway, a pool should also be provided.'),
$key . ".bridge_gateway"
));
} elseif (!empty((string)$instance->bridge_pool)) {
$parts = array_map('trim', explode('-', (string)$instance->bridge_pool));
if (count($parts) != 2 || !Util::isIpv4Address($parts[0]) || !Util::isIpv4Address($parts[1])) {
$messages->appendMessage(new Message(
gettext('Invalid range provided.'),
$key . ".bridge_pool"
));
} else {
$ip = (string)$instance->bridge_gateway;
if (!Util::isIPInCIDR($parts[0], $ip) || !Util::isIPInCIDR($parts[1], $ip)) {
$messages->appendMessage(new Message(
gettext('Range does not match specified subnet.'),
$key . ".bridge_pool"
));
}
}
}
}
if ((string)$instance->verify_client_cert != 'none') {
$messages->appendMessage(new Message(
gettext('To validate a certificate one has to be provided.'),
$key . ".verify_client_cert"
));
if (empty((string)$instance->cert)) {
$messages->appendMessage(new Message(
gettext('To validate a certificate one has to be provided.'),
$key . ".verify_client_cert"
));
}
} elseif (empty((string)$instance->authmode)) {
$messages->appendMessage(new Message(
gettext(
@ -463,6 +487,7 @@ class OpenVPN extends BaseModel
//$options['user'] = 'openvpn';
//$options['group'] = 'openvpn';
} else {
// server only settings
$event_script = '/usr/local/opnsense/scripts/openvpn/ovpn_event.py';
$options['dev'] = "ovpns{$node->vpnid}";
$options['ping-timer-rem'] = null;
@ -494,8 +519,17 @@ class OpenVPN extends BaseModel
$options['server'] = $parts[0] . " " . $mask;
}
} elseif ((string)$node->dev_type == 'tap') {
$options['mode'] = 'server';
$options['tls-server'] = null;
if (!empty((string)$node->bridge_gateway)) {
$parts = explode('/', (string)$node->bridge_gateway);
$options['server-bridge'] = sprintf(
"%s %s %s",
$parts[0],
Util::CIDRToMask($parts[1]),
str_replace('-', ' ', (string)$node->bridge_pool)
);
} else {
$options['server-bridge'] = '';
}
}
if (!empty((string)$node->server_ipv6)) {
$options['server-ipv6'] = (string)$node->server_ipv6;
@ -503,24 +537,17 @@ class OpenVPN extends BaseModel
if (!empty((string)$node->username_as_common_name)) {
$options['username-as-common-name'] = null;
}
// server only settings
if (
!empty((string)$node->server) ||
!empty((string)$node->server_ipv6) ||
(string)$node->dev_type == 'tap'
){
$options['client-config-dir'] = "/var/etc/openvpn-csc/{$node->vpnid}";
// hook event handlers
if (!empty((string)$node->authmode)) {
$options['auth-user-pass-verify'] = "\"{$event_script} --defer '{$node_uuid}'\" via-env";
$options['learn-address'] = "\"{$event_script} '{$node->vpnid}'\"";
} else {
// client specific profiles are being deployed using the connect event when no auth is used
$options['client-connect'] = "\"{$event_script} '{$node_uuid}'\"";
}
$options['client-disconnect'] = "\"{$event_script} '{$node_uuid}'\"";
$options['tls-verify'] = "\"{$event_script} '{$node_uuid}'\"";
$options['client-config-dir'] = "/var/etc/openvpn-csc/{$node->vpnid}";
// hook event handlers
if (!empty((string)$node->authmode)) {
$options['auth-user-pass-verify'] = "\"{$event_script} --defer '{$node_uuid}'\" via-env";
$options['learn-address'] = "\"{$event_script} '{$node->vpnid}'\"";
} else {
// client specific profiles are being deployed using the connect event when no auth is used
$options['client-connect'] = "\"{$event_script} '{$node_uuid}'\"";
}
$options['client-disconnect'] = "\"{$event_script} '{$node_uuid}'\"";
$options['tls-verify'] = "\"{$event_script} '{$node_uuid}'\"";
if (!empty((string)$node->maxclients)) {
$options['max-clients'] = (string)$node->maxclients;

View File

@ -161,6 +161,10 @@
<server_ipv6 type="NetworkField">
<WildcardEnabled>N</WildcardEnabled>
</server_ipv6>
<bridge_gateway type="NetworkField">
<WildcardEnabled>N</WildcardEnabled>
</bridge_gateway>
<bridge_pool type="TextField"/>
<route type="NetworkField">
<FieldSeparator>,</FieldSeparator>
<asList>Y</asList>