mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-08-08 09:21:48 +08:00
Allow using no-udp option in vhosts
Signed-off-by: Grigory Trenin <grigory.trenin@gmail.com>
This commit is contained in:
@@ -22,6 +22,8 @@
|
|||||||
- Aligned default values for 'keepalive', 'rekey-time', 'cookie-timeout',
|
- Aligned default values for 'keepalive', 'rekey-time', 'cookie-timeout',
|
||||||
'auth-timeout', 'ban-reset-time', 'max-ban-score', and
|
'auth-timeout', 'ban-reset-time', 'max-ban-score', and
|
||||||
'switch-to-tcp-timeout' with the values documented in sample.config
|
'switch-to-tcp-timeout' with the values documented in sample.config
|
||||||
|
- The `no-udp` option can now be used to disable DTLS for specific vhosts.
|
||||||
|
Previously it was only available in per-user or per-group config.
|
||||||
|
|
||||||
|
|
||||||
* Version 1.4.1 (released 2026-02-28)
|
* Version 1.4.1 (released 2026-02-28)
|
||||||
|
|||||||
@@ -677,6 +677,16 @@ route = 192.168.0.0/255.255.0.0
|
|||||||
# [scope: vhost user]
|
# [scope: vhost user]
|
||||||
no-route = 192.168.5.0/255.255.255.0
|
no-route = 192.168.5.0/255.255.255.0
|
||||||
|
|
||||||
|
# Whether to disable DTLS (UDP) for client connections. If set to true,
|
||||||
|
# the server will only accept TCP connections and will not negotiate DTLS.
|
||||||
|
# This can be useful for clients behind restrictive firewalls or NATs that
|
||||||
|
# have issues with UDP, or to enforce TCP-only operation for specific vhosts.
|
||||||
|
#
|
||||||
|
# This setting can be overridden per vhost, user, or group.
|
||||||
|
#
|
||||||
|
# [scope: vhost user]
|
||||||
|
#no-udp = false
|
||||||
|
|
||||||
# If set, the script /usr/libexec/ocserv-fw will be called to restrict
|
# If set, the script /usr/libexec/ocserv-fw will be called to restrict
|
||||||
# the user to its allowed routes and prevent him from accessing
|
# the user to its allowed routes and prevent him from accessing
|
||||||
# any other routes. In case of defaultroute, the no-routes are restricted.
|
# any other routes. In case of defaultroute, the no-routes are restricted.
|
||||||
|
|||||||
@@ -1308,6 +1308,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name,
|
|||||||
READ_STRING(config->camouflage_secret);
|
READ_STRING(config->camouflage_secret);
|
||||||
} else if (strcmp(name, "camouflage_realm") == 0) {
|
} else if (strcmp(name, "camouflage_realm") == 0) {
|
||||||
READ_STRING(config->camouflage_realm);
|
READ_STRING(config->camouflage_realm);
|
||||||
|
} else if (strcmp(name, "no-udp") == 0) {
|
||||||
|
READ_TF(config->no_udp);
|
||||||
} else {
|
} else {
|
||||||
if (reload == 0)
|
if (reload == 0)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ static void apply_default_config(sec_mod_instance_st *sec_mod_instance,
|
|||||||
vhost_cfg_st *vhost = proc->vhost;
|
vhost_cfg_st *vhost = proc->vhost;
|
||||||
|
|
||||||
if (!gc->has_no_udp) {
|
if (!gc->has_no_udp) {
|
||||||
gc->no_udp = (vhost->perm_config.udp_port != 0) ? 0 : 1;
|
gc->no_udp = vhost->perm_config.config->no_udp;
|
||||||
gc->has_no_udp = 1;
|
gc->has_no_udp = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
@@ -767,6 +767,16 @@ static int forward_udp_to_owner(main_server_st *s, struct listener_st *listener)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (proc_to_send != 0) {
|
if (proc_to_send != 0) {
|
||||||
|
if (GETPCONFIG(s)->udp_port == 0 ||
|
||||||
|
proc_to_send->config->no_udp) {
|
||||||
|
mslog(s, proc_to_send, LOG_WARNING,
|
||||||
|
"Received UDP packet from %s while UDP is disabled for this client. "
|
||||||
|
"Possible cause: compromised worker or incorrect worker match.",
|
||||||
|
human_addr((struct sockaddr *)&cli_addr,
|
||||||
|
cli_addr_size, tbuf, sizeof(tbuf)));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
UdpFdMsg msg = UDP_FD_MSG__INIT;
|
UdpFdMsg msg = UDP_FD_MSG__INIT;
|
||||||
|
|
||||||
if (now - proc_to_send->udp_fd_receive_time <=
|
if (now - proc_to_send->udp_fd_receive_time <=
|
||||||
|
|||||||
@@ -381,6 +381,8 @@ struct cfg_st {
|
|||||||
bool camouflage; /* [scope: vhost] */
|
bool camouflage; /* [scope: vhost] */
|
||||||
char *camouflage_secret; /* [scope: vhost] */
|
char *camouflage_secret; /* [scope: vhost] */
|
||||||
char *camouflage_realm; /* [scope: vhost] */
|
char *camouflage_realm; /* [scope: vhost] */
|
||||||
|
|
||||||
|
bool no_udp; /* [scope: vhost user] */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+2
-2
@@ -787,8 +787,8 @@ static int recv_cookie_auth_reply(worker_st *ws)
|
|||||||
ws, msg->ipv6_local);
|
ws, msg->ipv6_local);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg->config->no_udp != 0)
|
if (msg->config->has_no_udp)
|
||||||
WSPCONFIG(ws)->udp_port = 0;
|
WSCONFIG(ws)->no_udp = msg->config->no_udp;
|
||||||
|
|
||||||
/* routes */
|
/* routes */
|
||||||
if (check_if_default_route(msg->config->routes,
|
if (check_if_default_route(msg->config->routes,
|
||||||
|
|||||||
+2
-1
@@ -2131,7 +2131,8 @@ static int connect_handler(worker_st *ws)
|
|||||||
DTLS_ACTIVE(ws)->udp_state = UP_DISABLED;
|
DTLS_ACTIVE(ws)->udp_state = UP_DISABLED;
|
||||||
DTLS_INACTIVE(ws)->udp_state = UP_DISABLED;
|
DTLS_INACTIVE(ws)->udp_state = UP_DISABLED;
|
||||||
|
|
||||||
if (WSPCONFIG(ws)->udp_port != 0 && req->master_secret_set != 0) {
|
if (WSPCONFIG(ws)->udp_port != 0 && !WSCONFIG(ws)->no_udp &&
|
||||||
|
req->master_secret_set != 0) {
|
||||||
memcpy(ws->master_secret, req->master_secret, TLS_MASTER_SIZE);
|
memcpy(ws->master_secret, req->master_secret, TLS_MASTER_SIZE);
|
||||||
DTLS_ACTIVE(ws)->udp_state = UP_WAIT_FD;
|
DTLS_ACTIVE(ws)->udp_state = UP_WAIT_FD;
|
||||||
DTLS_INACTIVE(ws)->udp_state = UP_WAIT_FD;
|
DTLS_INACTIVE(ws)->udp_state = UP_WAIT_FD;
|
||||||
|
|||||||
@@ -246,7 +246,6 @@ def main():
|
|||||||
# are per-user only and don't appear in the global sample.config — skip those.
|
# are per-user only and don't appear in the global sample.config — skip those.
|
||||||
PER_USER_ONLY = {
|
PER_USER_ONLY = {
|
||||||
"iroute", "hostname", "explicit-ipv4", "explicit-ipv6",
|
"iroute", "hostname", "explicit-ipv4", "explicit-ipv6",
|
||||||
"no-udp",
|
|
||||||
"ipv4-dns", "ipv6-dns", "ipv4-nbns", "ipv6-nbns",
|
"ipv4-dns", "ipv6-dns", "ipv4-nbns", "ipv6-nbns",
|
||||||
}
|
}
|
||||||
for opt in sorted(user_in_code):
|
for opt in sorted(user_in_code):
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
auth = plain[@SRCDIR@/data/test-no-udp.passwd]
|
||||||
|
isolate-workers = @ISOLATE_WORKERS@
|
||||||
|
max-ban-score = 0
|
||||||
|
max-clients = 16
|
||||||
|
listen-proxy-proto = false
|
||||||
|
max-same-clients = 2
|
||||||
|
tcp-port = @PORT@
|
||||||
|
udp-port = @PORT@
|
||||||
|
keepalive = 32400
|
||||||
|
dpd = 440
|
||||||
|
try-mtu-discovery = false
|
||||||
|
server-cert = @SRCDIR@/certs/server-cert.pem
|
||||||
|
server-key = @SRCDIR@/certs/server-key.pem
|
||||||
|
tls-priorities = "PERFORMANCE:%SERVER_PRECEDENCE:%COMPAT"
|
||||||
|
auth-timeout = 40
|
||||||
|
socket-file = ./ocserv-socket
|
||||||
|
occtl-socket-file = @OCCTL_SOCKET@
|
||||||
|
use-occtl = true
|
||||||
|
run-as-user = @USERNAME@
|
||||||
|
run-as-group = @GROUP@
|
||||||
|
device = vpns
|
||||||
|
default-domain = example.com
|
||||||
|
ipv4-dns = 192.168.1.1
|
||||||
|
ipv4-network = @VPNNET@
|
||||||
|
ipv6-network = @VPNNET6@
|
||||||
|
ping-leases = false
|
||||||
|
|
||||||
|
[vhost:udp-restricted.example.com]
|
||||||
|
no-udp = true
|
||||||
|
auth = plain[passwd=@SRCDIR@/data/test-no-udp.passwd]
|
||||||
|
config-per-user = @SRCDIR@/user-config/
|
||||||
|
ca-cert = @SRCDIR@/certs/ca.pem
|
||||||
|
server-cert = @SRCDIR@/certs/server-cert.pem
|
||||||
|
server-key = @SRCDIR@/certs/server-key.pem
|
||||||
|
ipv4-network = @VPNNET@
|
||||||
|
ipv6-network = @VPNNET6@
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
noudp1:*:$5$FTX0hYx4Ns2EyUQJ$eSquiS6eIuqvS7lmVV37RTDGaoyK64l6UE8B370XZx.
|
||||||
|
noudp2:*:$5$O504PV8Po56eV95.$lMKsgC1qidOOSubv9J12Tq0YDtqygzAc7/fAWB/c8T3
|
||||||
|
noudp3:*:$5$pza6pL8iS67.T5jd$aWJuSF0GeoK3f5B8KaVoU4QtYKLARU2ktX3KAX6fuz8
|
||||||
+1
-1
@@ -240,7 +240,7 @@ if get_option('root-tests')
|
|||||||
'test-user-config', 'test-append-routes', 'test-ban',
|
'test-user-config', 'test-append-routes', 'test-ban',
|
||||||
'multiple-routes', 'json', 'test-udp-listen-host',
|
'multiple-routes', 'json', 'test-udp-listen-host',
|
||||||
'test-max-same-1', 'test-vhost-udp-port-inheritance',
|
'test-max-same-1', 'test-vhost-udp-port-inheritance',
|
||||||
'apple-ios', 'ipv6-iface',
|
'apple-ios', 'ipv6-iface', 'test-no-udp',
|
||||||
'disconnect-user', 'disconnect-user2', 'terminate-commands',
|
'disconnect-user', 'disconnect-user2', 'terminate-commands',
|
||||||
'ping-leases', 'test-ban-local', 'test-client-bypass-protocol',
|
'ping-leases', 'test-ban-local', 'test-client-bypass-protocol',
|
||||||
'ipv6-small-net', 'test-camouflage', 'test-camouflage-norealm',
|
'ipv6-small-net', 'test-camouflage', 'test-camouflage-norealm',
|
||||||
|
|||||||
Executable
+125
@@ -0,0 +1,125 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright (C) 2026 Grigory Trenin
|
||||||
|
#
|
||||||
|
# This file is part of ocserv.
|
||||||
|
#
|
||||||
|
# ocserv is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the
|
||||||
|
# Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
# your option) any later version.
|
||||||
|
#
|
||||||
|
# ocserv is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Test no-udp option
|
||||||
|
|
||||||
|
OCCTL="${OCCTL:-../src/occtl/occtl}"
|
||||||
|
OCCTL_SOCKET=./occtl-no-udp-$$.socket
|
||||||
|
SERV="${SERV:-../src/ocserv}"
|
||||||
|
srcdir=${srcdir:-.}
|
||||||
|
PIDFILE=ocserv-pid.$$.tmp
|
||||||
|
CLIPID=oc-pid.$$.tmp
|
||||||
|
PATH=${PATH}:/usr/sbin
|
||||||
|
OUTFILE=occtl-no-udp.$$.tmp
|
||||||
|
|
||||||
|
echo "Testing no-udp option..."
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
set +e
|
||||||
|
echo " * Cleaning up..."
|
||||||
|
test -n "${PID}" && kill ${PID} >/dev/null 2>&1
|
||||||
|
test -n "${PIDFILE}" && rm -f ${PIDFILE} >/dev/null 2>&1
|
||||||
|
test -n "${CLIPID}" && kill $(cat ${CLIPID}) >/dev/null 2>&1
|
||||||
|
test -n "${CLIPID}" && rm -f ${CLIPID} >/dev/null 2>&1
|
||||||
|
test -n "${CONFIG}" && rm -f ${CONFIG} >/dev/null 2>&1
|
||||||
|
rm -f ${OUTFILE}
|
||||||
|
}
|
||||||
|
|
||||||
|
connect_vhost() {
|
||||||
|
VHOST=$1
|
||||||
|
USERNAME=$2
|
||||||
|
|
||||||
|
echo " * Connecting to ${VHOST} at ${ADDRESS}:${PORT}..."
|
||||||
|
( echo ${USERNAME} | ${CMDNS1} ${OPENCONNECT} ${VHOST}:${PORT} --resolve ${VHOST}:${ADDRESS} -u ${USERNAME} --servercert=pin-sha256:xp3scfzy3rOQsv9NcOve/8YVVv+pHr4qNCXEXrNl5s8= -s ${srcdir}/scripts/vpnc-script --pid-file=${CLIPID} --passwd-on-stdin -b )
|
||||||
|
if test $? != 0;then
|
||||||
|
fail $PID "Could not connect to server"
|
||||||
|
fi
|
||||||
|
|
||||||
|
${OCCTL} -s ${OCCTL_SOCKET} show user ${USERNAME} >${OUTFILE}
|
||||||
|
if test $? != 0;then
|
||||||
|
cat ${OUTFILE}
|
||||||
|
fail $PID "occtl show user ${USERNAME} failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep -i "Username: ${USERNAME}" ${OUTFILE} >/dev/null
|
||||||
|
if test $? != 0;then
|
||||||
|
cat ${OUTFILE}
|
||||||
|
fail $PID "Username missing in occtl show user output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep -i "vhost: ${VHOST}" ${OUTFILE} >/dev/null
|
||||||
|
if test $? != 0;then
|
||||||
|
cat ${OUTFILE}
|
||||||
|
fail $PID "Session associated with wrong vhost (expected: ${VHOST})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -i "DTLS cipher:" ${OUTFILE} >/dev/null;then
|
||||||
|
HAVE_DTLS=1
|
||||||
|
else
|
||||||
|
HAVE_DTLS=0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
kill "$(cat ${CLIPID})" 2>/dev/null || true
|
||||||
|
rm -f "${CLIPID}"
|
||||||
|
sleep 3
|
||||||
|
# Restore NS1 routing in case vpnc-script failed to run during server-initiated disconnect
|
||||||
|
reset_client_routes
|
||||||
|
}
|
||||||
|
|
||||||
|
. `dirname $0`/common.sh
|
||||||
|
. `dirname $0`/random-net.sh
|
||||||
|
. `dirname $0`/ns.sh
|
||||||
|
|
||||||
|
eval "${GETPORT}"
|
||||||
|
|
||||||
|
# Run server
|
||||||
|
update_config test-no-udp.config
|
||||||
|
if test "$VERBOSE" = 1;then
|
||||||
|
DEBUG="-d 3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
${CMDNS2} ${SERV} -p ${PIDFILE} -f -c ${CONFIG} ${DEBUG} & PID=$!
|
||||||
|
|
||||||
|
sleep 4
|
||||||
|
|
||||||
|
echo "*** Test 1 - UDP enabled"
|
||||||
|
connect_vhost default noudp1
|
||||||
|
if test "${HAVE_DTLS}" = 0;then
|
||||||
|
fail $PID "DTLS session was not established with default vhost"
|
||||||
|
fi
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
echo "*** Test 2 - UDP disabled per vhost"
|
||||||
|
connect_vhost udp-restricted.example.com noudp2
|
||||||
|
if test "${HAVE_DTLS}" = 1;then
|
||||||
|
fail $PID "DTLS session established despite being disabled for this vhost"
|
||||||
|
fi
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
echo "*** Test 3 - UDP disabled per vhost, but enabled per-user"
|
||||||
|
connect_vhost udp-restricted.example.com noudp3
|
||||||
|
if test "${HAVE_DTLS}" = 0;then
|
||||||
|
fail $PID "DTLS session failed to establish despite user-level override"
|
||||||
|
fi
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
no-udp = false
|
||||||
Reference in New Issue
Block a user