diff --git a/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml b/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml index 1d2603ee2..187421757 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml +++ b/src/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogInstance.xml @@ -311,6 +311,17 @@ Set to 0 to disable, remember to change your client as well. When set to 0, the token will never expire, any other value specifies the lifetime in seconds. + + instance.provision_exclusive + + checkbox + + true + + Require, as a condition for authentication, that a tunnel address will be provisioned either from a local defined client-specific override or offered by an authenticator (such as RADIUS) . + This is similar to OpenVPN's 'ccd-exclusive' option, but stricter as we expect the client to receive a tunnel address for the protocol used. + + header diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php index 0189ca669..729263391 100644 --- a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php +++ b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php @@ -170,9 +170,10 @@ class OpenVPN extends BaseModel * Retrieve overwrite content in legacy format * @param string $server_id vpnid * @param string $common_name certificate common name (or username when specified) - * @return array legacy overwrite data + * @param array $overlay overwrite CSO properties + * @return array legacy overwrite data, empty when failed */ - public function getOverwrite($server_id, $common_name) + public function getOverwrite($server_id, $common_name, $overlay = []) { $result = []; foreach ($this->Overwrites->Overwrite->iterateItems() as $cso) { @@ -234,6 +235,29 @@ class OpenVPN extends BaseModel } } + if (empty($result)) { + $result['common_name'] = $common_name; + } + + // overlay is fed by authentication backends and takes precedence + $result = array_merge($result, $overlay); + + // check if provisioning by authentication backend is mandatory + foreach ($this->Instances->Instance->iterateItems() as $node_uuid => $node) { + if ( + !empty((string)$node->enabled) && + $server_id == $node_uuid && + (string)$node->role == 'server' && + !empty((string)$node->provision_exclusive) + ) { + if (!empty((string)$node->server) && empty($result['tunnel_network'])) { + return []; + } elseif (!empty((string)$node->server_ipv6) && empty($result['tunnel_networkv6'])) { + return []; + } + } + } + return $result; } diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml index fee809efe..931cdfc23 100644 --- a/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml +++ b/src/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml @@ -1,6 +1,6 @@ //OPNsense/OpenVPN - 1.0.0 + 1.0.1 OpenVPN @@ -314,6 +314,10 @@ 0 + + 0 + Y + Y diff --git a/src/opnsense/scripts/openvpn/client_connect.php b/src/opnsense/scripts/openvpn/client_connect.php index f6dcd1bd6..2c806c7c0 100755 --- a/src/opnsense/scripts/openvpn/client_connect.php +++ b/src/opnsense/scripts/openvpn/client_connect.php @@ -40,7 +40,8 @@ $server = (new OPNsense\OpenVPN\OpenVPN())->getInstanceById($vpnid, 'server'); if ($server) { $cso = (new OPNsense\OpenVPN\OpenVPN())->getOverwrite($vpnid, $common_name); if (empty($cso)) { - $cso = array("common_name" => $common_name); + syslog(LOG_NOTICE, "authentication failed for user '{$common_name}'. No tunnel network provisioned, but required."); + exit(1); } if (!empty($config_file)) { $cso_filename = openvpn_csc_conf_write($cso, $server, $config_file); diff --git a/src/opnsense/scripts/openvpn/user_pass_verify.php b/src/opnsense/scripts/openvpn/user_pass_verify.php index 24d6e6c94..d3e4756d0 100755 --- a/src/opnsense/scripts/openvpn/user_pass_verify.php +++ b/src/opnsense/scripts/openvpn/user_pass_verify.php @@ -125,12 +125,10 @@ function do_auth($common_name, $serverid, $method, $auth_file) LOG_NOTICE, "Locate overwrite for '{$common_name}' using server '{$serverid}' (vpnid: {$a_server['vpnid']})" ); - $cso = (new OPNsense\OpenVPN\OpenVPN())->getOverwrite($serverid, $common_name); + $cso = (new OPNsense\OpenVPN\OpenVPN())->getOverwrite($serverid, $common_name, parse_auth_properties($authenticator->getLastAuthProperties())); if (empty($cso)) { - $cso = array("common_name" => $common_name); + return "authentication failed for user '{$username}'. No tunnel network provisioned, but required."; } - - $cso = array_merge($cso, parse_auth_properties($authenticator->getLastAuthProperties())); $cso_filename = openvpn_csc_conf_write($cso, $a_server); if (!empty($cso_filename)) { $tmp = empty($a_server['cso_login_matching']) ? "CSO [CN]" : "CSO [USER]";