Feature #12267 ยป 0db0820b4a8a54412d9389a236e67f08fdd8acc7.diff
| src/etc/inc/openvpn.inc | ||
|---|---|---|
|
$conf .= "{$directive} {$fpath} {$opt}\n";
|
||
|
}
|
||
|
function openvpn_delete_tmp($mode, $id) {
|
||
|
global $g;
|
||
|
/* delete temporary files created by connect script */
|
||
|
if (($mode == "server") && (isset($id))) {
|
||
|
unlink_if_exists("{$g['tmp_path']}/ovpn_ovpns{$id}_*.rules");
|
||
|
}
|
||
|
/* delete temporary files created by OpenVPN */
|
||
|
$tmpfiles = array_filter(glob("{$g['tmp_path']}/openvpn_cc*.tmp"),'is_file');
|
||
|
if (!empty($tmpfiles)) {
|
||
|
foreach ($tmpfiles as $tmpfile) {
|
||
|
if ((time() - filemtime($tmpfile)) > 60) {
|
||
|
@unlink_if_exists($tmpfile);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function openvpn_reconfigure($mode, $settings) {
|
||
|
global $g, $config, $openvpn_tls_server_modes, $openvpn_dh_lengths, $openvpn_default_keepalive_interval, $openvpn_default_keepalive_timeout;
|
||
| ... | ... | |
|
}
|
||
|
break;
|
||
|
}
|
||
|
$connlimit = "0";
|
||
|
if ($settings['mode'] != 'p2p_shared_key' &&
|
||
|
isset($settings['duplicate_cn'])) {
|
||
|
$conf .= "duplicate-cn\n";
|
||
|
if ($settings['connlimit']) {
|
||
|
$connlimit = "{$settings['connlimit']}";
|
||
|
}
|
||
|
}
|
||
|
if (($settings['mode'] != 'p2p_shared_key') &&
|
||
|
isset($settings['remote_cert_tls'])) {
|
||
| ... | ... | |
|
unset($conf);
|
||
|
$fpath = "{$g['openvpn_base']}/{$mode_id}/interface";
|
||
|
file_put_contents($fpath, $interface);
|
||
|
$fpath = "{$g['openvpn_base']}/{$mode_id}/connuserlimit";
|
||
|
file_put_contents($fpath, $connlimit);
|
||
|
//chown($fpath, 'nobody');
|
||
|
//chgrp($fpath, 'nobody');
|
||
|
@chmod("{$g['openvpn_base']}/{$mode_id}/config.ovpn", 0600);
|
||
| ... | ... | |
|
@chmod("{$g['openvpn_base']}/{$mode_id}/key", 0600);
|
||
|
@chmod("{$g['openvpn_base']}/{$mode_id}/tls-auth", 0600);
|
||
|
@chmod("{$g['openvpn_base']}/{$mode_id}/conf", 0600);
|
||
|
@chmod("{$g['openvpn_base']}/{$mode_id}/connuserlimit", 0600);
|
||
|
if ($wait_tentative) {
|
||
|
interface_wait_tentative($interface);
|
||
| ... | ... | |
|
$vpnid = $settings['vpnid'];
|
||
|
$mode_id = $mode.$vpnid;
|
||
|
$lockhandle = lock("openvpnservice{$mode_id}", LOCK_EX);
|
||
|
openvpn_clean_rules($mode, $vpnid);
|
||
|
openvpn_reconfigure($mode, $settings);
|
||
|
/* kill the process if running */
|
||
|
$pfile = $g['varrun_path']."/openvpn_{$mode_id}.pid";
|
||
| ... | ... | |
|
return;
|
||
|
}
|
||
|
openvpn_delete_tmp($mode, $vpnid);
|
||
|
/* Do not start an instance if we are not CARP master on this vip! */
|
||
|
if (strstr($settings['interface'], "_vip")) {
|
||
|
$carpstatus = get_carp_bind_status($settings['interface']);
|
||
| ... | ... | |
|
/* remove the configuration files */
|
||
|
unlink_if_exists("{$g['openvpn_base']}/{$mode_id}/*/*");
|
||
|
unlink_if_exists("{$g['openvpn_base']}/{$mode_id}/*");
|
||
|
openvpn_clean_rules($mode, $vpnid);
|
||
|
openvpn_delete_tmp($mode, $vpnid);
|
||
|
}
|
||
|
function openvpn_resync_csc(& $settings) {
|
||
| ... | ... | |
|
return false;
|
||
|
}
|
||
|
function openvpn_clean_rules($mode, $id) {
|
||
|
global $g;
|
||
|
if ($mode == "server") {
|
||
|
unlink_if_exists("{$g['tmp_path']}/ovpn_ovpns{$id}_*.rules");
|
||
|
}
|
||
|
}
|
||
|
?>
|
||
| src/etc/inc/service-utils.inc | ||
|---|---|---|
|
$id = htmlspecialchars($extras['id']);
|
||
|
$pidfile = "{$g['varrun_path']}/openvpn_{$vpnmode}{$id}.pid";
|
||
|
killbypid($pidfile);
|
||
|
openvpn_clean_rules($vpnmode, $id);
|
||
|
openvpn_delete_tmp($vpnmode, $id);
|
||
|
}
|
||
|
break;
|
||
|
case 'syslogd':
|
||
| src/usr/local/sbin/openvpn.attributes.sh | ||
|---|---|---|
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
if [ -z "${untrusted_ip6}" ]; then
|
||
|
ipaddress="${untrusted_ip}"
|
||
|
else
|
||
|
ipaddress="${untrusted_ip6}"
|
||
|
fi
|
||
|
# Remote Access (SSL/TLS) mode
|
||
|
if [ -z "${username}" ]; then
|
||
|
if [ "$script_type" = "client-connect" ]; then
|
||
|
/usr/bin/logger -t openvpn "openvpn server '${dev}' user cert CN '${X509_0_CN}' address '${ipaddress}' - connected"
|
||
|
elif [ "$script_type" = "client-disconnect" ]; then
|
||
|
/usr/bin/logger -t openvpn "openvpn server '${dev}' user cert CN '${X509_0_CN}' address '${ipaddress}' - disconnected"
|
||
|
/sbin/pfctl -k $ifconfig_pool_remote_ip
|
||
|
/sbin/pfctl -K $ifconfig_pool_remote_ip
|
||
|
/sbin/pfctl -k $ifconfig_pool_remote_ip6
|
||
|
/sbin/pfctl -K $ifconfig_pool_remote_ip6
|
||
|
# Signal deferred handler
|
||
|
if [ "${script_type}" = "client-connect" ]; then
|
||
|
/bin/echo 2 > "${client_connect_deferred_file}"
|
||
|
if [ -f /tmp/"${common_name}" ]; then
|
||
|
/bin/cat /tmp/"${common_name}" > "${client_connect_config_file}"
|
||
|
/bin/rm /tmp/"${common_name}"
|
||
|
fi
|
||
|
exit 0
|
||
|
fi
|
||
|
lockfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.lock"
|
||
|
rulesfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.rules"
|
||
|
anchorname="openvpn/${dev}_${username}_${trusted_port}"
|
||
|
if [ "$script_type" = "client-connect" ]; then
|
||
|
/usr/bin/logger -t openvpn "openvpn server '${dev}' user '${username}' address '${ipaddress}' - connected"
|
||
|
i=1
|
||
|
while [ -f "${lockfile}" ]; do
|
||
|
if [ $i -ge 30 ]; then
|
||
|
/bin/echo "Timeout while waiting for lockfile"
|
||
|
exit 1
|
||
|
fi
|
||
|
/bin/sleep 1
|
||
|
i=$(( i + 1 ))
|
||
|
done
|
||
|
/usr/bin/touch "${lockfile}"
|
||
|
/bin/cat "${rulesfile}" | /usr/bin/sed "s/{clientip}/${ifconfig_pool_remote_ip}/g" | /usr/bin/sed "s/{clientipv6}/${ifconfig_pool_remote_ip6}/g" > "${rulesfile}.tmp" && /bin/mv "${rulesfile}.tmp" "${rulesfile}"
|
||
|
/sbin/pfctl -a "openvpn/${dev}_${username}_${trusted_port}" -f "${rulesfile}"
|
||
|
if [ -f /tmp/$common_name ]; then
|
||
|
/bin/cat /tmp/$common_name > $1
|
||
|
/bin/rm /tmp/$common_name
|
||
|
fi
|
||
|
/bin/rm "${lockfile}"
|
||
|
elif [ "$script_type" = "client-disconnect" ]; then
|
||
|
/usr/bin/logger -t openvpn "openvpn server '${dev}' user '${username}' address '${ipaddress}' - disconnected"
|
||
|
i=1
|
||
|
while [ -f "${lockfile}" ]; do
|
||
|
if [ $i -ge 30 ]; then
|
||
|
/bin/echo "Timeout while waiting for lockfile"
|
||
|
exit 1
|
||
|
fi
|
||
|
/bin/sleep 1
|
||
|
i=$(( i + 1 ))
|
||
|
done
|
||
|
/usr/bin/touch "${lockfile}"
|
||
|
command="/sbin/pfctl -a '${anchorname}' -F rules"
|
||
|
eval $command
|
||
|
/sbin/pfctl -k $ifconfig_pool_remote_ip
|
||
|
/sbin/pfctl -K $ifconfig_pool_remote_ip
|
||
|
/sbin/pfctl -k $ifconfig_pool_remote_ip6
|
||
|
/sbin/pfctl -K $ifconfig_pool_remote_ip6
|
||
|
/bin/rm "${rulesfile}"
|
||
|
/bin/rm "${lockfile}"
|
||
|
fi
|
||
|
# Handle 'client-connect' and 'client-disconnect'
|
||
|
/usr/bin/nohup /usr/local/sbin/openvpn.connect_async.sh > /dev/null &
|
||
|
# Signal "deferred handler started OK" for client-connect
|
||
|
exit 0
|
||
| src/usr/local/sbin/openvpn.connect_async.sh | ||
|---|---|---|
|
#!/bin/sh
|
||
|
#
|
||
|
# openvpn.connect_async.sh
|
||
|
#
|
||
|
# part of pfSense (https://www.pfsense.org)
|
||
|
# Copyright (c) 2021-2022 Rubicon Communications, LLC (Netgate)
|
||
|
# All rights reserved.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
log_session() {
|
||
|
if [ -z "${1}" ]; then
|
||
|
logmsg=""
|
||
|
else
|
||
|
logmsg=" - ${1}"
|
||
|
fi
|
||
|
if [ -z "${untrusted_ip6}" ]; then
|
||
|
hostaddress="${untrusted_ip}:${untrusted_port}"
|
||
|
else
|
||
|
hostaddress="${untrusted_ip6}:${untrusted_port}"
|
||
|
fi
|
||
|
|
||
|
if [ -z "${username}" ]; then
|
||
|
hostuser="user cert CN '${X509_0_CN}'"
|
||
|
else
|
||
|
hostuser="user '${username}'"
|
||
|
fi
|
||
|
/usr/bin/logger -t openvpn "openvpn server '${dev}' ${hostuser} address '${hostaddress}'${logmsg}"
|
||
|
}
|
||
|
if [ -n "${username}" ]; then
|
||
|
lockfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.lock"
|
||
|
rulesfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.rules"
|
||
|
anchorname="openvpn/${dev}_${username}_${trusted_port}"
|
||
|
fi
|
||
|
if [ "${script_type}" = "client-disconnect" ]; then
|
||
|
log_session "disconnected"
|
||
|
if [ -n "${username}" ]; then
|
||
|
# Avoid race condition. See https://redmine.pfsense.org/issues/9206
|
||
|
i=1
|
||
|
while
|
||
|
if [ -f "${lockfile}" ]; then
|
||
|
/bin/sleep 1
|
||
|
i="$((i+1))"
|
||
|
else
|
||
|
break
|
||
|
fi
|
||
|
[ "${i}" -lt 30 ]
|
||
|
do :; done
|
||
|
if [ ${i} -ge 30 ]; then
|
||
|
log_session "Timeout while waiting for lockfile"
|
||
|
else
|
||
|
/usr/bin/touch "${lockfile}"
|
||
|
eval "/sbin/pfctl -a '${anchorname}' -F rules"
|
||
|
/bin/rm "${lockfile}"
|
||
|
/bin/rm "${rulesfile}"
|
||
|
fi
|
||
|
fi
|
||
|
/sbin/pfctl -k $ifconfig_pool_remote_ip
|
||
|
/sbin/pfctl -K $ifconfig_pool_remote_ip
|
||
|
/sbin/pfctl -k $ifconfig_pool_remote_ip6
|
||
|
/sbin/pfctl -K $ifconfig_pool_remote_ip6
|
||
|
elif [ "${script_type}" = "client-connect" ]; then
|
||
|
log_session "connecting"
|
||
|
# Verify defer status code before continuing
|
||
|
i=1
|
||
|
while
|
||
|
deferstatus=$(/usr/bin/head -1 "${client_connect_deferred_file}")
|
||
|
if [ "${deferstatus}" -ne 2 ]; then
|
||
|
/bin/sleep 1
|
||
|
i="$((i+1))"
|
||
|
else
|
||
|
break
|
||
|
fi
|
||
|
[ "${i}" -lt 3 ]
|
||
|
do :; done
|
||
|
if [ ${i} -ge 3 ]; then
|
||
|
log_session "server write to defer file failed"
|
||
|
/bin/echo 0 > ${client_connect_deferred_file}
|
||
|
exit 1
|
||
|
fi
|
||
|
# Get active sessions
|
||
|
# active_sessions :: ovpns1_'user_01'_30001|ovpns1_'user_01'_30002|ovpns1_'user_01'_30003|
|
||
|
# Use php-cgi - see https://redmine.pfsense.org/issues/12382
|
||
|
active_sessions=$("/usr/local/bin/php-cgi" -f "/usr/local/sbin/openvpn_connect_async.php")
|
||
|
# Process "Duplicate Connection Limit" setting
|
||
|
if [ -n "${active_sessions}" ]; then
|
||
|
vpnid=$(/bin/echo ${dev} | /usr/bin/sed -e 's/ovpns//g')
|
||
|
if [ -f "/var/etc/openvpn/server${vpnid}/connuserlimit" ]; then
|
||
|
sessionlimit=$(/usr/bin/head -1 "/var/etc/openvpn/server${vpnid}/connuserlimit" | /usr/bin/sed -e 's/[[:space:]]//g')
|
||
|
if [ "${sessionlimit}" -ge 1 ]; then
|
||
|
if [ -z "${username}" ]; then
|
||
|
usersession="${dev}_'${X509_0_CN}'"
|
||
|
else
|
||
|
usersession="${dev}_'${username}'"
|
||
|
fi
|
||
|
sessioncount=$(/bin/echo "${active_sessions}" | /usr/bin/grep -o "${usersession}" | /usr/bin/wc -l | /usr/bin/sed -e 's/[[:space:]]//g')
|
||
|
if [ ${sessioncount} -gt ${sessionlimit} ]; then
|
||
|
log_session "active connection limit of '${sessionlimit}' reached"
|
||
|
/bin/echo 0 > ${client_connect_deferred_file}
|
||
|
if [ -n "${username}" ]; then
|
||
|
/bin/rm "${rulesfile}"
|
||
|
fi
|
||
|
exit 1
|
||
|
fi
|
||
|
fi
|
||
|
fi
|
||
|
fi
|
||
|
if [ -n "${username}" ]; then
|
||
|
i=1
|
||
|
while
|
||
|
if [ -f "${lockfile}" ]; then
|
||
|
/bin/sleep 1
|
||
|
i="$((i+1))"
|
||
|
else
|
||
|
break
|
||
|
fi
|
||
|
[ "${i}" -lt 30 ]
|
||
|
do :; done
|
||
|
if [ ${i} -ge 30 ]; then
|
||
|
log_session "Timeout while waiting for lockfile"
|
||
|
/bin/echo 0 > ${client_connect_deferred_file}
|
||
|
exit 1
|
||
|
else
|
||
|
/usr/bin/touch "${lockfile}"
|
||
|
# for each of this user's anchors loaded in pf
|
||
|
# $session :: ovpns3_'user_01'_61468
|
||
|
# $anchor :: openvpn/ovpns3_user_01_61468
|
||
|
anchors=$(/sbin/pfctl -s Anchors)
|
||
|
for anchor in $(/bin/echo "${anchors}" | /usr/bin/grep "${dev}_${username}"); do
|
||
|
session=$(/bin/echo "${anchor}" | /usr/bin/sed -r -e 's/.+'"${dev}_${username}"'/'"${dev}_\'${username}\'"'/')
|
||
|
# if no active session exists for the anchor, remove it from pf
|
||
|
if ! (/bin/echo "${active_sessions}" | /usr/bin/grep -q "${session}"); then
|
||
|
eval "/sbin/pfctl -a '${anchor}' -F rules"
|
||
|
fi
|
||
|
done
|
||
|
/bin/echo "$(/usr/bin/sed -e "s/{clientip}/${ifconfig_pool_remote_ip}/g;s/{clientipv6}/${ifconfig_pool_remote_ip6}/g" "${rulesfile}")" > "${rulesfile}"
|
||
|
eval "/sbin/pfctl -a '${anchorname}' -f '${rulesfile}'"
|
||
|
/bin/rm "${lockfile}"
|
||
|
fi
|
||
|
fi
|
||
|
# success; allow client connection
|
||
|
/bin/echo 1 > ${client_connect_deferred_file}
|
||
|
log_session "connected"
|
||
|
fi
|
||
|
exit 0
|
||
| src/usr/local/sbin/openvpn_connect_async.php | ||
|---|---|---|
|
<?php
|
||
|
/*
|
||
|
# openvpn_connect_async.php
|
||
|
#
|
||
|
# part of pfSense (https://www.pfsense.org)
|
||
|
# Copyright (c) 2021-2022 Rubicon Communications, LLC (Netgate)
|
||
|
# All rights reserved.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
*/
|
||
|
// Returns a string with all active sessions delimited by '|'.
|
||
|
require_once("openvpn.inc");
|
||
|
$result = openvpn_get_active_servers();
|
||
|
$output = null;
|
||
|
foreach ($result as $server) {
|
||
|
$vpnid = $server["vpnid"];
|
||
|
foreach ($server["conns"] as $client) {
|
||
|
if ( !(empty($client["remote_host"])) ) {
|
||
|
$port = substr($client["remote_host"], strpos($client["remote_host"], ":") + 1);
|
||
|
/* Handle RADIUS backend */
|
||
|
if (escapeshellarg($client["common_name"]) != "'UNDEF'") {
|
||
|
$name = escapeshellarg($client["common_name"]);
|
||
|
} else {
|
||
|
$name = escapeshellarg($client["user_name"]);
|
||
|
}
|
||
|
$name = isset($name) ? trim($name) : NULL;
|
||
|
$port = isset($port) ? trim($port) : NULL;
|
||
|
if ( !empty($name) && !empty($port) ) {
|
||
|
$output .= "ovpns{$vpnid}_{$name}_{$port}|";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
echo (isset($output)) ? $output : '';
|
||
|
?>
|
||
| src/usr/local/www/vpn_openvpn_server.php | ||
|---|---|---|
|
$pconfig['local_network'] = $a_server[$id]['local_network'];
|
||
|
$pconfig['local_networkv6'] = $a_server[$id]['local_networkv6'];
|
||
|
$pconfig['maxclients'] = $a_server[$id]['maxclients'];
|
||
|
$pconfig['connlimit'] = $a_server[$id]['connlimit'];
|
||
|
$pconfig['allow_compression'] = $a_server[$id]['allow_compression'];
|
||
|
$pconfig['compression'] = $a_server[$id]['compression'];
|
||
|
$pconfig['compression_push'] = $a_server[$id]['compression_push'];
|
||
| ... | ... | |
|
$input_errors[] = gettext("The field 'Concurrent connections' must be numeric.");
|
||
|
}
|
||
|
if ($pconfig['connlimit'] && !is_numericint($pconfig['connlimit'])) {
|
||
|
$input_errors[] = gettext("The field 'Duplicate Connection Limit' must be numeric.");
|
||
|
}
|
||
|
if (!array_key_exists($pconfig['topology'], $openvpn_topologies)) {
|
||
|
$input_errors[] = gettext("The field 'Topology' contains an invalid selection");
|
||
|
}
|
||
| ... | ... | |
|
$server['local_network'] = $pconfig['local_network'];
|
||
|
$server['local_networkv6'] = $pconfig['local_networkv6'];
|
||
|
$server['maxclients'] = $pconfig['maxclients'];
|
||
|
$server['connlimit'] = $pconfig['connlimit'];
|
||
|
$server['allow_compression'] = $pconfig['allow_compression'];
|
||
|
$server['compression'] = $pconfig['compression'];
|
||
|
$server['compression_push'] = $pconfig['compression_push'];
|
||
| ... | ... | |
|
'When unset, a new connection from a user will disconnect the previous session. %1$s%1$s' .
|
||
|
'Users are identified by their username or certificate properties, depending on the VPN configuration. ' .
|
||
|
'This practice is discouraged security reasons, but may be necessary in some environments.', '<br />');
|
||
|
|
||
|
$section->addInput(new Form_Input(
|
||
|
'connlimit',
|
||
|
'Duplicate Connection Limit',
|
||
|
'number',
|
||
|
$pconfig['connlimit']
|
||
|
))->setHelp('Limit the number of concurrent connections from the same user.');
|
||
|
$form->add($section);
|
||
| src/usr/local/www/wizards/openvpn_wizard.inc | ||
|---|---|---|
|
if ($_POST['concurrentcon'] && !is_numeric($_POST['concurrentcon']))
|
||
|
$input_errors[] = "The field 'Concurrent connections' must be numeric.";
|
||
|
|
||
|
if ($_POST['connuserlimit'] && !is_numeric($_POST['connuserlimit']))
|
||
|
$input_errors[] = "The field 'Duplicate Connection Limit' must be numeric.";
|
||
|
if (empty($_POST['tunnelnet']))
|
||
|
$input_errors[] = "A 'Tunnel network' must be specified.";
|
||
| ... | ... | |
|
$server['local_network'] = $pconfig['step10']['localnet'];
|
||
|
if (isset($pconfig['step10']['concurrentcon']))
|
||
|
$server['maxclients'] = $pconfig['step10']['concurrentcon'];
|
||
|
if (isset($pconfig['step10']['connuserlimit']))
|
||
|
$server['connlimit'] = $pconfig['step10']['connuserlimit'];
|
||
|
if (isset($pconfig['step10']['allowcompression']))
|
||
|
$server['allow_compression'] = $pconfig['step10']['allowcompression'];
|
||
|
if (isset($pconfig['step10']['compression']))
|
||
| src/usr/local/www/wizards/openvpn_wizard.xml | ||
|---|---|---|
|
<description>Allow multiple concurrent connections from clients using the same Common Name.<br/>NOTE: This is not generally recommended, but may be needed for some scenarios.</description>
|
||
|
<bindstofield>ovpnserver->step10->duplicate_cn</bindstofield>
|
||
|
</field>
|
||
|
<field>
|
||
|
<displayname>Duplicate Connection Limit</displayname>
|
||
|
<name>connuserlimit</name>
|
||
|
<description>Limit the number of concurrent connections from the same user.</description>
|
||
|
<type>input</type>
|
||
|
<size>10</size>
|
||
|
<bindstofield>ovpnserver->step10->connuserlimit</bindstofield>
|
||
|
</field>
|
||
|
<field>
|
||
|
<type>listtopic</type>
|
||
|
<name>Client Settings</name>
|
||