Project

General

Profile

Feature #12267 ยป 0db0820b4a8a54412d9389a236e67f08fdd8acc7.diff

Marcos M, 01/16/2022 10:30 AM

View differences:

src/etc/inc/openvpn.inc
821 821
	$conf .= "{$directive} {$fpath} {$opt}\n";
822 822
}
823 823

  
824
function openvpn_delete_tmp($mode, $id) {
825
	global $g;
826

  
827
	/* delete temporary files created by connect script */
828
	if (($mode == "server") && (isset($id))) {
829
		unlink_if_exists("{$g['tmp_path']}/ovpn_ovpns{$id}_*.rules");
830
	}
831
	/* delete temporary files created by OpenVPN */
832
	$tmpfiles = array_filter(glob("{$g['tmp_path']}/openvpn_cc*.tmp"),'is_file');
833
	if (!empty($tmpfiles)) {
834
		foreach ($tmpfiles as $tmpfile) {
835
			if ((time() - filemtime($tmpfile)) > 60) {
836
				@unlink_if_exists($tmpfile);
837
			}
838
		}
839
	}
840
}
841

  
824 842
function openvpn_reconfigure($mode, $settings) {
825 843
	global $g, $config, $openvpn_tls_server_modes, $openvpn_dh_lengths, $openvpn_default_keepalive_interval, $openvpn_default_keepalive_timeout;
826 844

  
......
1175 1193
				}
1176 1194
				break;
1177 1195
		}
1196
		$connlimit = "0";
1178 1197
		if ($settings['mode'] != 'p2p_shared_key' &&
1179 1198
		    isset($settings['duplicate_cn'])) {
1180 1199
			$conf .= "duplicate-cn\n";
1200
			if ($settings['connlimit']) {
1201
				$connlimit = "{$settings['connlimit']}";
1202
			}
1181 1203
		}
1182 1204
		if (($settings['mode'] != 'p2p_shared_key') &&
1183 1205
		    isset($settings['remote_cert_tls'])) {
......
1518 1540
	unset($conf);
1519 1541
	$fpath = "{$g['openvpn_base']}/{$mode_id}/interface";
1520 1542
	file_put_contents($fpath, $interface);
1543
	$fpath = "{$g['openvpn_base']}/{$mode_id}/connuserlimit";
1544
	file_put_contents($fpath, $connlimit);
1521 1545
	//chown($fpath, 'nobody');
1522 1546
	//chgrp($fpath, 'nobody');
1523 1547
	@chmod("{$g['openvpn_base']}/{$mode_id}/config.ovpn", 0600);
......
1525 1549
	@chmod("{$g['openvpn_base']}/{$mode_id}/key", 0600);
1526 1550
	@chmod("{$g['openvpn_base']}/{$mode_id}/tls-auth", 0600);
1527 1551
	@chmod("{$g['openvpn_base']}/{$mode_id}/conf", 0600);
1552
	@chmod("{$g['openvpn_base']}/{$mode_id}/connuserlimit", 0600);
1528 1553

  
1529 1554
	if ($wait_tentative) {
1530 1555
		interface_wait_tentative($interface);
......
1537 1562
	$vpnid = $settings['vpnid'];
1538 1563
	$mode_id = $mode.$vpnid;
1539 1564
	$lockhandle = lock("openvpnservice{$mode_id}", LOCK_EX);
1540
	openvpn_clean_rules($mode, $vpnid);
1541 1565
	openvpn_reconfigure($mode, $settings);
1542 1566
	/* kill the process if running */
1543 1567
	$pfile = $g['varrun_path']."/openvpn_{$mode_id}.pid";
......
1570 1594
		return;
1571 1595
	}
1572 1596

  
1597
	openvpn_delete_tmp($mode, $vpnid);
1598

  
1573 1599
	/* Do not start an instance if we are not CARP master on this vip! */
1574 1600
	if (strstr($settings['interface'], "_vip")) {
1575 1601
	       	$carpstatus = get_carp_bind_status($settings['interface']);
......
1649 1675
	/* remove the configuration files */
1650 1676
	unlink_if_exists("{$g['openvpn_base']}/{$mode_id}/*/*");
1651 1677
	unlink_if_exists("{$g['openvpn_base']}/{$mode_id}/*");
1652
	openvpn_clean_rules($mode, $vpnid);
1678
	openvpn_delete_tmp($mode, $vpnid);
1653 1679
}
1654 1680

  
1655 1681
function openvpn_resync_csc(& $settings) {
......
2405 2431
	return false;
2406 2432
}
2407 2433

  
2408
function openvpn_clean_rules($mode, $id) {
2409
	global $g;
2410

  
2411
	if ($mode == "server") {
2412
		unlink_if_exists("{$g['tmp_path']}/ovpn_ovpns{$id}_*.rules");
2413
	}
2414
}
2415

  
2416 2434
?>
src/etc/inc/service-utils.inc
763 763
				$id = htmlspecialchars($extras['id']);
764 764
				$pidfile = "{$g['varrun_path']}/openvpn_{$vpnmode}{$id}.pid";
765 765
				killbypid($pidfile);
766
				openvpn_clean_rules($vpnmode, $id);
766
				openvpn_delete_tmp($vpnmode, $id);
767 767
			}
768 768
			break;
769 769
		case 'syslogd':
src/usr/local/sbin/openvpn.attributes.sh
20 20
# See the License for the specific language governing permissions and
21 21
# limitations under the License.
22 22

  
23
if [ -z "${untrusted_ip6}" ]; then
24
	ipaddress="${untrusted_ip}"
25
else
26
	ipaddress="${untrusted_ip6}"
27
fi
28

  
29
# Remote Access (SSL/TLS) mode
30
if [ -z "${username}" ]; then
31
	if [ "$script_type" = "client-connect" ]; then
32
		/usr/bin/logger -t openvpn "openvpn server '${dev}' user cert CN '${X509_0_CN}' address '${ipaddress}' - connected"
33
	elif [ "$script_type" = "client-disconnect" ]; then
34
		/usr/bin/logger -t openvpn "openvpn server '${dev}' user cert CN '${X509_0_CN}' address '${ipaddress}' - disconnected"
35
		/sbin/pfctl -k $ifconfig_pool_remote_ip
36
		/sbin/pfctl -K $ifconfig_pool_remote_ip
37
		/sbin/pfctl -k $ifconfig_pool_remote_ip6
38
		/sbin/pfctl -K $ifconfig_pool_remote_ip6
23
# Signal deferred handler
24
if [ "${script_type}" = "client-connect" ]; then
25
	/bin/echo 2 > "${client_connect_deferred_file}"
26
	if [ -f /tmp/"${common_name}" ]; then
27
		/bin/cat /tmp/"${common_name}" > "${client_connect_config_file}"
28
		/bin/rm /tmp/"${common_name}"
39 29
	fi
40
	exit 0
41 30
fi
42 31

  
43
lockfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.lock"
44
rulesfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.rules"
45
anchorname="openvpn/${dev}_${username}_${trusted_port}"
46

  
47
if [ "$script_type" = "client-connect" ]; then
48
	/usr/bin/logger -t openvpn "openvpn server '${dev}' user '${username}' address '${ipaddress}' - connected"
49
	i=1
50
	while [ -f "${lockfile}" ]; do
51
		if [ $i -ge 30 ]; then
52
			/bin/echo "Timeout while waiting for lockfile"
53
			exit 1
54
		fi
55

  
56
		/bin/sleep 1
57
		i=$(( i + 1 ))
58
	done
59
	/usr/bin/touch "${lockfile}"
60

  
61
	/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}"
62
	/sbin/pfctl -a "openvpn/${dev}_${username}_${trusted_port}" -f "${rulesfile}"
63

  
64
	if [ -f /tmp/$common_name ]; then
65
		/bin/cat /tmp/$common_name > $1
66
		/bin/rm /tmp/$common_name
67
	fi
68

  
69
	/bin/rm "${lockfile}"
70
elif [ "$script_type" = "client-disconnect" ]; then
71
	/usr/bin/logger -t openvpn "openvpn server '${dev}' user '${username}' address '${ipaddress}' - disconnected"
72
	i=1
73
	while [ -f "${lockfile}" ]; do
74
		if [ $i -ge 30 ]; then
75
			/bin/echo "Timeout while waiting for lockfile"
76
			exit 1
77
		fi
78

  
79
		/bin/sleep 1
80
		i=$(( i + 1 ))
81
	done
82
	/usr/bin/touch "${lockfile}"
83

  
84
	command="/sbin/pfctl -a '${anchorname}' -F rules"
85
	eval $command
86
	/sbin/pfctl -k $ifconfig_pool_remote_ip
87
	/sbin/pfctl -K $ifconfig_pool_remote_ip
88
	/sbin/pfctl -k $ifconfig_pool_remote_ip6
89
	/sbin/pfctl -K $ifconfig_pool_remote_ip6
90

  
91
	/bin/rm "${rulesfile}"
92
	/bin/rm "${lockfile}"
93
fi
32
# Handle 'client-connect' and 'client-disconnect'
33
/usr/bin/nohup /usr/local/sbin/openvpn.connect_async.sh > /dev/null &
94 34

  
35
# Signal "deferred handler started OK" for client-connect
95 36
exit 0
src/usr/local/sbin/openvpn.connect_async.sh
1
#!/bin/sh
2
#
3
# openvpn.connect_async.sh
4
#
5
# part of pfSense (https://www.pfsense.org)
6
# Copyright (c) 2021-2022 Rubicon Communications, LLC (Netgate)
7
# All rights reserved.
8
#
9
# Licensed under the Apache License, Version 2.0 (the "License");
10
# you may not use this file except in compliance with the License.
11
# You may obtain a copy of the License at
12
#
13
# http://www.apache.org/licenses/LICENSE-2.0
14
#
15
# Unless required by applicable law or agreed to in writing, software
16
# distributed under the License is distributed on an "AS IS" BASIS,
17
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
# See the License for the specific language governing permissions and
19
# limitations under the License.
20

  
21
log_session() {
22
	if [ -z "${1}" ]; then
23
		logmsg=""
24
	else
25
		logmsg=" - ${1}"
26
	fi
27

  
28
	if [ -z "${untrusted_ip6}" ]; then
29
		hostaddress="${untrusted_ip}:${untrusted_port}"
30
	else
31
		hostaddress="${untrusted_ip6}:${untrusted_port}"
32
	fi
33
	
34
	if [ -z "${username}" ]; then
35
		hostuser="user cert CN '${X509_0_CN}'"
36
	else
37
		hostuser="user '${username}'"
38
	fi
39

  
40
	/usr/bin/logger -t openvpn "openvpn server '${dev}' ${hostuser} address '${hostaddress}'${logmsg}"
41
}
42

  
43
if [ -n "${username}" ]; then
44
	lockfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.lock"
45
	rulesfile="/tmp/ovpn_${dev}_${username}_${trusted_port}.rules"
46
	anchorname="openvpn/${dev}_${username}_${trusted_port}"
47
fi
48

  
49
if [ "${script_type}" = "client-disconnect" ]; then
50
	log_session "disconnected"
51

  
52
	if [ -n "${username}" ]; then
53
		# Avoid race condition. See https://redmine.pfsense.org/issues/9206
54
		i=1
55
		while
56
			if [ -f "${lockfile}" ]; then
57
				/bin/sleep 1
58
				i="$((i+1))"
59
			else
60
				break
61
			fi
62
			[ "${i}" -lt 30 ]
63
		do :;  done
64

  
65
		if [ ${i} -ge 30 ]; then
66
			log_session "Timeout while waiting for lockfile"
67
		else
68
			/usr/bin/touch "${lockfile}"
69
			eval "/sbin/pfctl -a '${anchorname}' -F rules"
70
			/bin/rm "${lockfile}"
71

  
72
			/bin/rm "${rulesfile}"
73
		fi
74
	fi
75

  
76
	/sbin/pfctl -k $ifconfig_pool_remote_ip
77
	/sbin/pfctl -K $ifconfig_pool_remote_ip
78
	/sbin/pfctl -k $ifconfig_pool_remote_ip6
79
	/sbin/pfctl -K $ifconfig_pool_remote_ip6
80
elif [ "${script_type}" = "client-connect" ]; then
81
	log_session "connecting"
82

  
83
	# Verify defer status code before continuing
84
	i=1
85
	while
86
		deferstatus=$(/usr/bin/head -1 "${client_connect_deferred_file}")
87
		if [ "${deferstatus}" -ne 2 ]; then
88
			/bin/sleep 1
89
			i="$((i+1))"
90
		else
91
			break
92
		fi
93
		[ "${i}" -lt 3 ]
94
	do :;  done
95
	if [ ${i} -ge 3 ]; then
96
		log_session "server write to defer file failed"
97
		/bin/echo 0 > ${client_connect_deferred_file}
98
		exit 1
99
	fi
100

  
101
	# Get active sessions
102
	# active_sessions :: ovpns1_'user_01'_30001|ovpns1_'user_01'_30002|ovpns1_'user_01'_30003|
103
	# Use php-cgi - see https://redmine.pfsense.org/issues/12382
104
	active_sessions=$("/usr/local/bin/php-cgi" -f "/usr/local/sbin/openvpn_connect_async.php")
105

  
106
	# Process "Duplicate Connection Limit" setting
107
	if [ -n "${active_sessions}" ]; then
108
		vpnid=$(/bin/echo ${dev} | /usr/bin/sed -e 's/ovpns//g')
109
		if [ -f "/var/etc/openvpn/server${vpnid}/connuserlimit" ]; then
110
			sessionlimit=$(/usr/bin/head -1 "/var/etc/openvpn/server${vpnid}/connuserlimit" | /usr/bin/sed -e 's/[[:space:]]//g')
111
			if [ "${sessionlimit}" -ge 1 ]; then
112
				if [ -z "${username}" ]; then
113
					usersession="${dev}_'${X509_0_CN}'"
114
				else
115
					usersession="${dev}_'${username}'"
116
				fi
117
				sessioncount=$(/bin/echo "${active_sessions}" | /usr/bin/grep -o "${usersession}" | /usr/bin/wc -l | /usr/bin/sed -e 's/[[:space:]]//g')
118

  
119
				if [ ${sessioncount} -gt ${sessionlimit} ]; then
120
					log_session "active connection limit of '${sessionlimit}' reached"
121
					/bin/echo 0 > ${client_connect_deferred_file}
122
					if [ -n "${username}" ]; then
123
						/bin/rm "${rulesfile}"
124
					fi
125
					exit 1
126
				fi
127
			fi
128
		fi
129
	fi
130

  
131
	if [ -n "${username}" ]; then
132

  
133
		i=1
134
		while
135
			if [ -f "${lockfile}" ]; then
136
				/bin/sleep 1
137
				i="$((i+1))"
138
			else
139
				break
140
			fi
141
			[ "${i}" -lt 30 ]
142
		do :;  done
143
		if [ ${i} -ge 30 ]; then
144
			log_session "Timeout while waiting for lockfile"
145
			/bin/echo 0 > ${client_connect_deferred_file}
146
			exit 1
147
		else
148
			/usr/bin/touch "${lockfile}"
149

  
150
			# for each of this user's anchors loaded in pf
151
			# $session :: ovpns3_'user_01'_61468
152
			# $anchor  :: openvpn/ovpns3_user_01_61468
153
			anchors=$(/sbin/pfctl -s Anchors)
154
			for anchor in $(/bin/echo "${anchors}" | /usr/bin/grep "${dev}_${username}"); do
155
				session=$(/bin/echo "${anchor}" | /usr/bin/sed -r -e 's/.+'"${dev}_${username}"'/'"${dev}_\'${username}\'"'/')
156
				# if no active session exists for the anchor, remove it from pf
157
				if ! (/bin/echo "${active_sessions}" | /usr/bin/grep -q "${session}"); then
158
					eval "/sbin/pfctl -a '${anchor}' -F rules"
159
				fi
160
			done
161

  
162
			/bin/echo "$(/usr/bin/sed -e "s/{clientip}/${ifconfig_pool_remote_ip}/g;s/{clientipv6}/${ifconfig_pool_remote_ip6}/g" "${rulesfile}")" > "${rulesfile}"
163
			eval "/sbin/pfctl -a '${anchorname}' -f '${rulesfile}'"
164

  
165
			/bin/rm "${lockfile}"
166
		fi
167
	fi
168

  
169
	# success; allow client connection
170
	/bin/echo 1 > ${client_connect_deferred_file}
171
	log_session "connected"
172
fi
173

  
174
exit 0
src/usr/local/sbin/openvpn_connect_async.php
1
<?php
2
/*
3
# openvpn_connect_async.php
4
#
5
# part of pfSense (https://www.pfsense.org)
6
# Copyright (c) 2021-2022 Rubicon Communications, LLC (Netgate)
7
# All rights reserved.
8
#
9
# Licensed under the Apache License, Version 2.0 (the "License");
10
# you may not use this file except in compliance with the License.
11
# You may obtain a copy of the License at
12
#
13
# http://www.apache.org/licenses/LICENSE-2.0
14
#
15
# Unless required by applicable law or agreed to in writing, software
16
# distributed under the License is distributed on an "AS IS" BASIS,
17
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
# See the License for the specific language governing permissions and
19
# limitations under the License.
20
*/
21

  
22
// Returns a string with all active sessions delimited by '|'.
23

  
24
require_once("openvpn.inc");
25

  
26
$result = openvpn_get_active_servers();
27
$output = null;
28

  
29
foreach ($result as $server) {
30
	$vpnid = $server["vpnid"];
31
	foreach ($server["conns"] as $client) {
32
		if ( !(empty($client["remote_host"])) ) {
33
			$port = substr($client["remote_host"], strpos($client["remote_host"], ":") + 1);
34
			/* Handle RADIUS backend */
35
			if (escapeshellarg($client["common_name"]) != "'UNDEF'") {
36
				$name = escapeshellarg($client["common_name"]);
37
			} else {
38
				$name = escapeshellarg($client["user_name"]);
39
			}
40
			$name = isset($name) ? trim($name) : NULL;
41
			$port = isset($port) ? trim($port) : NULL;
42
			if ( !empty($name) && !empty($port) ) {
43
				$output .= "ovpns{$vpnid}_{$name}_{$port}|";
44
			}
45
		}
46
	}
47
}
48

  
49
echo (isset($output)) ? $output : '';
50

  
51
?>
src/usr/local/www/vpn_openvpn_server.php
189 189
		$pconfig['local_network'] = $a_server[$id]['local_network'];
190 190
		$pconfig['local_networkv6'] = $a_server[$id]['local_networkv6'];
191 191
		$pconfig['maxclients'] = $a_server[$id]['maxclients'];
192
		$pconfig['connlimit'] = $a_server[$id]['connlimit'];
192 193
		$pconfig['allow_compression'] = $a_server[$id]['allow_compression'];
193 194
		$pconfig['compression'] = $a_server[$id]['compression'];
194 195
		$pconfig['compression_push'] = $a_server[$id]['compression_push'];
......
497 498
		$input_errors[] = gettext("The field 'Concurrent connections' must be numeric.");
498 499
	}
499 500

  
501
	if ($pconfig['connlimit'] && !is_numericint($pconfig['connlimit'])) {
502
		$input_errors[] = gettext("The field 'Duplicate Connection Limit' must be numeric.");
503
	}
504

  
500 505
	if (!array_key_exists($pconfig['topology'], $openvpn_topologies)) {
501 506
		$input_errors[] = gettext("The field 'Topology' contains an invalid selection");
502 507
	}
......
671 676
		$server['local_network'] = $pconfig['local_network'];
672 677
		$server['local_networkv6'] = $pconfig['local_networkv6'];
673 678
		$server['maxclients'] = $pconfig['maxclients'];
679
		$server['connlimit'] = $pconfig['connlimit'];
674 680
		$server['allow_compression'] = $pconfig['allow_compression'];
675 681
		$server['compression'] = $pconfig['compression'];
676 682
		$server['compression_push'] = $pconfig['compression_push'];
......
1338 1344
			'When unset, a new connection from a user will disconnect the previous session. %1$s%1$s' .
1339 1345
			'Users are identified by their username or certificate properties, depending on the VPN configuration. ' .
1340 1346
			'This practice is discouraged security reasons, but may be necessary in some environments.', '<br />');
1347
	
1348
	$section->addInput(new Form_Input(
1349
		'connlimit',
1350
		'Duplicate Connection Limit',
1351
		'number',
1352
		$pconfig['connlimit']
1353
	))->setHelp('Limit the number of concurrent connections from the same user.');
1341 1354

  
1342 1355
	$form->add($section);
1343 1356

  
src/usr/local/www/wizards/openvpn_wizard.inc
452 452

  
453 453
	if ($_POST['concurrentcon'] && !is_numeric($_POST['concurrentcon']))
454 454
		$input_errors[] = "The field 'Concurrent connections' must be numeric.";
455
	
456
	if ($_POST['connuserlimit'] && !is_numeric($_POST['connuserlimit']))
457
		$input_errors[] = "The field 'Duplicate Connection Limit' must be numeric.";
455 458

  
456 459
	if (empty($_POST['tunnelnet']))
457 460
		$input_errors[] = "A 'Tunnel network' must be specified.";
......
649 652
		$server['local_network'] = $pconfig['step10']['localnet'];
650 653
	if (isset($pconfig['step10']['concurrentcon']))
651 654
		$server['maxclients'] = $pconfig['step10']['concurrentcon'];
655
	if (isset($pconfig['step10']['connuserlimit']))
656
		$server['connlimit'] = $pconfig['step10']['connuserlimit'];
652 657
	if (isset($pconfig['step10']['allowcompression']))
653 658
		$server['allow_compression'] = $pconfig['step10']['allowcompression'];
654 659
	if (isset($pconfig['step10']['compression']))
src/usr/local/www/wizards/openvpn_wizard.xml
971 971
			<description>Allow multiple concurrent connections from clients using the same Common Name.&lt;br/&gt;NOTE: This is not generally recommended, but may be needed for some scenarios.</description>
972 972
			<bindstofield>ovpnserver->step10->duplicate_cn</bindstofield>
973 973
		</field>
974
		<field>
975
			<displayname>Duplicate Connection Limit</displayname>
976
			<name>connuserlimit</name>
977
			<description>Limit the number of concurrent connections from the same user.</description>
978
			<type>input</type>
979
			<size>10</size>
980
			<bindstofield>ovpnserver->step10->connuserlimit</bindstofield>
981
		</field>
974 982
		<field>
975 983
			<type>listtopic</type>
976 984
			<name>Client Settings</name>
    (1-1/1)