VPN: OpenVPN: Client Specific Overrides: Migrate to MVC closes https://github.com/opnsense/core/issues/6381

o add new mvc module
o migrate existing data
o add getOverwrite() in OpenVPN model to retrieve data structured as legacy data to make this an easy drop-in
This commit is contained in:
Ad Schellevis 2023-03-22 18:10:03 +01:00
parent ffcd35ff3f
commit 011f1eebe4
15 changed files with 746 additions and 834 deletions

9
plist
View File

@ -383,10 +383,13 @@
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/ClientOverwritesController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/ExportController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/ServiceController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/ClientOverwritesController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/ExportController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/StatusController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/dialogCSO.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/forms/export_options.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Proxy/Api/ServiceController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Proxy/Api/SettingsController.php
@ -645,6 +648,10 @@
/usr/local/opnsense/mvc/app/models/OPNsense/Ntpd/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/Export.php
/usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/Export.xml
/usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/FieldTypes/OpenVPNServerField.php
/usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/Migrations/M1_0_0.php
/usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.php
/usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/OpenVPN.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Proxy/ACL/ACL.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Proxy/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Proxy/Migrations/M1_0_0.php
@ -721,6 +728,7 @@
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vxlan.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/index.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/status.volt
/usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/cso.volt
/usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/export.volt
/usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/status.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Proxy/index.volt
@ -1999,7 +2007,6 @@
/usr/local/www/vpn_ipsec_phase2.php
/usr/local/www/vpn_ipsec_settings.php
/usr/local/www/vpn_openvpn_client.php
/usr/local/www/vpn_openvpn_csc.php
/usr/local/www/vpn_openvpn_server.php
/usr/local/www/widgets/api/get.php
/usr/local/www/widgets/api/plugins/interfaces.inc

View File

@ -1043,38 +1043,6 @@ function openvpn_csc_conf_write($settings, $server, $target_filename = null)
}
}
/**
* Construct a single array containing all client specific overrides per server and common name
* @return array [vpnid][common_name] = settings
*/
function openvpn_fetch_csc_list()
{
global $config;
$result = array();
if (!empty($config['openvpn']['openvpn-csc'])) {
$all_servers = array_keys(openvpn_get_remote_access_servers());
foreach ($config['openvpn']['openvpn-csc'] as $settings) {
if (!isset($settings['disable'])) {
if (!empty($settings['ovpn_servers'])) {
$ovpn_servers = explode(',', $settings['ovpn_servers']);
} else {
$ovpn_servers = $all_servers;
}
foreach ($ovpn_servers as $vpnid) {
if (in_array($vpnid, $all_servers)) {
$vpnid = filter_var($vpnid, FILTER_SANITIZE_NUMBER_INT);
if (!isset($result[$vpnid])) {
$result[$vpnid] = array();
}
$result[$vpnid][$settings['common_name']] = $settings;
}
}
}
}
}
return $result;
}
function openvpn_prepare($device)
{
global $config;

View File

@ -0,0 +1,70 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\OpenVPN\Api;
use OPNsense\Base\ApiMutableModelControllerBase;
/**
* Class ClientOverwritesController
* @package OPNsense\OpenVPN\Api
*/
class ClientOverwritesController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'cso';
protected static $internalModelClass = 'OPNsense\OpenVPN\OpenVPN';
public function searchAction()
{
return $this->searchBase(
'Overwrites.Overwrite',
['description', 'common_name', 'enabled', 'tunnel_network', 'tunnel_networkv6']
);
}
public function getAction($uuid = null)
{
return $this->getBase('cso', 'Overwrites.Overwrite', $uuid);
}
public function addAction()
{
return $this->addBase('cso', 'Overwrites.Overwrite');
}
public function setAction($uuid = null)
{
return $this->setBase('cso', 'Overwrites.Overwrite', $uuid);
}
public function delAction($uuid)
{
return $this->delBase('Overwrites.Overwrite', $uuid);
}
public function toggleAction($uuid, $enabled = null)
{
return $this->toggleBase('Overwrites.Overwrite', $uuid, $enabled);
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\OpenVPN;
use OPNsense\Base\IndexController as BaseIndexController;
/**
* Class ClientOverwritesController
* @package OPNsense\OpenVPN
*/
class ClientOverwritesController extends BaseIndexController
{
/**
* default index page
* @throws \Exception
*/
public function indexAction()
{
$this->view->pick('OPNsense/OpenVPN/cso');
$this->view->formDialogCSO = $this->getForm('dialogCSO');
}
}

View File

@ -0,0 +1,148 @@
<fields>
<field>
<type>header</type>
<label>General Settings</label>
</field>
<field>
<id>cso.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
</field>
<field>
<id>cso.servers</id>
<label>Servers</label>
<type>select_multiple</type>
<help>Select the OpenVPN servers where this override applies to, leave empty for all</help>
</field>
<field>
<id>cso.description</id>
<label>Description</label>
<type>text</type>
<help>You may enter a description here for your reference (not parsed).</help>
</field>
<field>
<id>cso.common_name</id>
<label>Common name</label>
<type>text</type>
<help>Enter the client's X.509 common name here.</help>
</field>
<field>
<id>cso.block</id>
<label>Connection blocking</label>
<type>checkbox</type>
<help>
Block this client connection based on its common name.
Don't use this option to permanently disable a client due to a compromised key or password. Use a CRL (certificate revocation list) instead.
</help>
</field>
<field>
<id>cso.push_reset</id>
<label>Push reset</label>
<type>checkbox</type>
<help>
Don't inherit the global push list for a specific client instance.
NOTE: --push-reset is very thorough: it will remove almost all options from the list of to-be-pushed options.
In many cases, some of these options will need to be re-configured afterwards - specifically, --topology subnet
and --route-gateway will get lost and this will break client configs in many cases.
</help>
<advanced>true</advanced>
</field>
<field>
<type>header</type>
<label>Tunnel Settings</label>
</field>
<field>
<id>cso.tunnel_network</id>
<label>IPv4 Tunnel Network</label>
<type>text</type>
<help>Push virtual IP endpoints for client tunnel, overriding dynamic allocation.</help>
</field>
<field>
<id>cso.tunnel_networkv6</id>
<label>IPv6 Tunnel Network</label>
<type>text</type>
<help>Push virtual IP endpoints for client tunnel, overriding dynamic allocation.</help>
</field>
<field>
<id>cso.local_networks</id>
<label>Local Network</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>These are the networks accessible by the client, these are pushed via route{-ipv6} clauses in OpenVPN to the client.</help>
</field>
<field>
<id>cso.remote_networks</id>
<label>Remote Network</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>
Remote networks for the server, these are configured via iroute{-ipv6} clauses in OpenVPN and inform the server to send these networks to this specific client.
</help>
</field>
<field>
<id>cso.redirect_gateway</id>
<label>Redirect gateway</label>
<type>select_multiple</type>
<help>Automatically execute routing commands to cause all outgoing IP traffic to be redirected over the VPN.</help>
</field>
<field>
<type>header</type>
<label>Client Settings</label>
<collapse>true</collapse>
</field>
<field>
<id>cso.register_dns</id>
<label>Register DNS</label>
<type>checkbox</type>
<help>Run ipconfig /flushdns and ipconfig /registerdns on connection initiation. This is known to kick Windows into recognizing pushed DNS servers.</help>
</field>
<field>
<id>cso.dns_domain</id>
<label>DNS Default Domain</label>
<type>text</type>
<help>Set Connection-specific DNS Suffix.</help>
</field>
<field>
<id>cso.dns_domain_search</id>
<label>DNS Domain search list</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>
Add name to the domain search list. Repeat this option to add more entries. Up to 10 domains are supported
</help>
</field>
<field>
<id>cso.dns_servers</id>
<label>DNS Servers</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>
Set primary domain name server IPv4 or IPv6 address. Repeat this option to set secondary DNS server addresses.
</help>
</field>
<field>
<id>cso.ntp_servers</id>
<label>NTP Servers</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>
Set primary NTP server address (Network Time Protocol). Repeat this option to set secondary NTP server addresses.
</help>
</field>
<field>
<id>cso.wins_servers</id>
<label>WINS Servers</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<advanced>true</advanced>
<help>
Set primary WINS server address (NetBIOS over TCP/IP Name Server). Repeat this option to set secondary WINS server addresses.
</help>
</field>
</fields>

View File

@ -741,7 +741,8 @@
<page-openvpn-csc>
<name>VPN: OpenVPN: Client Specific Override</name>
<patterns>
<pattern>vpn_openvpn_csc.php*</pattern>
<pattern>ui/openvpn/client_overwrites</pattern>
<pattern>api/openvpn/client_overwrites/*</pattern>
</patterns>
</page-openvpn-csc>
<page-openvpn-server>

View File

@ -211,9 +211,7 @@
<Clients order="20" url="/vpn_openvpn_client.php">
<Edit url="/vpn_openvpn_client.php?*" visibility="hidden"/>
</Clients>
<CSC order="30" VisibleName="Client Specific Overrides" url="/vpn_openvpn_csc.php">
<Edit url="/vpn_openvpn_csc.php?*" visibility="hidden"/>
</CSC>
<CSC order="30" VisibleName="Client Specific Overrides" url="/ui/openvpn/client_overwrites"/>
<ClientExport order="40" VisibleName="Client Export" url="/ui/openvpn/export">
<Edit url="/ui/openvpn/export?*" visibility="hidden"/>
</ClientExport>

View File

@ -0,0 +1,60 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\OpenVPN\FieldTypes;
use OPNsense\Base\FieldTypes\BaseListField;
use OPNsense\Core\Config;
/**
* @package OPNsense\Base\FieldTypes
*/
class OpenVPNServerField extends BaseListField
{
private static $internalCacheOptionList = [];
protected function actionPostLoadingEvent()
{
if (empty(self::$internalCacheOptionList)) {
$ref = 'openvpn-server';
if (
isset(Config::getInstance()->object()->openvpn) &&
isset(Config::getInstance()->object()->openvpn->$ref)
) {
foreach (Config::getInstance()->object()->openvpn->$ref as $server) {
$label = (string)$server->description ?? '';
$label .= ' ( ' . (string)$server->local_port . ' / ' . (string)$server->protocol . ' )';
self::$internalCacheOptionList[(string)$server->vpnid] = $label;
}
}
natcasesort(self::$internalCacheOptionList);
}
$this->internalOptionList = self::$internalCacheOptionList;
}
}

View File

@ -0,0 +1,116 @@
<?php
/**
* Copyright (C) 2023 Deciso B.V.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OPNsense\OpenVPN\Migrations;
use OPNsense\Core\Config;
use OPNsense\Base\BaseModelMigration;
use OPNsense\OpenVPN\OpenVPN;
use OPNsense\Firewall\Util;
class M1_0_0 extends BaseModelMigration
{
/**
* is valid network or ip address
*/
private function valid_net($val)
{
return Util::isIpAddress($val) || Util::isSubnet($val);
}
/**
* Migrate legacy aliases
* @param $model
*/
public function run($model)
{
if ($model instanceof OpenVPN) {
$cfgObj = Config::getInstance()->object();
if (!empty($cfgObj->openvpn) && !empty($cfgObj->openvpn->{'openvpn-csc'})) {
foreach ($cfgObj->openvpn->{'openvpn-csc'} as $csc) {
$record = $model->Overwrites->Overwrite->Add();
$record->enabled = empty((string)$csc->disable) ? '1' : '0';
if (!empty((string)$csc->ovpn_servers)) {
$record->servers = (string)$csc->ovpn_servers;
}
$record->common_name = (string)$csc->common_name;
$record->description = (string)$csc->description;
if ($this->valid_net((string)$csc->tunnel_network)) {
$record->tunnel_network = (string)$csc->tunnel_network;
}
if ($this->valid_net((string)$csc->tunnel_networkv6)) {
$record->tunnel_networkv6 = (string)$csc->tunnel_networkv6;
}
foreach (['local', 'remote'] as $type) {
$nets = [];
$f1 = $type . '_network';
$f2 = $type . '_networkv6';
foreach (explode(',', (string)$csc->$f1 . ',' . (string)$csc->$f2) as $item) {
if (trim($item) != '' && $this->valid_net($item)) {
$nets[] = trim($item);
}
}
$record->{$type.'_networks'} = implode(',', $nets);
}
if (!empty((string)$csc->gwredir)) {
$record->redirect_gateway = 'def1';
}
$record->push_reset = !empty((string)$csc->push_reset) ? '1' : '0';
$record->block = !empty((string)$csc->block) ? '1' : '0';
$record->dns_domain = (string)$csc->dns_domain;
$record->dns_domain_search = (string)$csc->dns_domain_search;
foreach (['dns_server', 'ntp_server', 'wins_server'] as $fieldname) {
$items = [];
for ($i=1 ; $i <= 4; ++$i) {
$fname = $fieldname . $i;
if (!empty((string)$csc->$fname)){
$items[] = (string)$csc->$fname;
}
}
$record->{$fieldname . 's'} = implode(',', $items);
}
}
}
}
}
/**
* cleanup old config after config save
* @param $model
*/
public function post($model)
{
if ($model instanceof OpenVPN) {
$cfgObj = Config::getInstance()->object();
unset($cfgObj->openvpn->{'openvpn-csc'});
}
}
}

View File

@ -0,0 +1,94 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\OpenVPN;
use OPNsense\Base\BaseModel;
/**
* Class OpenVPN
* @package OPNsense\OpenVPN
*/
class OpenVPN extends BaseModel
{
public function getOverwrite($server_id, $common_name)
{
$result = [];
foreach ($this->Overwrites->Overwrite->iterateItems() as $cso) {
if (empty((string)$cso->enabled)) {
continue;
}
$servers = !empty((string)$cso->servers) ? explode(',', (string)$cso->servers) : [];
if (!empty($servers) && !in_array($server_id, $servers)) {
continue;
}
if ((string)$cso->common_name != $common_name) {
continue;
}
// translate content to legacy format so this may easily inject into the existing codebase
$result['ovpn_servers'] = (string)$cso->servers;
$result['common_name'] = (string)$cso->common_name;
$result['description'] = (string)$cso->description;
$result['tunnel_network'] = (string)$cso->tunnel_network;
$result['tunnel_networkv6'] = (string)$cso->tunnel_networkv6;
foreach (['local', 'remote'] as $type) {
$f1 = $type . '_network';
$f2 = $type . '_networkv6';
foreach (explode(',', (string)$cso->{$type.'_networks'}) as $item) {
if (strpos($item, ":") === false) {
$target_fieldname = $f1;
} else {
$target_fieldname = $f2;
}
if (!isset($result[$target_fieldname])) {
$result[$target_fieldname] = $item;
} else {
$result[$target_fieldname] .= "," . $item;
}
}
}
if (!empty((string)$cso->push_reset)){
$result['push_reset'] = '1';
}
if (!empty((string)$cso->block)){
$result['block'] = '1';
}
$result['dns_domain'] = (string)$cso->dns_domain;
$result['dns_domain_search'] = (string)$cso->dns_domain_search;
foreach (['dns_server', 'ntp_server', 'wins_server'] as $fieldname) {
if (!empty((string)$cso->$fieldname . 's')) {
foreach (explode(',', (string)$cso->{$fieldname . 's'}) as $idx => $item) {
$result[$fieldname. (string)($idx+1)] = $item;
}
}
}
}
return $result;
}
}

View File

@ -0,0 +1,97 @@
<model>
<mount>//OPNsense/OpenVPN</mount>
<version>1.0.0</version>
<description>OpenVPN</description>
<items>
<Overwrites>
<Overwrite type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<servers type=".\OpenVPNServerField">
<Required>N</Required>
<Multiple>Y</Multiple>
</servers>
<common_name type="TextField">
<Required>Y</Required>
</common_name>
<block type="BooleanField">
<default>0</default>
<Required>Y</Required>
</block>
<push_reset type="BooleanField">
<default>0</default>
<Required>Y</Required>
</push_reset>
<tunnel_network type="NetworkField">
<NetMaskRequired>Y</NetMaskRequired>
<Required>N</Required>
<AddressFamily>ipv4</AddressFamily>
</tunnel_network>
<tunnel_networkv6 type="NetworkField">
<NetMaskRequired>Y</NetMaskRequired>
<Required>N</Required>
<AddressFamily>ipv6</AddressFamily>
</tunnel_networkv6>
<local_networks type="NetworkField">
<FieldSeparator>,</FieldSeparator>
<Required>N</Required>
<asList>Y</asList>
</local_networks>
<remote_networks type="NetworkField">
<FieldSeparator>,</FieldSeparator>
<Required>N</Required>
<asList>Y</asList>
</remote_networks>
<redirect_gateway type="OptionField">
<Required>N</Required>
<Multiple>Y</Multiple>
<OptionValues>
<local value="local">local</local>
<autolocal value="autolocal">autolocal</autolocal>
<def1>default</def1>
<bypass_dhcp value="bypass-dhcp">bypass dhcp</bypass_dhcp>
<bypass_dns value="bypass-dns">bypass dns</bypass_dns>
<block_local value="block-local">block local</block_local>
<ipv6 value="ipv6">ipv6 (default)</ipv6>
<notipv4 value="!ipv4">not ipv4 (default)</notipv4>
</OptionValues>
</redirect_gateway>
<register_dns type="BooleanField">
<default>0</default>
<Required>Y</Required>
</register_dns>
<dns_domain type="HostnameField">
<Required>N</Required>
</dns_domain>
<dns_domain_search type="HostnameField">
<Required>N</Required>
<FieldSeparator>,</FieldSeparator>
<AsList>Y</AsList>
</dns_domain_search>
<dns_servers type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<Required>N</Required>
<FieldSeparator>,</FieldSeparator>
<asList>Y</asList>
</dns_servers>
<ntp_servers type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<Required>N</Required>
<FieldSeparator>,</FieldSeparator>
<asList>Y</asList>
</ntp_servers>
<wins_servers type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<Required>N</Required>
<FieldSeparator>,</FieldSeparator>
<asList>Y</asList>
</wins_servers>
<description type="TextField">
<Required>N</Required>
</description>
</Overwrite>
</Overwrites>
</items>
</model>

View File

@ -0,0 +1,97 @@
{#
# Copyright (c) 2023 Deciso B.V.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#}
<script>
'use strict';
$( document ).ready(function () {
let grid_cso = $("#grid-cso").UIBootgrid({
search:'/api/openvpn/client_overwrites/search/',
get:'/api/openvpn/client_overwrites/get/',
add:'/api/openvpn/client_overwrites/add/',
set:'/api/openvpn/client_overwrites/set/',
del:'/api/openvpn/client_overwrites/del/',
toggle:'/api/openvpn/client_overwrites/toggle/',
options:{
selection: false,
formatters:{
tunnel: function (column, row) {
let items = [];
if (row.tunnel_network) {
items.push(row.tunnel_network);
}
if (row.tunnel_networkv6) {
items.push(row.tunnel_networkv6);
}
return items.join('<br/>');
}
}
}
});
});
</script>
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#cso">{{ lang._('Overwrites') }}</a></li>
</ul>
<div class="tab-content content-box">
<div id="cso" class="tab-pane fade in active">
<table id="grid-cso" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogCSO" data-editAlert="CSOChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="common_name" data-type="string">{{ lang._('Common name') }}</th>
<th data-column-id="tunnel_network" data-type="string" data-formatter="tunnel">{{ lang._('Tunnel Network') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-fw fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-fw fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div class="col-md-12">
<div id="CSOChangeMessage" class="alert alert-info" style="display: none" role="alert">
{{ lang._('After changing settings, please remember to apply them') }}
</div>
<hr/>
</div>
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogCSO,'id':'DialogCSO','label':lang._('Edit CSO')])}}

View File

@ -41,11 +41,9 @@ if (isset($config['openvpn']['openvpn-server'])) {
if ($server['vpnid'] == $vpnid) {
// XXX: Eventually we should move the responsibility to determine if we do want to write a file
// to here instead of the configuration file (always call event, filter relevant).
$all_cso = openvpn_fetch_csc_list();
if (!empty($all_cso[$vpnid][$common_name])) {
$cso = $all_cso[$vpnid][$common_name];
} else {
$cso = ["common_name" => $common_name];
$cso = (new OPNsense\OpenVPN\OpenVPN())->getOverwrite($vpnid, $common_name);
if (empty($cso)) {
$cso = array("common_name" => $common_name);
}
if (!empty($config_file)) {
$cso_filename = openvpn_csc_conf_write($cso, $server, $config_file);

View File

@ -137,12 +137,9 @@ function do_auth($common_name, $serverid, $method, $auth_file)
if ($authenticator->authenticate($username, $password)) {
$vpnid = filter_var($a_server['vpnid'], FILTER_SANITIZE_NUMBER_INT);
// fetch or create client specif override
$all_cso = openvpn_fetch_csc_list();
$common_name = empty($a_server['cso_login_matching']) ? $common_name : $username;
$login_type = empty($a_server['cso_login_matching']) ? "CN" : "USER";
if (!empty($all_cso[$vpnid][$common_name])) {
$cso = $all_cso[$vpnid][$common_name];
} else {
$cso = (new OPNsense\OpenVPN\OpenVPN())->getOverwrite($vpnid, $common_name);
if (empty($cso)) {
$cso = array("common_name" => $common_name);
}

View File

@ -1,787 +0,0 @@
<?php
/*
* Copyright (C) 2014-2022 Deciso B.V.
* Copyright (C) 2008 Shrew Soft Inc. <mgrooms@shrew.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once("guiconfig.inc");
require_once("interfaces.inc");
require_once("plugins.inc.d/openvpn.inc");
$all_form_fields = "custom_options,disable,common_name,block,description
,tunnel_network,tunnel_networkv6,local_network,local_networkv6,remote_network
,remote_networkv6,gwredir,push_reset,dns_domain,dns_domain_search,dns_server1
,dns_server2,dns_server3,dns_server4,ntp_server1,ntp_server2
,netbios_enable,netbios_ntype,netbios_scope,wins_server1
,wins_server2,ovpn_servers";
$a_csc = &config_read_array('openvpn', 'openvpn-csc');
$vpnid = 0;
$act = null;
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$pconfig = array();
if (isset($_GET['dup']) && isset($a_csc[$_GET['dup']])) {
$configId = $_GET['dup'];
} elseif (isset($_GET['id']) && isset($a_csc[$_GET['id']])) {
$id = $_GET['id'];
$configId = $id;
}
if (isset($_GET['act'])) {
$act = $_GET['act'];
}
// 1 on 1 copy of config attributes
foreach (explode(",", $all_form_fields) as $fieldname) {
$fieldname = trim($fieldname);
if (isset($a_csc[$configId][$fieldname])) {
$pconfig[$fieldname] = $a_csc[$configId][$fieldname];
} elseif (!isset($pconfig[$fieldname])) {
// initialize element
$pconfig[$fieldname] = null;
}
}
// servers => array
$pconfig['ovpn_servers'] = empty($pconfig['ovpn_servers']) ? array() : explode(',', $pconfig['ovpn_servers']);
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input_errors = array();
$pconfig = $_POST;
if (isset($_POST['act'])) {
$act = $_POST['act'];
}
if (isset($_POST['id']) && isset($a_csc[$_POST['id']])) {
$id = $_POST['id'];
}
if ($act == "del") {
if (isset($id)) {
unset($a_csc[$id]);
write_config();
}
header(url_safe('Location: /vpn_openvpn_csc.php'));
exit;
} elseif ($act == "del_x") {
if (!empty($pconfig['rule']) && is_array($pconfig['rule'])) {
foreach ($pconfig['rule'] as $rulei) {
if (isset($a_csc[$rulei])) {
unset($a_csc[$rulei]);
}
}
write_config();
}
header(url_safe('Location: /vpn_openvpn_csc.php'));
exit;
} elseif ($act == "move"){
// move selected items
if (!isset($id)) {
// if id not set/found, move to end
$id = count($a_csc);
}
$a_csc = legacy_move_config_list_items($a_csc, $id, $pconfig['rule']);
write_config();
header(url_safe('Location: /vpn_openvpn_csc.php'));
exit;
} elseif ($act == "toggle") {
if (isset($id)) {
if (isset($a_csc[$id]['disable'])) {
unset($a_csc[$id]['disable']);
} else {
$a_csc[$id]['disable'] = true;
}
write_config();
}
header(url_safe('Location: /vpn_openvpn_csc.php'));
exit;
} else {
if ($result = openvpn_validate_cidr($pconfig['tunnel_network'], gettext('IPv4 Tunnel Network'), false, 'ipv4')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['tunnel_networkv6'], gettext('IPv6 Tunnel Network'), false, 'ipv6')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['local_network'], gettext('IPv4 Local Network'), true, 'ipv4')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['local_networkv6'], gettext('IPv6 Local Network'), true, 'ipv6')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['remote_network'], gettext('IPv4 Remote Network'), true, 'ipv4')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['remote_networkv6'], gettext('IPv6 Remote Network'), true, 'ipv6')) {
$input_errors[] = $result;
}
if (!empty($pconfig['dns_server_enable'])) {
if (!empty($pconfig['dns_server1']) && !is_ipaddr(trim($pconfig['dns_server1']))) {
$input_errors[] = gettext("The field 'DNS Server #1' must contain a valid IP address");
}
if (!empty($pconfig['dns_server2']) && !is_ipaddr(trim($pconfig['dns_server2']))) {
$input_errors[] = gettext("The field 'DNS Server #2' must contain a valid IP address");
}
if (!empty($pconfig['dns_server3']) && !is_ipaddr(trim($pconfig['dns_server3']))) {
$input_errors[] = gettext("The field 'DNS Server #3' must contain a valid IP address");
}
if (!empty($pconfig['dns_server4']) && !is_ipaddr(trim($pconfig['dns_server4']))) {
$input_errors[] = gettext("The field 'DNS Server #4' must contain a valid IP address");
}
}
if (!empty($pconfig['dns_domain_search'])) {
$tmp_ok_domain = 0;
$tmp_nok_domain = 0;
foreach (explode(",", $pconfig['dns_domain_search'] ?? "") as $domain) {
if (is_domain($domain)) {
$tmp_ok_domain++;
} else {
$tmp_nok_domain++;
}
}
if ($tmp_nok_domain > 0) {
$input_errors[] = gettext("The field 'DNS Domain search list' must contain valid domain names");
} elseif ($tmp_ok_domain > 10) {
$input_errors[] = gettext("The field 'DNS Domain search list' may contain max 10 entries");
}
}
if (!empty($pconfig['ntp_server_enable'])) {
if (!empty($pconfig['ntp_server1']) && !is_ipaddr(trim($pconfig['ntp_server1']))) {
$input_errors[] = gettext("The field 'NTP Server #1' must contain a valid IP address");
}
if (!empty($pconfig['ntp_server2']) && !is_ipaddr(trim($pconfig['ntp_server2']))) {
$input_errors[] = gettext("The field 'NTP Server #2' must contain a valid IP address");
}
}
if (!empty($pconfig['netbios_enable'])) {
if ($pconfig['wins_server_enable']) {
if (!empty($pconfig['wins_server1']) && !is_ipaddr(trim($pconfig['wins_server1']))) {
$input_errors[] = gettext("The field 'WINS Server #1' must contain a valid IP address");
}
if (!empty($pconfig['wins_server2']) && !is_ipaddr(trim($pconfig['wins_server2']))) {
$input_errors[] = gettext("The field 'WINS Server #2' must contain a valid IP address");
}
}
}
$prev_opt = (isset($id) && !empty($a_csc[$id])) ? $a_csc[$id]['custom_options'] : "";
if ($prev_opt != str_replace("\r\n", "\n", $pconfig['custom_options']) && !userIsAdmin($_SESSION['Username'])) {
$input_errors[] = gettext('Advanced options may only be edited by system administrators due to the increased possibility of privilege escalation.');
}
$reqdfields[] = 'common_name';
$reqdfieldsn[] = 'Common name';
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
if (count($input_errors) == 0) {
$csc = array();
// 1 on 1 copy of config attributes
foreach (explode(",", $all_form_fields) as $fieldname) {
$fieldname = trim($fieldname);
if (!empty($pconfig[$fieldname])) {
if (is_array($pconfig[$fieldname])) {
$csc[$fieldname] = implode(',', $pconfig[$fieldname]);
} else {
$csc[$fieldname] = $pconfig[$fieldname];
}
}
}
// handle fields with some kind of logic
if (!empty($pconfig['disable']) && $pconfig['disable'] == "yes") {
$csc['disable'] = true;
}
if (isset($id)) {
$a_csc[$id] = $csc;
} else {
$a_csc[] = $csc;
}
write_config();
header(url_safe('Location: /vpn_openvpn_csc.php'));
exit;
}
}
}
// escape form output before processing
legacy_html_escape_form_data($pconfig);
include("head.inc");
?>
<body>
<script>
//<![CDATA[
$( document ).ready(function() {
// link delete buttons
$(".act_delete").click(function(){
var id = $(this).data("id");
if (id != 'x') {
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?= gettext("OpenVPN");?>",
message: "<?= gettext("Do you really want to delete this csc?"); ?>",
buttons: [{
label: "<?= gettext("No");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Yes");?>",
action: function(dialogRef) {
$.post(window.location, {act: 'del', id:id}, function(data) {
location.reload();
});
dialogRef.close();
}
}]
});
} else {
// delete selected
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?=gettext("OpenVPN");?>",
message: "<?=gettext("Do you really want to delete the selected csc's?");?>",
buttons: [{
label: "<?= gettext("No");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Yes");?>",
action: function(dialogRef) {
$("#id").val("");
$("#action").val("del_x");
$("#iform2").submit()
}
}]
});
}
});
// link toggle buttons
$(".act_toggle").click(function(event){
event.preventDefault();
$.post(window.location, {act: 'toggle', id:$(this).data("id")}, function(data) {
location.reload();
});
});
// link move buttons
$(".act_move").click(function(event){
event.preventDefault();
$("#id").val($(this).data("id"));
$("#action").val("move");
$("#iform2").submit();
});
// checkboxes
$("#dns_domain_enable").change(function(){
if ($("#dns_domain_enable").is(":checked")) {
$("#dns_domain_data").show();
} else {
$("#dns_domain_data").hide();
}
});
$("#dns_domain_search_enable").change(function(){
if ($("#dns_domain_search_enable").is(':checked')) {
$("#dns_domain_search_data").show();
} else {
$("#dns_domain_search_data").hide();
}
});
$("#dns_domain_search_enable").change();
$("#dns_server_enable").change(function(){
if ($("#dns_server_enable").is(":checked")) {
$("#dns_server_data").show();
} else {
$("#dns_server_data").hide();
}
});
$("#wins_server_enable").change(function(){
if ($("#wins_server_enable").is(":checked")) {
$("#wins_server_data").show();
} else {
$("#wins_server_data").hide();
}
});
$("#ntp_server_enable").change(function(){
if ($("#ntp_server_enable").is(":checked")) {
$("#ntp_server_data").show();
} else {
$("#ntp_server_data").hide();
}
});
$("#netbios_enable").change(function(){
if ($("#netbios_enable").is(":checked")) {
$("#netbios_data").show();
$("#wins_opts").show();
} else {
$("#netbios_data").hide();
$("#wins_opts").hide();
}
});
// init form (old stuff)
if (document.iform != undefined) {
$("#dns_domain_search_enable").change();
$("#dns_domain_enable").change();
$("#dns_server_enable").change();
$("#wins_server_enable").change();
$("#ntp_server_enable").change();
$("#netbios_enable").change();
}
// watch scroll position and set to last known on page load
watchScrollPosition();
});
//]]>
</script>
<?php
include("fbegin.inc");
?>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php
if (isset($input_errors) && count($input_errors) > 0) {
print_input_errors($input_errors);
}
if (isset($savemsg)) {
print_info_box($savemsg);
}?>
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<?php
if ($act=="new" || $act=="edit") :?>
<form method="post" name="iform" id="iform">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td><?=gettext("General information"); ?></td>
<td style="text-align:right">
<small><?=gettext("full help"); ?> </small>
<i class="fa fa-toggle-off text-danger" style="cursor: pointer;" id="show_all_help_page"></i>
</td>
</tr>
<tr>
<td style="width:22%"><a id="help_for_disable" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Disabled"); ?></td>
<td style="width:78%">
<input name="disable" type="checkbox" value="yes" <?= !empty($pconfig['disable']) ? "checked=\"checked\"" : "";?> />
<div class="hidden" data-for="help_for_disable">
<?=gettext("Set this option to disable this client-specific override without removing it from the list"); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_servers" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Servers"); ?></td>
<td>
<select name="ovpn_servers[]" class="selectpicker" multiple="multiple" data-size="5" data-live-search="true">
<?php
foreach (openvpn_get_remote_access_servers() as $ra_server_vpnid => $ra_server):?>
<option value="<?=$ra_server_vpnid;?>" <?= !empty($pconfig['ovpn_servers']) && in_array($ra_server_vpnid, $pconfig['ovpn_servers']) ? 'selected="selected"' : '' ?>>
<?=!empty($ra_server['description']) ? $ra_server['description'] : ""?> ( <?=$ra_server['local_port'];?> / <?=$ra_server['protocol'];?>)
</option>
<?php
endforeach;?>
</select>
<div class="hidden" data-for="help_for_servers">
<?=gettext("Select the OpenVPN servers where this override applies to, leave empty for all"); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_common_name" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Common name"); ?></td>
<td>
<input name="common_name" type="text" value="<?=$pconfig['common_name'];?>" />
<div class="hidden" data-for="help_for_common_name">
<?=gettext("Enter the client's X.509 common name here"); ?>.
</div>
</td>
</tr>
<tr>
<td><a id="help_for_description" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Description"); ?></td>
<td>
<input name="description" type="text" value="<?=$pconfig['description'];?>" />
<div class="hidden" data-for="help_for_description">
<?=gettext("You may enter a description here for your reference (not parsed)."); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_block" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Connection blocking"); ?></td>
<td>
<input name="block" type="checkbox" value="yes" <?= !empty($pconfig['block']) ? "checked=\"checked\"" : "";?> />
<div class="hidden" data-for="help_for_block">
<?=gettext("Block this client connection based on its common name"); ?>.<br/>
<?=gettext("Don't use this option to permanently disable a " .
"client due to a compromised key or password. " .
"Use a CRL (certificate revocation list) instead"); ?>.
</div>
</td>
</tr>
<tr>
<td colspan="2" height="12"></td>
</tr>
<tr>
<td colspan="2" ><?=gettext("Tunnel Settings"); ?></td>
</tr>
<tr>
<td><a id="help_for_tunnel_network" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv4 Tunnel Network"); ?></td>
<td>
<input name="tunnel_network" type="text" size="20" value="<?=$pconfig['tunnel_network'];?>" />
<div class="hidden" data-for="help_for_tunnel_network">
<?=sprintf(
gettext(
"This defines the %s network used for this client in CIDR format, the addressing depends on ".
"the device mode (tun, tap) and the choice for topology. <br/><br/>".
"When topology is unset for a server in tun mode, the first network address is ".
"assumed to be the server address and the second network address will be assigned to the client virtual ".
"interface. </br><br/>".
"Otherwise the configured address is used for this clients virtual interface using the configured netmask."
),
gettext("IPv4"));?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_tunnel_networkv6" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv6 Tunnel Network"); ?></td>
<td>
<input name="tunnel_networkv6" type="text" value="<?=$pconfig['tunnel_networkv6'];?>" />
<div class="hidden" data-for="help_for_tunnel_networkv6">
<?=sprintf(
gettext(
"This defines the %s network used for this client in CIDR format, the addressing depends on ".
"the device mode (tun, tap) and the choice for topology. <br/><br/>".
"When topology is unset for a server in tun mode, the first network address is ".
"assumed to be the server address and the second network address will be assigned to the client virtual ".
"interface. </br><br/>".
"Otherwise the configured address is used for this clients virtual interface using the configured netmask."
),
gettext("IPv6"));?>
</div>
</td>
</tr>
<tr id="local_optsv4">
<td><a id="help_for_local_network" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv4 Local Network"); ?></td>
<td>
<input name="local_network" type="text" size="40" value="<?=$pconfig['local_network'];?>" />
<div class="hidden" data-for="help_for_local_network">
<?=gettext("These are the IPv4 networks that will be accessible " .
"from this particular client. Expressed as a comma-separated list of one or more CIDR ranges."); ?>
<br /><?=gettext("NOTE: You do not need to specify networks here if they have " .
"already been defined on the main server configuration.");?>
</div>
</td>
</tr>
<tr id="local_optsv6">
<td><a id="help_for_local_networkv6" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv6 Local Network"); ?></td>
<td>
<input name="local_networkv6" type="text" size="40" value="<?=$pconfig['local_networkv6'];?>" />
<div class="hidden" data-for="help_for_local_networkv6">
<?=gettext("These are the IPv6 networks that will be accessible " .
"from this particular client. Expressed as a comma-separated list of one or more IP/PREFIX networks."); ?><br />
<?=gettext("NOTE: You do not need to specify networks here if they have " .
"already been defined on the main server configuration.");?>
</div>
</td>
</tr>
<tr id="remote_optsv4">
<td><a id="help_for_remote_network" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv4 Remote Network"); ?></td>
<td>
<input name="remote_network" type="text" size="40" value="<?=$pconfig['remote_network'];?>" />
<div class="hidden" data-for="help_for_remote_network">
<?=gettext("These are the IPv4 networks that will be routed " .
"to this client specifically using iroute, so that a site-to-site " .
"VPN can be established. " .
"Expressed as a comma-separated list of one or more CIDR ranges. " .
"You may leave this blank if there are no client-side networks to " .
"be routed"); ?>.<br />
<?=gettext("NOTE: Remember to add these subnets to the " .
"IPv4 Remote Networks list on the corresponding OpenVPN server settings.");?>
</div>
</td>
</tr>
<tr id="remote_optsv6">
<td><a id="help_for_remote_networkv6" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv6 Remote Network"); ?></td>
<td>
<input name="remote_networkv6" type="text" size="40" value="<?=$pconfig['remote_networkv6'];?>" />
<div class="hidden" data-for="help_for_remote_networkv6">
<?=gettext("These are the IPv6 networks that will be routed " .
"to this client specifically using iroute, so that a site-to-site " .
"VPN can be established. " .
"Expressed as a comma-separated list of one or more IP/PREFIX networks. " .
"You may leave this blank if there are no client-side networks to " .
"be routed."); ?><br />
<?=gettext("NOTE: Remember to add these subnets to the " .
"IPv6 Remote Networks list on the corresponding OpenVPN server settings.");?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_gwredir" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Redirect Gateway"); ?></td>
<td>
<input name="gwredir" type="checkbox" value="yes" <?= !empty($pconfig['gwredir']) ? "checked=\"checked\"" : "";?> />
<div class="hidden" data-for="help_for_gwredir">
<?= gettext('Force all client generated traffic through the tunnel.') ?>
</div>
</td>
</tr>
<tr>
<td colspan="2" height="12"></td>
</tr>
<tr>
<td colspan="2"><?=gettext("Client Settings"); ?></td>
</tr>
<tr>
<td><a id="help_for_push_reset" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Server Definitions"); ?></td>
<td>
<input name="push_reset" type="checkbox" value="yes" <?= !empty($pconfig['push_reset']) ? "checked=\"checked\"" : "";?> />
<div class="hidden" data-for="help_for_push_reset">
<?=gettext("Prevent this client from receiving any server-defined client settings."); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_dns_domain" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("DNS Default Domain"); ?></td>
<td>
<input name="dns_domain_enable" type="checkbox" id="dns_domain_enable" value="yes" <?= !empty($pconfig['dns_domain']) ? "checked=\"checked\"" : "";?> />
<div id="dns_domain_data" style="display:none">
<input name="dns_domain" type="text" id="dns_domain" value="<?=$pconfig['dns_domain'];?>" />
</div>
<div class="hidden" data-for="help_for_dns_domain">
<?=gettext("Provide a default domain name to clients"); ?><br />
</div>
</td>
</tr>
<tr>
<td style="width:22%"><a id="help_for_dns_domain_search" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("DNS Domain search list"); ?></td>
<td>
<input name="dns_domain_search_enable" type="checkbox" id="dns_domain_search_enable" value="yes" <?=!empty($pconfig['dns_domain_search']) ? "checked=\"checked\"" : "" ;?> />
<div id="dns_domain_search_data">
<input name="dns_domain_search" type="text" class="form-control unknown" value="<?=htmlspecialchars($pconfig['dns_domain_search']);?>" />
</div>
<div class="hidden" data-for="help_for_dns_domain_search">
<span>
<?=gettext("Add name to the domain search list. Repeat this option to add more entries. Expressed as a comma-separated list up to 10 domains are supported."); ?><br />
</span>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_dns_server" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("DNS Servers"); ?></td>
<td>
<input name="dns_server_enable" type="checkbox" id="dns_server_enable" value="yes" <?=!empty($pconfig['dns_server1']) || !empty($pconfig['dns_server2']) || !empty($pconfig['dns_server3']) || !empty($pconfig['dns_server4']) ? "checked=\"checked\"" : "" ;?> />
<div id="dns_server_data" style="display:none">
<?=gettext("Server #1:"); ?>&nbsp;
<input name="dns_server1" type="text" id="dns_server1" size="20" value="<?=htmlspecialchars($pconfig['dns_server1']);?>" />
<?=gettext("Server #2:"); ?>&nbsp;
<input name="dns_server2" type="text" id="dns_server2" size="20" value="<?=htmlspecialchars($pconfig['dns_server2']);?>" />
<?=gettext("Server #3:"); ?>&nbsp;
<input name="dns_server3" type="text" id="dns_server3" size="20" value="<?=htmlspecialchars($pconfig['dns_server3']);?>" />
<?=gettext("Server #4:"); ?>&nbsp;
<input name="dns_server4" type="text" id="dns_server4" size="20" value="<?=htmlspecialchars($pconfig['dns_server4']);?>" />
</div>
<div class="hidden" data-for="help_for_dns_server">
<?=gettext("Provide a DNS server list to clients"); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_ntp_server" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("NTP Servers"); ?></td>
<td>
<input name="ntp_server_enable" type="checkbox" id="ntp_server_enable" value="yes" <?=!empty($pconfig['ntp_server1']) || !empty($pconfig['ntp_server2']) ? "checked=\"checked\"" : "" ;?> />
<div id="ntp_server_data" style="display:none">
<?=gettext("Server #1:"); ?>&nbsp;
<input name="ntp_server1" type="text" id="ntp_server1" size="20" value="<?=$pconfig['ntp_server1'];?>" />
<?=gettext("Server #2:"); ?>&nbsp;
<input name="ntp_server2" type="text" id="ntp_server2" size="20" value="<?=$pconfig['ntp_server2'];?>" />
</div>
<div class="hidden" data-for="help_for_ntp_server">
<?=gettext("Provide a NTP server list to clients"); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_netbios_enable" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("NetBIOS Options"); ?></td>
<td>
<input name="netbios_enable" type="checkbox" id="netbios_enable" value="yes" <?=!empty($pconfig['netbios_enable']) ? "checked=\"checked\"" : "" ;?> />
<div class="hidden" data-for="help_for_netbios_enable">
<?=gettext("Enable NetBIOS over TCP/IP");?><br/>
<?=gettext("If this option is not set, all NetBIOS-over-TCP/IP options (including WINS) will be disabled"); ?>.
</div>
<div id="netbios_data">
<?=gettext("Node Type"); ?>:&nbsp;
<select name='netbios_ntype'>
<?php
foreach ($netbios_nodetypes as $type => $name) :
$selected = "";
if ($pconfig['netbios_ntype'] == $type) {
$selected = "selected=\"selected\"";
}?>
<option value="<?=$type;?>" <?=$selected;?>><?=$name;?></option>
<?php
endforeach; ?>
</select>
<div class="hidden" data-for="help_for_netbios_enable">
<?=gettext("Possible options: b-node (broadcasts), p-node " .
"(point-to-point name queries to a WINS server), " .
"m-node (broadcast then query name server), and " .
"h-node (query name server, then broadcast)."); ?>
</div>
Scope ID:&nbsp;
<input name="netbios_scope" type="text" id="netbios_scope" value="<?=$pconfig['netbios_scope'];?>" />
<div class="hidden" data-for="help_for_netbios_enable">
<?=gettext("A NetBIOS Scope ID provides an extended naming " .
"service for NetBIOS over TCP/IP. The NetBIOS " .
"Scope ID isolates NetBIOS traffic on a single " .
"network to only those nodes with the same " .
"NetBIOS Scope ID."); ?>
</div>
</div>
</td>
</tr>
<tr id="wins_opts">
<td><a id="help_for_wins_server" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("WINS Servers"); ?></td>
<td>
<input name="wins_server_enable" type="checkbox" id="wins_server_enable" value="yes" <?=!empty($pconfig['wins_server1']) || !empty($pconfig['wins_server2']) ? "checked=\"checked\"" : "" ;?> />
<div id="wins_server_data" style="display:none">
<?=gettext("Server #1:"); ?>
<input name="wins_server1" type="text" id="wins_server1" size="20" value="<?=$pconfig['wins_server1'];?>" />
<?=gettext("Server #2:"); ?>
<input name="wins_server2" type="text" id="wins_server2" size="20" value="<?=$pconfig['wins_server2'];?>" />
</div>
<div class="hidden" data-for="help_for_wins_server">
<?=gettext("Provide a WINS server list to clients"); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_custom_options" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Advanced"); ?></td>
<td>
<textarea rows="6" cols="70" name="custom_options" id="custom_options"><?=$pconfig['custom_options'];?></textarea>
<?=gettext("This option will be removed in the future due to being insecure by nature. In the mean time only full administrators are allowed to change this setting.");?>
<div class="hidden" data-for="help_for_custom_options">
<?=gettext("Enter any additional options you would like to add for this client specific override, separated by a semicolon"); ?><br />
<?=gettext("EXAMPLE: push \"route 10.0.0.0 255.255.255.0\""); ?>;
</div>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<input name="save" type="submit" class="btn btn-primary" value="<?=html_safe(gettext('Save')); ?>" />
<input name="act" type="hidden" value="<?=$act;?>" />
<?php
if (isset($id)) :?>
<input name="id" type="hidden" value="<?=$id;?>" />
<?php
endif; ?>
</td>
</tr>
</table>
</div>
</form>
<?php
else :?>
<form method="post" name="iform2" id="iform2">
<input type="hidden" id="id" name="id" value="" />
<input type="hidden" id="action" name="act" value="" />
<div class="table-responsive">
<table class="table table-striped">
<tr>
<td></td>
<td><?=gettext("Common Name"); ?></td>
<td><?=gettext("Tunnel Network");?></td>
<td><?=gettext("Description"); ?></td>
<td class="text-nowrap">
<a href="vpn_openvpn_csc.php?act=new" class="btn btn-primary btn-xs" data-toggle="tooltip" title="<?= html_safe(gettext('Add')) ?>">
<i class="fa fa-plus fa-fw"></i>
</a>
<a data-id="<?= count($a_csc) ?>" data-toggle="tooltip" title="<?=gettext("Move selected items to end");?>" class="act_move btn btn-default btn-xs">
<span class="fa fa-arrow-down fa-fw"></span>
</a>
<a data-id="x" title="<?=gettext("delete selected rules"); ?>" data-toggle="tooltip" class="act_delete btn btn-default btn-xs">
<span class="fa fa-trash fa-fw"></span>
</a>
</td>
</tr>
<?php
$i = 0;
foreach ($a_csc as $csc):?>
<tr>
<td>
<input type="checkbox" name="rule[]" value="<?=$i;?>"/>
<a href="#" class="act_toggle" data-id="<?=$i;?>" data-toggle="tooltip" title="<?=(empty($csc['disable'])) ? gettext("Disable") : gettext("Enable");?>">
<span class="fa fa-play fa-fw <?=(empty($csc['disable'])) ? "text-success" : "text-muted";?>"></span>
</a>
</td>
<td>
<?=htmlspecialchars($csc['common_name']);?>
</td>
<td>
<?=!empty($csc['tunnel_network']) ? htmlspecialchars($csc['tunnel_network']) : "";?>
</td>
<td>
<?=htmlspecialchars($csc['description']);?>
</td>
<td class="text-nowrap">
<a data-id="<?=$i;?>" data-toggle="tooltip" title="<?=gettext("Move selected before this item");?>" class="act_move btn btn-default btn-xs">
<span class="fa fa-arrow-left fa-fw"></span>
</a>
<a href="vpn_openvpn_csc.php?act=edit&amp;id=<?=$i;?>" class="btn btn-default btn-xs"><span class="fa fa-pencil fa-fw"></span></a>
<a data-id="<?=$i;?>" title="<?=gettext("delete csc"); ?>" class="act_delete btn btn-default btn-xs"><span class="fa fa-trash fa-fw"></span></a>
<a href="vpn_openvpn_csc.php?act=new&dup=<?=$i;?>" class="btn btn-default btn-xs" data-toggle="tooltip" title="<?=gettext("clone rule");?>">
<span class="fa fa-clone fa-fw"></span>
</a>
</td>
</tr>
<?php
$i++;
endforeach;?>
</table>
</div>
</form>
<?php
endif; ?>
</div>
</section>
</div>
</div>
</section>
<?php include("foot.inc"); ?>