OpenVPN: Instances: add Require Client Provisioning option (#7956)

This commit is contained in:
Stephan de Wit 2024-10-09 10:46:43 +02:00 committed by GitHub
parent a890e5f6e2
commit 04f9e5cfc2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 46 additions and 8 deletions

View File

@ -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.
</help>
</field>
<field>
<id>instance.provision_exclusive</id>
<label>Require Client Provisioning</label>
<type>checkbox</type>
<style>role role_server</style>
<advanced>true</advanced>
<help>
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.
</help>
</field>
<field>
<type>header</type>
<label>Routing</label>

View File

@ -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;
}

View File

@ -1,6 +1,6 @@
<model>
<mount>//OPNsense/OpenVPN</mount>
<version>1.0.0</version>
<version>1.0.1</version>
<description>OpenVPN</description>
<items>
<Overwrites>
@ -314,6 +314,10 @@
<auth-gen-token type="IntegerField">
<MinimumValue>0</MinimumValue>
</auth-gen-token>
<provision_exclusive type="BooleanField">
<Default>0</Default>
<Required>Y</Required>
</provision_exclusive>
<redirect_gateway type="OptionField">
<Multiple>Y</Multiple>
<OptionValues>

View File

@ -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);

View File

@ -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]";