mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-08-08 09:21:48 +08:00
config: add scope annotations and validation script
Add machine-readable [scope: X] annotations to doc/sample.config and
src/vpn.h struct fields to document which config options are permanent,
global-only, per-vhost, or per-user/group overridable.
Scope vocabulary:
global (non-reloadable) -- in perm_cfg_st; requires restart; cannot differ per vhost
vhost (non-reloadable) -- in perm_cfg_st; requires restart; can differ per vhost
global -- in cfg_st; reloadable; cannot be set in [vhost:] sections
vhost -- in cfg_st; reloadable; can differ per vhost
vhost user -- in cfg_st; reloadable; also overridable per user/group
Add tests/check-config-scope.py: a script that cross-checks the annotations
against the actual code:
(a) every option in sample.config has a [scope:] annotation
(b-c) [global] options match error_on_vhost() calls in config.c
(d-e) [vhost user] options match handlers in src/sup-config/file.c
(f) every field in cfg_st and perm_cfg_st has a [scope:] comment
Also fix a pre-existing bug in src/sup-config/file.c: the tunnel-all-dns
option was compared using an underscore ("tunnel_all_dns") instead of
the correct dash ("tunnel-all-dns"), silently ignoring the per-user
setting.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
This commit is contained in:
@@ -24,6 +24,11 @@ variables:
|
||||
-Droot-tests=false
|
||||
JOBS: 6
|
||||
|
||||
config-scope-check:
|
||||
stage: preliminaries
|
||||
script:
|
||||
- python3 tests/check-config-scope.py
|
||||
|
||||
Signoff:
|
||||
stage: preliminaries
|
||||
script:
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
* Version 1.4.2 (unreleased)
|
||||
- Added VIRTUAL HOSTS and CONFIGURATION sections to ocserv(8) man page;
|
||||
documented options scope.
|
||||
- Setting a global-only option inside a [vhost:] section is now a hard
|
||||
configuration error (previously it was ignored). Options are
|
||||
annotated with their scope in doc/sample.config; see ocserv(8) for a
|
||||
description of the configuration scope.
|
||||
- radius: added group-separator option to auth configuration, allowing
|
||||
the separator used in OU= Class attributes to be set to 'semicolon'
|
||||
(default) or 'comma', to support Freeradius deployments (#428)
|
||||
@@ -9,8 +15,6 @@
|
||||
- Replaced autoconf/automake build system with meson (#699)
|
||||
- Added nftables-based ocserv-fw; requires ipcalc-ng/ipcalc (#397)
|
||||
- No longer need to duplicate global options in virtual hosts (#698)
|
||||
- Added VIRTUAL HOSTS section to ocserv(8) man page; documented global options
|
||||
that cannot be overridden at vhost level
|
||||
- Fixed a bug where a correct password was rejected after a wrong password
|
||||
attempt in the same session (#323)
|
||||
- Aligned the default values for 'dpd' and 'mobile-dpd' options with
|
||||
|
||||
+59
-20
@@ -80,6 +80,65 @@ server.
|
||||
Output version of program and exit.
|
||||
|
||||
|
||||
## CONFIGURATION
|
||||
|
||||
Each configuration option has a **scope** that determines where it may appear.
|
||||
|
||||
**Scope** takes one of three values:
|
||||
|
||||
* *global* — the option applies to the entire server and may only appear at
|
||||
the top level of the configuration file. Placing it inside a `[vhost:]`
|
||||
section is a configuration error that prevents the server from starting
|
||||
(e.g. `max-clients`, `device`, `route-add-cmd`).
|
||||
|
||||
* *vhost* — the option may appear at the top level applying to the default
|
||||
virtual host, or inside a specific `[vhost:]` (e.g. `tls-priorities`,
|
||||
`banner`, `cookie-timeout`).
|
||||
|
||||
* *vhost user* — the option may appear at the top level, in a `[vhost:]` section,
|
||||
and also in per-user or per-group supplemental configuration files
|
||||
(e.g. `routes`, `iroutes`, `no-udp`).
|
||||
|
||||
The description of each option in the configuration file carries a `[scope:]`
|
||||
annotation that identifies its scope.
|
||||
|
||||
Furthermore, certain configuration options specified are not-reloadable and a
|
||||
change only takes effect when restarting the server. These options are marked
|
||||
as `(non-reloadable)` in their scope annotation.
|
||||
|
||||
## PER-USER AND PER-GROUP CONFIGURATION
|
||||
|
||||
Options with *vhost user* scope may be further overridden for individual users or
|
||||
groups through supplemental INI-format configuration files. The relevant
|
||||
directories are set with `config-per-user` and `config-per-group` in the main
|
||||
configuration file.
|
||||
|
||||
When a user connects, ocserv looks for a file named after their username in
|
||||
the per-user directory and a file named after their group in the per-group
|
||||
directory. Values found there override the corresponding values from the
|
||||
active virtual host configuration for that session only; they do not affect
|
||||
other connected users.
|
||||
|
||||
Options not specified within a virtual host, or within a per-user/group file,
|
||||
fall back to the global configuration value, or to the built-in default if
|
||||
not set globally.
|
||||
|
||||
## VIRTUAL HOSTS
|
||||
Ocserv supports virtual hosts, allowing a single instance to serve multiple
|
||||
domains with different configurations. This feature operates similarly to
|
||||
virtual hosts in Apache or Nginx — when clients connect requesting a specific
|
||||
domain name (via TLS SNI), the server selects the corresponding virtual host
|
||||
configuration. If no matching virtual host is found, the connection falls back
|
||||
to the global configuration.
|
||||
|
||||
Virtual host sections are introduced in the configuration file with a header
|
||||
of the form:
|
||||
|
||||
[vhost:www.example.com]
|
||||
|
||||
All options that follow (until the next section header or end of file) apply
|
||||
only to that virtual host.
|
||||
|
||||
## AUTHENTICATION
|
||||
Users can be authenticated in multiple ways, which are explained in the following
|
||||
paragraphs. Connected users can be managed using the _occtl_ tool.
|
||||
@@ -243,26 +302,6 @@ should be generated as follows.
|
||||
--load-ca-certificate ca-cert.pem \
|
||||
--template crl.tmpl --outfile crl.pem
|
||||
|
||||
## VIRTUAL HOSTS
|
||||
Ocserv supports virtual hosts, allowing a single instance to serve multiple
|
||||
domains with different configurations. This feature operates similarly to
|
||||
virtual hosts in Apache or Nginx - when clients connect requesting a specific
|
||||
domain name (via TLS SNI), the server selects the corresponding virtual host
|
||||
configuration. If no matching virtual host is found, the connection falls back
|
||||
to the global configuration.
|
||||
|
||||
The global configuration can be thought of as a default virtual host that is
|
||||
used when an incoming connection does not match any explicitly defined virtual
|
||||
host.
|
||||
|
||||
Options not specified within a virtual host are initialized to their default
|
||||
values. Default value is typically zero or an empty string, unless an option's
|
||||
description states otherwise.
|
||||
|
||||
Note that certain options have global scope (affecting the entire server) and
|
||||
cannot be specified within virtual hosts. Such options are only recognized
|
||||
in the global configuration.
|
||||
|
||||
## FILES
|
||||
|
||||
### ocserv's configuration file format
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
# to have been issued within the provided number of seconds. That option is used to
|
||||
# restrict logins even if the KDC provides long time TGT tickets.
|
||||
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#auth = "pam"
|
||||
#auth = "pam[gid-min=1000]"
|
||||
#auth = "plain[passwd=./sample.passwd,otp=./sample.otp]"
|
||||
@@ -59,6 +60,7 @@ auth = "plain[passwd=./sample.passwd]"
|
||||
# will be sufficient to login, irrespective of the main 'auth' entries.
|
||||
# When multiple options are present, they are OR composed (any of them
|
||||
# succeeding allows login).
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#enable-auth = "certificate"
|
||||
#enable-auth = "gssapi"
|
||||
#enable-auth = "gssapi[keytab=/etc/key.tab,require-local-user-map=true,tgt-freshness-time=900]"
|
||||
@@ -73,50 +75,62 @@ auth = "plain[passwd=./sample.passwd]"
|
||||
# PAM.
|
||||
#
|
||||
# Only one accounting method can be specified.
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#acct = "radius[config=/etc/radiusclient/radiusclient.conf]"
|
||||
|
||||
# Use listen-host to limit to a specific IP or to the IPs of a provided
|
||||
# hostname.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global (non-reloadable)]
|
||||
#listen-host = [IP|HOSTNAME]
|
||||
|
||||
# Use udp-listen-host to limit udp to a specific IP or to the IPs of a provided
|
||||
# hostname. if not set, listen-host will be used.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global (non-reloadable)]
|
||||
#udp-listen-host = [IP|HOSTNAME]
|
||||
|
||||
# When the server has a dynamic DNS address (that may change),
|
||||
# should set that to true to ask the client to resolve again on
|
||||
# reconnects.
|
||||
# [scope: vhost]
|
||||
#listen-host-is-dyndns = true
|
||||
|
||||
# move the listen socket within the specified network namespace
|
||||
# [scope: global (non-reloadable)]
|
||||
# listen-netns = "foo"
|
||||
|
||||
# TCP and UDP port number (not configurable per vhost)
|
||||
# [scope: global (non-reloadable)]
|
||||
tcp-port = 443
|
||||
# [scope: global (non-reloadable)]
|
||||
udp-port = 443
|
||||
|
||||
# The user the worker processes will be run as. This should be a dedicated
|
||||
# unprivileged user (e.g., 'ocserv') and no other services should run as this
|
||||
# user.
|
||||
# These settings are global and cannot be set at vhost level.
|
||||
# [scope: global (non-reloadable)]
|
||||
run-as-user = nobody
|
||||
# [scope: global (non-reloadable)]
|
||||
run-as-group = daemon
|
||||
|
||||
# socket file used for IPC with occtl. You only need to set that,
|
||||
# if you use more than a single server.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global (non-reloadable)]
|
||||
#occtl-socket-file = /var/run/occtl.socket
|
||||
|
||||
# socket file used for server IPC (worker-main), will be appended with .PID
|
||||
# It must be accessible within the chroot environment (if any), so it is best
|
||||
# specified relatively to the chroot directory.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global (non-reloadable)]
|
||||
socket-file = /var/run/ocserv-socket
|
||||
|
||||
# The default server directory. Does not require any devices present.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global (non-reloadable)]
|
||||
#chroot-dir = /var/lib/ocserv
|
||||
|
||||
# The key and the certificates of the server
|
||||
@@ -132,6 +146,7 @@ socket-file = /var/run/ocserv-socket
|
||||
# certificate renewal (they are checked and reloaded periodically;
|
||||
# a SIGHUP signal to main server will force reload).
|
||||
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#server-cert = /etc/ocserv/server-cert.pem
|
||||
#server-key = /etc/ocserv/server-key.pem
|
||||
server-cert = ../tests/certs/server-cert.pem
|
||||
@@ -141,26 +156,32 @@ server-key = ../tests/certs/server-key.pem
|
||||
# versions of GnuTLS for supporting DHE ciphersuites.
|
||||
# Can be generated using:
|
||||
# certtool --generate-dh-params --outfile /etc/ocserv/dh.pem
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#dh-params = /etc/ocserv/dh.pem
|
||||
|
||||
# In case PKCS #11, TPM or encrypted keys are used the PINs should be available
|
||||
# in files. The srk-pin-file is applicable to TPM keys only, and is the
|
||||
# storage root key.
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#pin-file = /etc/ocserv/pin.txt
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#srk-pin-file = /etc/ocserv/srkpin.txt
|
||||
|
||||
# The password or PIN needed to unlock the key in server-key file.
|
||||
# Only needed if the file is encrypted or a PKCS #11 object. This
|
||||
# is an alternative method to pin-file.
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#key-pin = 1234
|
||||
|
||||
# The SRK PIN for TPM.
|
||||
# This is an alternative method to srk-pin-file.
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#srk-pin = 1234
|
||||
|
||||
# The Certificate Authority that will be used to verify
|
||||
# client certificates (public keys) if certificate authentication
|
||||
# is set.
|
||||
# [scope: vhost (non-reloadable)]
|
||||
#ca-cert = /etc/ocserv/ca.pem
|
||||
ca-cert = ../tests/certs/ca.pem
|
||||
|
||||
@@ -168,6 +189,7 @@ ca-cert = ../tests/certs/ca.pem
|
||||
# processes. Typically this should not be set as the number of processes
|
||||
# is determined automatically by the initially set maximum number of clients.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global (non-reloadable)]
|
||||
#sec-mod-scale = 4
|
||||
|
||||
|
||||
@@ -190,22 +212,27 @@ ca-cert = ../tests/certs/ca.pem
|
||||
# disabling that option and report the failures you, along with system and debugging
|
||||
# information at: https://gitlab.com/openconnect/ocserv/issues
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
isolate-workers = true
|
||||
|
||||
# A banner to be displayed on clients after connection
|
||||
# [scope: vhost]
|
||||
#banner = "Welcome"
|
||||
|
||||
# A banner to be displayed on clients before connection
|
||||
# [scope: vhost]
|
||||
#pre-login-banner = "Welcome"
|
||||
|
||||
# Limit the number of clients. Unset or set to zero if unknown. In
|
||||
# that case the maximum value is ~8k clients.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
#max-clients = 1024
|
||||
max-clients = 16
|
||||
|
||||
# Limit the number of identical clients (i.e., users connecting
|
||||
# multiple times). Unset or set to zero for unlimited.
|
||||
# [scope: vhost user]
|
||||
max-same-clients = 2
|
||||
|
||||
# When the server receives connections from a proxy, like haproxy
|
||||
@@ -215,6 +242,7 @@ max-same-clients = 2
|
||||
# and v2 versions of proxy protocol are supported, the v2 version
|
||||
# is recommended as it is more efficient in parsing.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
#listen-proxy-proto = true
|
||||
|
||||
# Rate limit the number of incoming connections to one every X milliseconds
|
||||
@@ -222,21 +250,25 @@ max-same-clients = 2
|
||||
# makes the server more resilient (and prevents connection failures) on
|
||||
# multiple concurrent connections. Set to zero for no limit.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
rate-limit-ms = 100
|
||||
|
||||
# Stats report time. The number of seconds after which each
|
||||
# worker process will report its usage statistics (number of
|
||||
# bytes transferred etc). This is useful when accounting like
|
||||
# radius is in use.
|
||||
# [scope: vhost user]
|
||||
#stats-report-time = 360
|
||||
|
||||
# Stats reset time. The period of time statistics kept by main/sec-mod
|
||||
# processes will be reset. These are the statistics shown by cmd
|
||||
# 'occtl show stats'. For daily: 86400, weekly: 604800
|
||||
# This is unrelated to stats-report-time.
|
||||
# [scope: global (non-reloadable)]
|
||||
server-stats-reset-time = 604800
|
||||
|
||||
# Keepalive in seconds
|
||||
# [scope: vhost user]
|
||||
keepalive = 32400
|
||||
|
||||
# Dead peer detection in seconds.
|
||||
@@ -244,6 +276,7 @@ keepalive = 32400
|
||||
# needs to be short enough to prevent the NAT disassociating
|
||||
# his UDP session from the port number. Otherwise the client
|
||||
# could have his UDP connection stalled, for several minutes.
|
||||
# [scope: vhost user]
|
||||
dpd = 90
|
||||
|
||||
# Dead peer detection for mobile clients. That needs to
|
||||
@@ -251,6 +284,7 @@ dpd = 90
|
||||
# often by the DPD messages, and save battery.
|
||||
# The mobile clients are distinguished from the header
|
||||
# 'X-AnyConnect-Identifier-Platform'.
|
||||
# [scope: vhost user]
|
||||
mobile-dpd = 1800
|
||||
|
||||
# If using DTLS, and no UDP traffic is received for this
|
||||
@@ -259,14 +293,17 @@ mobile-dpd = 1800
|
||||
# in the case that there is a NAT and the UDP translation
|
||||
# was deleted. If this is unset, do not attempt to use this
|
||||
# recovery mechanism.
|
||||
# [scope: vhost]
|
||||
switch-to-tcp-timeout = 25
|
||||
|
||||
# MTU discovery (DPD must be enabled)
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
try-mtu-discovery = false
|
||||
|
||||
# To enable load-balancer connection draining, set server-drain-ms to a value
|
||||
# higher than your load-balancer health probe interval.
|
||||
# [scope: global]
|
||||
#server-drain-ms = 15000
|
||||
|
||||
# If you have a certificate from a CA that provides an OCSP
|
||||
@@ -276,12 +313,14 @@ try-mtu-discovery = false
|
||||
# You can update this response periodically using:
|
||||
# ocsptool --ask --load-cert=your_cert --load-issuer=your_ca --outfile response
|
||||
# Make sure that you replace the following file in an atomic way.
|
||||
# [scope: vhost]
|
||||
#ocsp-response = /etc/ocserv/ocsp.der
|
||||
|
||||
# The object identifier that will be used to read the user ID in the client
|
||||
# certificate. The object identifier should be part of the certificate's DN
|
||||
# Useful OIDs are:
|
||||
# CN = 2.5.4.3, UID = 0.9.2342.19200300.100.1.1, SAN(rfc822name)
|
||||
# [scope: vhost]
|
||||
cert-user-oid = 0.9.2342.19200300.100.1.1
|
||||
|
||||
# The object identifier that will be used to read the user group in the
|
||||
@@ -289,23 +328,33 @@ cert-user-oid = 0.9.2342.19200300.100.1.1
|
||||
# DN. If the user may belong to multiple groups, then use multiple such fields
|
||||
# in the certificate's DN. Useful OIDs are:
|
||||
# OU (organizational unit) = 2.5.4.11
|
||||
# [scope: vhost]
|
||||
#cert-group-oid = 2.5.4.11
|
||||
|
||||
# The revocation list of the certificates issued by the 'ca-cert' above.
|
||||
# See the manual to generate an empty CRL initially. The CRL will be reloaded
|
||||
# periodically when ocserv detects a change in the file. To force a reload use
|
||||
# SIGHUP.
|
||||
# [scope: vhost]
|
||||
#crl = /etc/ocserv/crl.pem
|
||||
|
||||
# Uncomment this to enable compression negotiation (LZS, LZ4).
|
||||
# To increase security it is recommended to keep it disabled;
|
||||
# see https://ocserv.openconnect-vpn.net/technical.html for rationale.
|
||||
# [scope: vhost]
|
||||
#compression = false
|
||||
|
||||
# Set the priority order of compression algorithms. Takes a colon-separated
|
||||
# list of algorithms (e.g., "lz4:lzs"). This is a global setting and cannot
|
||||
# be set per virtual host.
|
||||
# [scope: global]
|
||||
#compression-algo-priority = lz4:lzs
|
||||
|
||||
# Set the minimum size under which a packet will not be compressed.
|
||||
# That is to allow low-latency for VoIP packets. The default size
|
||||
# is 256 bytes. Modify it if the clients typically use compression
|
||||
# as well of VoIP with codecs that exceed the default value.
|
||||
# [scope: vhost]
|
||||
#no-compress-limit = 256
|
||||
|
||||
# GnuTLS priority string; note that SSL 3.0 is disabled by default
|
||||
@@ -322,6 +371,7 @@ cert-user-oid = 0.9.2342.19200300.100.1.1
|
||||
# difference with AES_128_CBC_SHA1 (the default for anyconnect clients)
|
||||
# in your system.
|
||||
|
||||
# [scope: vhost]
|
||||
tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1"
|
||||
|
||||
# More combinations in priority strings are available, check
|
||||
@@ -334,24 +384,29 @@ tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0:-VERS-TLS1.0:-V
|
||||
# cipher as the primary TLS channel.Note also, that this option implies
|
||||
# that the dtls-legacy option is false; this option cannot be enforced
|
||||
# in the legacy/compat protocol.
|
||||
# [scope: vhost]
|
||||
#match-tls-dtls-ciphers = true
|
||||
|
||||
# The time (in seconds) that a client is allowed to stay connected prior
|
||||
# to authentication
|
||||
# [scope: global]
|
||||
auth-timeout = 240
|
||||
|
||||
# The time (in seconds) that a client is allowed to stay idle (no traffic)
|
||||
# before being disconnected. Unset to disable.
|
||||
# [scope: vhost user]
|
||||
#idle-timeout = 1200
|
||||
|
||||
# The time (in seconds) that a client is allowed to stay connected
|
||||
# Unset to disable. When set a client will be disconnected after being
|
||||
# continuously connected for this amount of time, and its cookies will
|
||||
# be invalidated (i.e., re-authentication will be required).
|
||||
# [scope: vhost user]
|
||||
#session-timeout = 86400
|
||||
|
||||
# The time (in seconds) that a mobile client is allowed to stay idle (no
|
||||
# traffic) before being disconnected. Unset to disable.
|
||||
# [scope: vhost user]
|
||||
#mobile-idle-timeout = 2400
|
||||
|
||||
# Banning clients in ocserv works with a point system. IP addresses
|
||||
@@ -365,18 +420,24 @@ auth-timeout = 240
|
||||
# are global and cannot be set at vhost level.
|
||||
#
|
||||
# Set to zero to disable.
|
||||
# [scope: global]
|
||||
max-ban-score = 80
|
||||
|
||||
# The duration (in seconds) an IP address remains banned
|
||||
# after exceeding max-ban-score.
|
||||
# [scope: global]
|
||||
ban-time = 300
|
||||
|
||||
# The time (in seconds) that all score kept for a client is reset.
|
||||
# [scope: global]
|
||||
ban-reset-time = 1200
|
||||
|
||||
# In case you'd like to change the default points.
|
||||
# [scope: global]
|
||||
#ban-points-wrong-password = 10
|
||||
# [scope: global]
|
||||
#ban-points-connection = 1
|
||||
# [scope: global]
|
||||
#ban-points-kkdcp = 1
|
||||
|
||||
# Cookie timeout (in seconds)
|
||||
@@ -386,22 +447,26 @@ ban-reset-time = 1200
|
||||
# the user's connected time, and after user disconnection it
|
||||
# remains active for this amount of time. That setting should allow a
|
||||
# reasonable amount of time for roaming between different networks.
|
||||
# [scope: vhost]
|
||||
cookie-timeout = 300
|
||||
|
||||
# If this is enabled (not recommended) the cookies will stay
|
||||
# valid even after a user manually disconnects, and until they
|
||||
# expire. This may improve roaming with some broken clients.
|
||||
# [scope: vhost]
|
||||
#persistent-cookies = true
|
||||
|
||||
# Whether roaming is allowed, i.e., if true a cookie is
|
||||
# restricted to a single IP address and cannot be reused
|
||||
# from a different IP.
|
||||
# [scope: vhost user]
|
||||
deny-roaming = false
|
||||
|
||||
# ReKey time (in seconds)
|
||||
# ocserv will ask the client to refresh keys periodically once
|
||||
# this amount of seconds is elapsed. Set to zero to disable (note
|
||||
# that, some clients fail if rekey is disabled).
|
||||
# [scope: vhost]
|
||||
rekey-time = 172800
|
||||
|
||||
# ReKey method
|
||||
@@ -411,6 +476,7 @@ rekey-time = 172800
|
||||
# new-tunnel: Will instruct the client to discard and re-establish the channel.
|
||||
# Use this option only if the connecting clients have issues with the ssl
|
||||
# option.
|
||||
# [scope: vhost]
|
||||
rekey-method = ssl
|
||||
|
||||
# Script to call when a client connects and obtains an IP.
|
||||
@@ -436,27 +502,33 @@ rekey-method = ssl
|
||||
# These scripts are global and cannot be set at vhost level.
|
||||
# They receive the virtual host name via the VHOST environment variable.
|
||||
|
||||
# [scope: global]
|
||||
#connect-script = /usr/bin/myscript
|
||||
# [scope: global]
|
||||
#disconnect-script = /usr/bin/myscript
|
||||
|
||||
# This script is to be called when the client's advertised hostname becomes
|
||||
# available. It will contain REASON with "host-update" value and the
|
||||
# variable REMOTE_HOSTNAME in addition to the connect variables.
|
||||
|
||||
# [scope: global]
|
||||
#host-update-script = /usr/bin/myhostnamescript
|
||||
|
||||
# UTMP
|
||||
# Register the connected clients to utmp. This will allow viewing
|
||||
# the connected clients using the command 'who'.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
#use-utmp = true
|
||||
|
||||
# Whether to enable support for the occtl tool (i.e., either through D-BUS,
|
||||
# or via a unix socket).
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
use-occtl = true
|
||||
|
||||
# PID file. It can be overridden in the command line.
|
||||
# [scope: global (non-reloadable)]
|
||||
pid-file = /var/run/ocserv.pid
|
||||
|
||||
# Log Level. Ocserv sends the logging messages to standard error
|
||||
@@ -471,6 +543,7 @@ pid-file = /var/run/ocserv.pid
|
||||
# 4 http
|
||||
# 8 sensitive
|
||||
# 9 TLS
|
||||
# [scope: global (non-reloadable)]
|
||||
log-level = 2
|
||||
|
||||
# Set the protocol-defined priority (SO_PRIORITY) for packets to
|
||||
@@ -478,10 +551,12 @@ log-level = 2
|
||||
# priority. Alternatively this can be used to set the IP Type-
|
||||
# Of-Service, by setting it to a hexadecimal number (e.g., 0x20).
|
||||
# This can be set per user/group or globally.
|
||||
# [scope: vhost user]
|
||||
#net-priority = 3
|
||||
|
||||
# Set the VPN worker process into a specific cgroup. This is Linux
|
||||
# specific and can be set per user/group or globally.
|
||||
# [scope: vhost user]
|
||||
#cgroup = "cpuset,cpu:test"
|
||||
|
||||
#
|
||||
@@ -490,14 +565,17 @@ log-level = 2
|
||||
|
||||
# The name to use for the tun device
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
device = vpns
|
||||
|
||||
# Whether the generated IPs will be predictable, i.e., IP stays the
|
||||
# same for the same user when possible.
|
||||
# [scope: vhost]
|
||||
predictable-ips = true
|
||||
|
||||
# The default domain to be advertised. Multiple domains (functional on
|
||||
# openconnect clients) can be provided in a space separated list.
|
||||
# [scope: vhost]
|
||||
default-domain = example.com
|
||||
#default-domain = "example.com one.example.com"
|
||||
|
||||
@@ -509,36 +587,44 @@ default-domain = example.com
|
||||
# Note that, you could use addresses from a subnet of your LAN network if you
|
||||
# enable [proxy arp in the LAN interface](http://ocserv.openconnect-vpn.net/recipes-ocserv-pseudo-bridge.html);
|
||||
# in that case it is recommended to set ping-leases to true.
|
||||
# [scope: vhost user]
|
||||
ipv4-network = 192.168.1.0
|
||||
# [scope: vhost user]
|
||||
ipv4-netmask = 255.255.255.0
|
||||
|
||||
# An alternative way of specifying the network:
|
||||
#ipv4-network = 192.168.1.0/24
|
||||
|
||||
# The IPv6 subnet that leases will be given from.
|
||||
# [scope: vhost user]
|
||||
#ipv6-network = fda9:4efe:7e3b:03ea::/48
|
||||
|
||||
# Specify the size of the network to provide to clients. It is
|
||||
# generally recommended to provide clients with a /64 network in
|
||||
# IPv6, but any subnet may be specified. To provide clients only
|
||||
# with a single IP use the prefix 128.
|
||||
# [scope: vhost user]
|
||||
#ipv6-subnet-prefix = 128
|
||||
#ipv6-subnet-prefix = 64
|
||||
|
||||
# Whether to tunnel all DNS queries via the VPN. This is the default
|
||||
# when a default route is set.
|
||||
# [scope: vhost user]
|
||||
#tunnel-all-dns = true
|
||||
|
||||
# The advertised DNS server. Use multiple lines for
|
||||
# multiple servers.
|
||||
# [scope: vhost user]
|
||||
# dns = fc00::4be0
|
||||
dns = 192.168.1.2
|
||||
|
||||
# The NBNS server (if any)
|
||||
# [scope: vhost user]
|
||||
#nbns = 192.168.1.3
|
||||
|
||||
# The domains over which the provided DNS should be used. Use
|
||||
# multiple lines for multiple domains.
|
||||
# [scope: vhost user]
|
||||
#split-dns = example.com
|
||||
|
||||
# Prior to leasing any IP from the pool ping it to verify that
|
||||
@@ -546,12 +632,14 @@ dns = 192.168.1.2
|
||||
# Only set to true, if there can be occupied addresses in the
|
||||
# IP range for leases.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
ping-leases = false
|
||||
|
||||
# Use this option to set a link MTU value to the incoming
|
||||
# connections. Unset to use the default MTU of the TUN device.
|
||||
# Note that the MTU is negotiated using the value set and the
|
||||
# value sent by the peer.
|
||||
# [scope: vhost user]
|
||||
#mtu = 1420
|
||||
|
||||
# Unset to enable bandwidth restrictions (in bytes/second). The
|
||||
@@ -559,12 +647,15 @@ ping-leases = false
|
||||
# The RX direction refers to received data on the server from the
|
||||
# VPN client, and the TX refers to transmitted data by the server
|
||||
# to the client.
|
||||
# [scope: vhost user]
|
||||
#rx-data-per-sec = 40000
|
||||
# [scope: vhost user]
|
||||
#tx-data-per-sec = 40000
|
||||
|
||||
# The number of packets (of MTU size) that are available in
|
||||
# the output buffer. The default is low to improve latency.
|
||||
# Setting it higher will improve throughput.
|
||||
# [scope: vhost]
|
||||
#output-buffer = 10
|
||||
|
||||
# Routes to be forwarded to the client. If you need the
|
||||
@@ -575,6 +666,7 @@ ping-leases = false
|
||||
# comment out all routes from the server, or use the special keyword
|
||||
# 'default'.
|
||||
|
||||
# [scope: vhost user]
|
||||
route = 10.10.10.0/255.255.255.0
|
||||
route = 192.168.0.0/255.255.0.0
|
||||
#route = fef4:db8:1000:1001::/64
|
||||
@@ -583,6 +675,7 @@ route = 192.168.0.0/255.255.0.0
|
||||
# Subsets of the routes above that will not be routed by
|
||||
# the server.
|
||||
|
||||
# [scope: vhost user]
|
||||
no-route = 192.168.5.0/255.255.255.0
|
||||
|
||||
# If set, the script /usr/libexec/ocserv-fw will be called to restrict
|
||||
@@ -590,12 +683,14 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
# any other routes. In case of defaultroute, the no-routes are restricted.
|
||||
# All the routes applied by ocserv can be reverted using /usr/libexec/ocserv-fw
|
||||
# --removeall. This option can be set globally or in the per-user configuration.
|
||||
# [scope: vhost user]
|
||||
#restrict-user-to-routes = true
|
||||
|
||||
# This option implies restrict-user-to-routes set to true. If set, the
|
||||
# script /usr/libexec/ocserv-fw will be called to restrict the user to
|
||||
# access specific ports in the network. This option can be set globally
|
||||
# or in the per-user configuration.
|
||||
# [scope: vhost user]
|
||||
#restrict-user-to-ports = "tcp(443), tcp(80), udp(443), sctp(99), tcp(583), icmp(), icmpv6()"
|
||||
|
||||
# You could also use negation, i.e., block the user from accessing these ports only.
|
||||
@@ -604,6 +699,7 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
# When set to true, all client's iroutes are made visible to all
|
||||
# connecting clients except for the ones offering them. This option
|
||||
# only makes sense if config-per-user is set.
|
||||
# [scope: vhost]
|
||||
#expose-iroutes = true
|
||||
|
||||
# Groups that a client is allowed to select from.
|
||||
@@ -617,22 +713,26 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
# Add multiple entries for multiple groups. Note that the
|
||||
# groups are global and cannot be overridden per vhost.
|
||||
# The group may be followed by a user-friendly name in brackets.
|
||||
# [scope: vhost]
|
||||
#select-group = group1
|
||||
#select-group = group2[My special group]
|
||||
|
||||
# Allow easy selection of the authgroup by the user by enabling
|
||||
# a URL such as vpn.example.com/group_name that the user can
|
||||
# connect, that will not prompt for the group. Default is disabled.
|
||||
# [scope: vhost]
|
||||
#select-group-by-url = false
|
||||
|
||||
# The name of the (virtual) group that if selected it would assign the user
|
||||
# to its default group.
|
||||
# [scope: vhost]
|
||||
#default-select-group = DEFAULT
|
||||
|
||||
# Instead of specifying manually all the allowed groups, you may instruct
|
||||
# ocserv to scan all available groups and include the full list. This
|
||||
# option will scan on startup available groups (e.g., from the password file,
|
||||
# or PAM) and offer these.
|
||||
# [scope: vhost]
|
||||
#auto-select-group = true
|
||||
|
||||
# Configuration files that will be applied per user connection or
|
||||
@@ -654,12 +754,16 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
# hostname to override any proposed by the user. Note also, that, any
|
||||
# routes, no-routes, DNS or NBNS servers present will overwrite the global ones.
|
||||
|
||||
# [scope: vhost]
|
||||
#config-per-user = /etc/ocserv/config-per-user/
|
||||
# [scope: vhost]
|
||||
#config-per-group = /etc/ocserv/config-per-group/
|
||||
|
||||
# When config-per-xxx is specified and there is no group or user that
|
||||
# matches, then utilize the following configuration.
|
||||
# [scope: vhost]
|
||||
#default-user-config = /etc/ocserv/defaults/user.conf
|
||||
# [scope: vhost]
|
||||
#default-group-config = /etc/ocserv/defaults/group.conf
|
||||
|
||||
# The system command to use to setup a route. %{R} will be replaced with the
|
||||
@@ -669,11 +773,14 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
# like 192.168.2.0/255.255.255.0 and %{RI} 192.168.2.0/24 (the argument of iroute).
|
||||
# These settings are global and cannot be set at vhost level.
|
||||
|
||||
# [scope: global]
|
||||
#route-add-cmd = "ip route add %{R} dev %{D}"
|
||||
# [scope: global]
|
||||
#route-del-cmd = "ip route delete %{R} dev %{D}"
|
||||
|
||||
# This option allows one to forward a proxy. The special keywords '%{U}'
|
||||
# and '%{G}', if present will be replaced by the username and group name.
|
||||
# [scope: vhost]
|
||||
#proxy-url = http://example.com/
|
||||
#proxy-url = http://example.com/%{U}/
|
||||
|
||||
@@ -689,6 +796,7 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
#
|
||||
# The following option is available in ocserv, when compiled with GSSAPI support.
|
||||
|
||||
# [scope: vhost]
|
||||
#kkdcp = "SERVER-PATH KERBEROS-REALM PROTOCOL@SERVER:PORT"
|
||||
#kkdcp = "/KdcProxy KERBEROS.REALM udp@127.0.0.1:88"
|
||||
#kkdcp = "/KdcProxy KERBEROS.REALM tcp@127.0.0.1:88"
|
||||
@@ -715,6 +823,7 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
# set to true).
|
||||
# (2) This option cannot be set per-user or per-group; only the global
|
||||
# version is being sent to client.
|
||||
# [scope: vhost user]
|
||||
#user-profile = profile.xml
|
||||
|
||||
#
|
||||
@@ -725,6 +834,7 @@ no-route = 192.168.5.0/255.255.255.0
|
||||
# will not require clients to present their certificate on every TLS
|
||||
# connection. It must be set to true to support legacy CISCO clients
|
||||
# and openconnect clients < 7.08. When set to true, it implies dtls-legacy = true.
|
||||
# [scope: vhost]
|
||||
cisco-client-compat = true
|
||||
|
||||
# This option allows one to disable the DTLS-PSK negotiation (enabled by default).
|
||||
@@ -732,6 +842,7 @@ cisco-client-compat = true
|
||||
# the pre-draft-DTLS negotiation inherited from AnyConnect. It allows the
|
||||
# DTLS channel to negotiate its ciphers and the DTLS protocol version.
|
||||
# This option is global and cannot be set at vhost level.
|
||||
# [scope: global]
|
||||
#dtls-psk = false
|
||||
|
||||
# This option allows one to disable the legacy DTLS negotiation (enabled by default,
|
||||
@@ -739,17 +850,20 @@ cisco-client-compat = true
|
||||
# The legacy DTLS uses a pre-draft version of the DTLS protocol and was
|
||||
# from AnyConnect protocol. It has several limitations, that are addressed
|
||||
# by the dtls-psk protocol supported by openconnect 7.08+.
|
||||
# [scope: vhost]
|
||||
dtls-legacy = true
|
||||
|
||||
# This option will enable the settings needed for Cisco SVC IPPhone clients
|
||||
# to connect. It implies dtls-legacy = true and tls-priorities is changed to
|
||||
# only the ciphers the device supports.
|
||||
# [scope: vhost]
|
||||
cisco-svc-client-compat = false
|
||||
|
||||
# This option will enable the X-CSTP-Client-Bypass-Protocol (disabled by default).
|
||||
# If the server has not configured an IPv6 or IPv4 address pool, enabling this option
|
||||
# will instruct the client to bypass the server for that IP protocol. The option is
|
||||
# currently only understood by Anyconnect clients.
|
||||
# [scope: vhost user]
|
||||
client-bypass-protocol = false
|
||||
|
||||
# The following options are related to server camouflage (hidden service)
|
||||
@@ -759,15 +873,18 @@ client-bypass-protocol = false
|
||||
# With "camouflage" enabled, connection to the VPN can be established only if the client provided a specific
|
||||
# "secret string" in the connection URL, e.g. "https://example.com/?mysecretkey",
|
||||
# otherwise the server will return HTTP error for all requests.
|
||||
# [scope: vhost]
|
||||
camouflage = false
|
||||
|
||||
# The URL prefix that should be set on the client (after '?' sign) to pass through the camouflage check,
|
||||
# e.g. in case of 'mysecretkey', the server URL on the client should be like "https://example.com/?mysecretkey".
|
||||
# [scope: vhost]
|
||||
camouflage_secret = "mysecretkey"
|
||||
|
||||
# Defines the realm (browser prompt) for HTTP authentication.
|
||||
# If no realm is set, the server will return 404 Not found error instead of 401 Unauthorized.
|
||||
# Better change it from the default value to avoid fingerprinting.
|
||||
# [scope: vhost]
|
||||
camouflage_realm = "Restricted Content"
|
||||
|
||||
#Advanced options
|
||||
@@ -778,6 +895,7 @@ camouflage_realm = "Restricted Content"
|
||||
# this may help others, please send your settings and reason to
|
||||
# the openconnect mailing list. The special keywords '%{U}'
|
||||
# and '%{G}', if present will be replaced by the username and group name.
|
||||
# [scope: vhost]
|
||||
#custom-header = "X-My-Header: hi there"
|
||||
|
||||
|
||||
@@ -802,6 +920,7 @@ ipv4-netmask = 255.255.255.0
|
||||
cert-user-oid = 0.9.2342.19200300.100.1.1
|
||||
|
||||
# HTTP headers
|
||||
# [scope: vhost]
|
||||
included-http-headers = Strict-Transport-Security: max-age=31536000 ; includeSubDomains
|
||||
included-http-headers = X-Frame-Options: deny
|
||||
included-http-headers = X-Content-Type-Options: nosniff
|
||||
|
||||
+9
-6
@@ -727,7 +727,8 @@ struct ini_ctx_st {
|
||||
static bool error_on_vhost(const char *vname, const char *oname)
|
||||
{
|
||||
if (vname) {
|
||||
fprintf(stderr, ERRSTR
|
||||
fprintf(stderr,
|
||||
ERRSTR
|
||||
"'%s' cannot be set inside a virtual host section\n",
|
||||
oname);
|
||||
return true;
|
||||
@@ -926,11 +927,13 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name,
|
||||
} else if (strcmp(name, "socket-file") == 0) {
|
||||
if (error_on_vhost(vhost->name, "socket-file"))
|
||||
return 0;
|
||||
PREAD_STRING(pool, vhost->perm_config.socket_file_prefix);
|
||||
PREAD_STRING(pool,
|
||||
vhost->perm_config.socket_file_prefix);
|
||||
} else if (strcmp(name, "occtl-socket-file") == 0) {
|
||||
if (error_on_vhost(vhost->name, "occtl-socket-file"))
|
||||
return 0;
|
||||
PREAD_STRING(pool, vhost->perm_config.occtl_socket_file);
|
||||
PREAD_STRING(pool,
|
||||
vhost->perm_config.occtl_socket_file);
|
||||
} else if (strcmp(name, "chroot-dir") == 0) {
|
||||
if (error_on_vhost(vhost->name, "chroot-dir"))
|
||||
return 0;
|
||||
@@ -938,7 +941,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name,
|
||||
} else if (strcmp(name, "server-stats-reset-time") == 0) {
|
||||
/* cannot be modified as it would require sec-mod to
|
||||
* re-read configuration too */
|
||||
if (error_on_vhost(vhost->name, "server-stats-reset-time"))
|
||||
if (error_on_vhost(vhost->name,
|
||||
"server-stats-reset-time"))
|
||||
return 0;
|
||||
READ_NUMERIC(vhost->perm_config.stats_reset_time);
|
||||
} else if (strcmp(name, "pid-file") == 0) {
|
||||
@@ -1061,8 +1065,7 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name,
|
||||
#if defined(OCSERV_WORKER_PROCESS)
|
||||
if (switch_comp_priority(pool, value) == 0) {
|
||||
fprintf(stderr,
|
||||
WARNSTR
|
||||
"invalid compression modstring %s\n",
|
||||
WARNSTR "invalid compression modstring %s\n",
|
||||
value);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -113,7 +113,7 @@ static int group_cfg_ini_handler(void *_ctx, const char *section,
|
||||
} else if (strcmp(name, "restrict-user-to-routes") == 0) {
|
||||
READ_TF(msg->config->restrict_user_to_routes,
|
||||
msg->config->has_restrict_user_to_routes);
|
||||
} else if (strcmp(name, "tunnel_all_dns") == 0) {
|
||||
} else if (strcmp(name, "tunnel-all-dns") == 0) {
|
||||
READ_TF(msg->config->tunnel_all_dns,
|
||||
msg->config->has_tunnel_all_dns);
|
||||
} else if (strcmp(name, "deny-roaming") == 0) {
|
||||
|
||||
@@ -213,212 +213,231 @@ typedef struct kkdcp_st {
|
||||
unsigned int realms_size;
|
||||
} kkdcp_st;
|
||||
|
||||
/*
|
||||
* Scope tags for config options:
|
||||
* [scope: global] -- reloadable; error if set in a [vhost:X] section
|
||||
* [scope: vhost] -- reloadable; can differ per virtual host
|
||||
* [scope: vhost user] -- reloadable; settable per-vhost and overridable per-user/group
|
||||
* Options in perm_cfg_st are permanent (require restart); see tags there.
|
||||
*/
|
||||
struct cfg_st {
|
||||
unsigned int is_dyndns;
|
||||
unsigned int listen_proxy_proto;
|
||||
unsigned int stats_report_time;
|
||||
unsigned int is_dyndns; /* [scope: vhost] */
|
||||
unsigned int listen_proxy_proto; /* [scope: global] */
|
||||
unsigned int stats_report_time; /* [scope: vhost user] */
|
||||
|
||||
kkdcp_st *kkdcp;
|
||||
unsigned int kkdcp_size;
|
||||
kkdcp_st *kkdcp; /* [scope: vhost] */
|
||||
unsigned int kkdcp_size; /* [scope: vhost] */
|
||||
|
||||
char *cert_user_oid; /* The OID that will be used to extract the username */
|
||||
char *cert_group_oid; /* The OID that will be used to extract the groupname */
|
||||
char *cert_user_oid; /* [scope: vhost] The OID that will be used to extract the username */
|
||||
char *cert_group_oid; /* [scope: vhost] The OID that will be used to extract the groupname */
|
||||
|
||||
gnutls_certificate_request_t cert_req;
|
||||
char *priorities;
|
||||
gnutls_certificate_request_t cert_req; /* [scope: vhost] */
|
||||
char *priorities; /* [scope: vhost] */
|
||||
#ifdef ENABLE_COMPRESSION
|
||||
unsigned int enable_compression;
|
||||
unsigned int enable_compression; /* [scope: vhost] */
|
||||
unsigned int
|
||||
no_compress_limit; /* under this size (in bytes) of data there will be no compression */
|
||||
no_compress_limit; /* [scope: vhost] under this size (in bytes) of data there will be no compression */
|
||||
#endif
|
||||
char *banner;
|
||||
char *pre_login_banner;
|
||||
char *ocsp_response; /* file with the OCSP response */
|
||||
char *default_domain; /* domain to be advertised */
|
||||
char *banner; /* [scope: vhost] */
|
||||
char *pre_login_banner; /* [scope: vhost] */
|
||||
char *ocsp_response; /* [scope: vhost] file with the OCSP response */
|
||||
char *default_domain; /* [scope: vhost] domain to be advertised */
|
||||
|
||||
char **group_list; /* select_group */
|
||||
unsigned int group_list_size;
|
||||
char **group_list; /* [scope: vhost] select_group */
|
||||
unsigned int group_list_size; /* [scope: vhost] */
|
||||
|
||||
char **friendly_group_list; /* the same size as group_list_size */
|
||||
char **friendly_group_list; /* [scope: vhost] the same size as group_list_size */
|
||||
|
||||
unsigned int select_group_by_url;
|
||||
unsigned int auto_select_group;
|
||||
char *default_select_group;
|
||||
unsigned int select_group_by_url; /* [scope: vhost] */
|
||||
unsigned int auto_select_group; /* [scope: vhost] */
|
||||
char *default_select_group; /* [scope: vhost] */
|
||||
|
||||
char **custom_header;
|
||||
size_t custom_header_size;
|
||||
char **custom_header; /* [scope: vhost] */
|
||||
size_t custom_header_size; /* [scope: vhost] */
|
||||
|
||||
char **split_dns;
|
||||
size_t split_dns_size;
|
||||
char **split_dns; /* [scope: vhost user] */
|
||||
size_t split_dns_size; /* [scope: vhost user] */
|
||||
|
||||
/* http headers to include */
|
||||
char **included_http_headers;
|
||||
size_t included_http_headers_size;
|
||||
char **included_http_headers; /* [scope: vhost] */
|
||||
size_t included_http_headers_size; /* [scope: vhost] */
|
||||
|
||||
unsigned int
|
||||
append_routes; /* whether to append global routes to per-user config */
|
||||
append_routes; /* [scope: vhost] whether to append global routes to per-user config */
|
||||
unsigned int
|
||||
restrict_user_to_routes; /* whether the firewall script will be run for the user */
|
||||
restrict_user_to_routes; /* [scope: vhost user] whether the firewall script will be run for the user */
|
||||
unsigned int
|
||||
deny_roaming; /* whether a cookie is restricted to a single IP */
|
||||
time_t cookie_timeout; /* in seconds */
|
||||
time_t session_timeout; /* in seconds */
|
||||
deny_roaming; /* [scope: vhost user] whether a cookie is restricted to a single IP */
|
||||
time_t cookie_timeout; /* [scope: vhost] in seconds */
|
||||
time_t session_timeout; /* [scope: vhost user] in seconds */
|
||||
unsigned int
|
||||
persistent_cookies; /* whether cookies stay valid after disconnect */
|
||||
persistent_cookies; /* [scope: vhost] whether cookies stay valid after disconnect */
|
||||
|
||||
time_t rekey_time; /* in seconds */
|
||||
unsigned int rekey_method; /* REKEY_METHOD_ */
|
||||
time_t rekey_time; /* [scope: vhost] in seconds */
|
||||
unsigned int rekey_method; /* [scope: vhost] REKEY_METHOD_ */
|
||||
|
||||
time_t ban_time; /* duration IP remains banned after hitting max_ban_score -> in seconds */
|
||||
time_t ban_time; /* [scope: global] duration IP remains banned after hitting max_ban_score -> in seconds */
|
||||
unsigned int
|
||||
max_ban_score; /* the score allowed before a user is banned (see vpn.h) */
|
||||
int ban_reset_time;
|
||||
max_ban_score; /* [scope: global] the score allowed before a user is banned (see vpn.h) */
|
||||
int ban_reset_time; /* [scope: global] */
|
||||
|
||||
unsigned int ban_points_wrong_password;
|
||||
unsigned int ban_points_connect;
|
||||
unsigned int ban_points_kkdcp;
|
||||
unsigned int ban_points_wrong_password; /* [scope: global] */
|
||||
unsigned int ban_points_connect; /* [scope: global] */
|
||||
unsigned int ban_points_kkdcp; /* [scope: global] */
|
||||
|
||||
/* when using the new PSK DTLS negotiation make sure that
|
||||
* the negotiated DTLS cipher/mac matches the TLS cipher/mac. */
|
||||
unsigned int match_dtls_and_tls;
|
||||
unsigned int dtls_psk; /* whether to enable DTLS-PSK */
|
||||
unsigned int dtls_legacy; /* whether to enable DTLS-LEGACY */
|
||||
|
||||
unsigned int isolate; /* whether seccomp should be enabled or not */
|
||||
|
||||
unsigned int auth_timeout; /* timeout of HTTP auth */
|
||||
unsigned int idle_timeout; /* timeout when idle */
|
||||
unsigned int mobile_idle_timeout; /* timeout when a mobile is idle */
|
||||
unsigned int match_dtls_and_tls; /* [scope: vhost] */
|
||||
unsigned int dtls_psk; /* [scope: global] whether to enable DTLS-PSK */
|
||||
unsigned int
|
||||
switch_to_tcp_timeout; /* length of no traffic period to automatically switch to TCP */
|
||||
unsigned int keepalive;
|
||||
unsigned int dpd;
|
||||
unsigned int mobile_dpd;
|
||||
unsigned int max_clients;
|
||||
unsigned int max_same_clients;
|
||||
unsigned int use_utmp;
|
||||
unsigned int tunnel_all_dns;
|
||||
unsigned int
|
||||
use_occtl; /* whether support for the occtl tool will be enabled */
|
||||
dtls_legacy; /* [scope: vhost] whether to enable DTLS-LEGACY */
|
||||
|
||||
unsigned int try_mtu; /* MTU discovery enabled */
|
||||
unsigned int cisco_client_compat; /* do not require client certificate,
|
||||
unsigned int
|
||||
isolate; /* [scope: global] whether seccomp should be enabled or not */
|
||||
|
||||
unsigned int auth_timeout; /* [scope: global] timeout of HTTP auth */
|
||||
unsigned int idle_timeout; /* [scope: vhost user] timeout when idle */
|
||||
unsigned int
|
||||
mobile_idle_timeout; /* [scope: vhost user] timeout when a mobile is idle */
|
||||
unsigned int
|
||||
switch_to_tcp_timeout; /* [scope: vhost] length of no traffic period to automatically switch to TCP */
|
||||
unsigned int keepalive; /* [scope: vhost user] */
|
||||
unsigned int dpd; /* [scope: vhost user] */
|
||||
unsigned int mobile_dpd; /* [scope: vhost user] */
|
||||
unsigned int max_clients; /* [scope: global] */
|
||||
unsigned int max_same_clients; /* [scope: vhost user] */
|
||||
unsigned int use_utmp; /* [scope: global] */
|
||||
unsigned int tunnel_all_dns; /* [scope: vhost user] */
|
||||
unsigned int
|
||||
use_occtl; /* [scope: global] whether support for the occtl tool will be enabled */
|
||||
|
||||
unsigned int try_mtu; /* [scope: global] MTU discovery enabled */
|
||||
unsigned int
|
||||
cisco_client_compat; /* [scope: vhost] do not require client certificate,
|
||||
* and allow auth to complete in different
|
||||
* TCP sessions. */
|
||||
unsigned int
|
||||
cisco_svc_client_compat; /* force allowed ciphers and disable dtls-legacy */
|
||||
cisco_svc_client_compat; /* [scope: vhost] force allowed ciphers and disable dtls-legacy */
|
||||
unsigned int
|
||||
rate_limit_ms; /* if non zero force a connection every rate_limit milliseconds if ocserv-sm is heavily loaded */
|
||||
rate_limit_ms; /* [scope: global] if non zero force a connection every rate_limit milliseconds if ocserv-sm is heavily loaded */
|
||||
unsigned int
|
||||
ping_leases; /* non zero if we need to ping prior to leasing */
|
||||
ping_leases; /* [scope: global] non zero if we need to ping prior to leasing */
|
||||
unsigned int
|
||||
server_drain_ms; /* how long to wait after we stop accepting new connections before closing old connections */
|
||||
server_drain_ms; /* [scope: global] how long to wait after we stop accepting new connections before closing old connections */
|
||||
|
||||
size_t rx_per_sec;
|
||||
size_t tx_per_sec;
|
||||
unsigned int net_priority;
|
||||
size_t rx_per_sec; /* [scope: vhost user] */
|
||||
size_t tx_per_sec; /* [scope: vhost user] */
|
||||
unsigned int net_priority; /* [scope: vhost user] */
|
||||
|
||||
char *crl;
|
||||
char *crl; /* [scope: vhost] */
|
||||
|
||||
unsigned int output_buffer;
|
||||
unsigned int default_mtu;
|
||||
unsigned int predictable_ips; /* boolean */
|
||||
unsigned int output_buffer; /* [scope: vhost] */
|
||||
unsigned int default_mtu; /* [scope: vhost user] */
|
||||
unsigned int predictable_ips; /* [scope: vhost] boolean */
|
||||
|
||||
char *route_add_cmd;
|
||||
char *route_del_cmd;
|
||||
char *route_add_cmd; /* [scope: global] */
|
||||
char *route_del_cmd; /* [scope: global] */
|
||||
|
||||
char *connect_script;
|
||||
char *host_update_script;
|
||||
char *disconnect_script;
|
||||
char *connect_script; /* [scope: global] */
|
||||
char *host_update_script; /* [scope: global] */
|
||||
char *disconnect_script; /* [scope: global] */
|
||||
|
||||
char *cgroup;
|
||||
char *proxy_url;
|
||||
char *cgroup; /* [scope: vhost user] */
|
||||
char *proxy_url; /* [scope: vhost] */
|
||||
|
||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||
char *xml_config_file;
|
||||
char *xml_config_hash;
|
||||
char *xml_config_file; /* [scope: vhost user] */
|
||||
char *xml_config_hash; /* [scope: vhost user] */
|
||||
#endif
|
||||
|
||||
unsigned int client_bypass_protocol;
|
||||
unsigned int client_bypass_protocol; /* [scope: vhost user] */
|
||||
|
||||
/* additional configuration files */
|
||||
char *per_group_dir;
|
||||
char *per_user_dir;
|
||||
char *default_group_conf;
|
||||
char *default_user_conf;
|
||||
char *per_group_dir; /* [scope: vhost] */
|
||||
char *per_user_dir; /* [scope: vhost] */
|
||||
char *default_group_conf; /* [scope: vhost] */
|
||||
char *default_user_conf; /* [scope: vhost] */
|
||||
|
||||
bool gssapi_no_local_user_map;
|
||||
bool gssapi_no_local_user_map; /* [scope: vhost] */
|
||||
|
||||
/* known iroutes - only sent to the users who are not registering them
|
||||
*/
|
||||
char **known_iroutes;
|
||||
size_t known_iroutes_size;
|
||||
/* known iroutes - only sent to the users who are not registering them */
|
||||
char **known_iroutes; /* [scope: vhost] */
|
||||
size_t known_iroutes_size; /* [scope: vhost] */
|
||||
|
||||
FwPortSt **fw_ports;
|
||||
size_t n_fw_ports;
|
||||
FwPortSt **fw_ports; /* [scope: vhost user] */
|
||||
size_t n_fw_ports; /* [scope: vhost user] */
|
||||
|
||||
/* the tun network */
|
||||
struct vpn_st network;
|
||||
struct vpn_st
|
||||
network; /* [scope: vhost user] dns/routes/network sub-fields */
|
||||
|
||||
/* holds a usage count of holders of pointers in this struct */
|
||||
int *usage_count;
|
||||
int *usage_count; /* [scope: vhost] */
|
||||
|
||||
bool camouflage;
|
||||
char *camouflage_secret;
|
||||
char *camouflage_realm;
|
||||
bool camouflage; /* [scope: vhost] */
|
||||
char *camouflage_secret; /* [scope: vhost] */
|
||||
char *camouflage_realm; /* [scope: vhost] */
|
||||
};
|
||||
|
||||
/*
|
||||
* Permanent config (perm_cfg_st): requires server restart to change.
|
||||
* Scope tags:
|
||||
* [scope: global (non-reloadable)] -- cannot differ per virtual host
|
||||
* [scope: vhost (non-reloadable)] -- can differ per virtual host
|
||||
*/
|
||||
struct perm_cfg_st {
|
||||
/* gets reloaded */
|
||||
struct cfg_st *config;
|
||||
|
||||
/* stuff here don't change on reload */
|
||||
auth_struct_st auth[MAX_AUTH_METHODS];
|
||||
unsigned int auth_methods;
|
||||
acct_struct_st acct;
|
||||
unsigned int sup_config_type; /* one of SUP_CONFIG_ */
|
||||
auth_struct_st
|
||||
auth[MAX_AUTH_METHODS]; /* [scope: vhost (non-reloadable)] */
|
||||
unsigned int auth_methods; /* [scope: vhost (non-reloadable)] */
|
||||
acct_struct_st acct; /* [scope: vhost (non-reloadable)] */
|
||||
unsigned int
|
||||
sup_config_type; /* [scope: vhost (non-reloadable)] one of SUP_CONFIG_ */
|
||||
|
||||
char *chroot_dir; /* where the xml files are served from */
|
||||
char *occtl_socket_file;
|
||||
char *socket_file_prefix;
|
||||
char *chroot_dir; /* [scope: global (non-reloadable)] where the xml files are served from */
|
||||
char *occtl_socket_file; /* [scope: global (non-reloadable)] */
|
||||
char *socket_file_prefix; /* [scope: global (non-reloadable)] */
|
||||
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
uid_t uid; /* [scope: global (non-reloadable)] */
|
||||
gid_t gid; /* [scope: global (non-reloadable)] */
|
||||
|
||||
char *key_pin;
|
||||
char *srk_pin;
|
||||
char *key_pin; /* [scope: vhost (non-reloadable)] */
|
||||
char *srk_pin; /* [scope: vhost (non-reloadable)] */
|
||||
|
||||
char *pin_file;
|
||||
char *srk_pin_file;
|
||||
char **cert;
|
||||
size_t cert_size;
|
||||
char **key;
|
||||
size_t key_size;
|
||||
char *pin_file; /* [scope: vhost (non-reloadable)] */
|
||||
char *srk_pin_file; /* [scope: vhost (non-reloadable)] */
|
||||
char **cert; /* [scope: vhost (non-reloadable)] */
|
||||
size_t cert_size; /* [scope: vhost (non-reloadable)] */
|
||||
char **key; /* [scope: vhost (non-reloadable)] */
|
||||
size_t key_size; /* [scope: vhost (non-reloadable)] */
|
||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||
char *cert_hash;
|
||||
char *cert_hash; /* [scope: vhost (non-reloadable)] */
|
||||
#endif
|
||||
unsigned int stats_reset_time;
|
||||
unsigned int foreground;
|
||||
unsigned int no_chdir;
|
||||
unsigned int log_level;
|
||||
unsigned int log_stderr;
|
||||
unsigned int syslog;
|
||||
unsigned int stats_reset_time; /* [scope: global (non-reloadable)] */
|
||||
unsigned int foreground; /* [scope: global (non-reloadable)] */
|
||||
unsigned int no_chdir; /* [scope: global (non-reloadable)] */
|
||||
unsigned int log_level; /* [scope: global (non-reloadable)] */
|
||||
unsigned int log_stderr; /* [scope: global (non-reloadable)] */
|
||||
unsigned int syslog; /* [scope: global (non-reloadable)] */
|
||||
|
||||
unsigned int pr_dumpable;
|
||||
unsigned int pr_dumpable; /* [scope: global (non-reloadable)] */
|
||||
|
||||
char *ca;
|
||||
char *dh_params_file;
|
||||
char *ca; /* [scope: vhost (non-reloadable)] */
|
||||
char *dh_params_file; /* [scope: vhost (non-reloadable)] */
|
||||
|
||||
char *listen_host;
|
||||
char *udp_listen_host;
|
||||
char *listen_netns_name;
|
||||
unsigned int port;
|
||||
unsigned int udp_port;
|
||||
char *listen_host; /* [scope: global (non-reloadable)] */
|
||||
char *udp_listen_host; /* [scope: global (non-reloadable)] */
|
||||
char *listen_netns_name; /* [scope: global (non-reloadable)] */
|
||||
unsigned int port; /* [scope: global (non-reloadable)] */
|
||||
unsigned int udp_port; /* [scope: global (non-reloadable)] */
|
||||
|
||||
unsigned int sec_mod_scale;
|
||||
unsigned int sec_mod_scale; /* [scope: global (non-reloadable)] */
|
||||
|
||||
/* for testing ocserv only */
|
||||
unsigned int debug_no_secmod_stats;
|
||||
unsigned int debug_no_secmod_stats; /* [scope: global (non-reloadable)] */
|
||||
|
||||
/* attic, where old config allocated values are stored */
|
||||
struct list_head attic;
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Validate that [scope:] annotations in doc/sample.config are consistent
|
||||
with the actual config parsing code in src/config.c and src/sup-config/file.c.
|
||||
|
||||
Scope vocabulary:
|
||||
global (non-reloadable) -- perm_cfg_st; requires restart; cannot differ per vhost
|
||||
vhost (non-reloadable) -- perm_cfg_st; requires restart; can differ per vhost
|
||||
global -- cfg_st; reloadable; cannot be set in [vhost:] sections
|
||||
vhost -- cfg_st; reloadable; can differ per vhost
|
||||
vhost user -- cfg_st; reloadable; per-vhost AND per-user/group overridable
|
||||
|
||||
Checks performed:
|
||||
(a) Every option in sample.config has a [scope: ...] annotation.
|
||||
(b) Every option annotated [... global] has an error_on_vhost() call in
|
||||
config.c (or is a permanent-global option in perm_cfg_st, which never
|
||||
reaches error_on_vhost).
|
||||
(c) Every error_on_vhost() option in config.c is annotated [... global] in
|
||||
sample.config.
|
||||
(d) Every option annotated [...user...] is handled in src/sup-config/file.c.
|
||||
(e) Every option in src/sup-config/file.c is annotated [...user...] in
|
||||
sample.config.
|
||||
(f) Every option in sample.config annotated [...user...] is also annotated
|
||||
[...vhost...] (user-overridable options must also be settable per-vhost).
|
||||
(g) Every field in struct cfg_st and struct perm_cfg_st in src/vpn.h has a
|
||||
[scope: ...] inline comment.
|
||||
|
||||
Exit code: 0 on success, 1 if any errors are found.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.dirname(SCRIPT_DIR)
|
||||
|
||||
SAMPLE_CONFIG = os.path.join(ROOT, "doc", "sample.config")
|
||||
CONFIG_C = os.path.join(ROOT, "src", "config.c")
|
||||
SUP_CONFIG_FILE = os.path.join(ROOT, "src", "sup-config", "file.c")
|
||||
VPN_H = os.path.join(ROOT, "src", "vpn.h")
|
||||
|
||||
|
||||
def read_file(path):
|
||||
with open(path, "r") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 1: Parse doc/sample.config
|
||||
# Returns dict: {option_name: scope_string or None}
|
||||
# ---------------------------------------------------------------------------
|
||||
def extract_sample_config_scopes(text):
|
||||
"""
|
||||
For each option line (has '=' and is not a pure comment/blank),
|
||||
look backwards for the nearest '# [scope: ...]' line.
|
||||
"""
|
||||
lines = text.splitlines()
|
||||
scopes = {}
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
# Strip leading '#' and whitespace to get the raw content
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("#"):
|
||||
after_hash = stripped[1:]
|
||||
# Only treat as a potential option if 0 or 1 spaces follow '#'.
|
||||
# Two or more spaces indicate an example buried inside a prose
|
||||
# comment block (e.g. "# CN = 2.5.4.3" or "# kdc = ...").
|
||||
if after_hash.startswith(" "):
|
||||
continue
|
||||
stripped = after_hash.lstrip()
|
||||
|
||||
# Is it an option line? Must contain ' = ' or '= ' and not be empty
|
||||
if "=" not in stripped or stripped.startswith("["):
|
||||
continue
|
||||
# Skip pure comment lines (the line itself starts with '#' then a word)
|
||||
raw = line.lstrip()
|
||||
if raw == "" or raw.startswith("###"):
|
||||
continue
|
||||
# Extract option name: everything before the first '='
|
||||
eq_pos = stripped.find("=")
|
||||
opt = stripped[:eq_pos].strip()
|
||||
# Reject options that look like key=value inside a comment or contain spaces badly
|
||||
if not re.match(r'^[a-zA-Z][a-zA-Z0-9_-]*$', opt):
|
||||
continue
|
||||
|
||||
# Avoid duplicates — keep first occurrence (the active/commented-first)
|
||||
if opt in scopes:
|
||||
continue
|
||||
|
||||
# Walk backwards looking for a '# [scope: ...]' tag
|
||||
scope = None
|
||||
for j in range(i - 1, max(i - 10, -1), -1):
|
||||
prev = lines[j].strip()
|
||||
m = re.match(r'^#\s*\[scope:\s*([^\]]+)\]', prev)
|
||||
if m:
|
||||
scope = m.group(1).strip()
|
||||
break
|
||||
# Stop if we hit a blank line or a non-comment line
|
||||
if prev == "" or (prev and not prev.startswith("#")):
|
||||
break
|
||||
|
||||
scopes[opt] = scope
|
||||
|
||||
return scopes
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 2: Parse src/config.c — options that call error_on_vhost()
|
||||
# ---------------------------------------------------------------------------
|
||||
def extract_error_on_vhost_options(text):
|
||||
"""Return set of option names passed to error_on_vhost()."""
|
||||
# Pattern: error_on_vhost(vhost->name, "option-name")
|
||||
pattern = re.compile(r'error_on_vhost\s*\([^,]+,\s*"([^"]+)"')
|
||||
return set(pattern.findall(text))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 3: Parse src/sup-config/file.c — options handled per-user/group
|
||||
# ---------------------------------------------------------------------------
|
||||
def extract_sup_config_options(text):
|
||||
"""Return set of option names handled in the per-user/group file parser."""
|
||||
pattern = re.compile(r'strcmp\s*\(\s*name\s*,\s*"([^"]+)"')
|
||||
return set(pattern.findall(text))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 4: Parse src/vpn.h — check that cfg_st and perm_cfg_st fields
|
||||
# all have [scope: ...] annotations.
|
||||
# ---------------------------------------------------------------------------
|
||||
def check_vpn_h_annotations(text):
|
||||
"""
|
||||
Returns list of field declarations in cfg_st and perm_cfg_st
|
||||
that are missing a [scope: ...] inline comment.
|
||||
"""
|
||||
missing = []
|
||||
|
||||
# Find cfg_st body
|
||||
for struct_name in ("struct cfg_st", "struct perm_cfg_st"):
|
||||
m = re.search(re.escape(struct_name) + r'\s*\{', text)
|
||||
if not m:
|
||||
continue
|
||||
start = m.end()
|
||||
# Find matching closing brace
|
||||
depth = 1
|
||||
pos = start
|
||||
while pos < len(text) and depth > 0:
|
||||
if text[pos] == '{':
|
||||
depth += 1
|
||||
elif text[pos] == '}':
|
||||
depth -= 1
|
||||
pos += 1
|
||||
body = text[start:pos - 1]
|
||||
|
||||
# Find field declarations: lines ending in ';' that look like declarations
|
||||
for line in body.splitlines():
|
||||
stripped = line.strip()
|
||||
# Skip blank, comment-only, preprocessor, and struct/union lines
|
||||
if (not stripped or stripped.startswith("/*") or stripped.startswith("//")
|
||||
or stripped.startswith("#") or stripped.startswith("}")
|
||||
or stripped.startswith("{") or stripped.startswith("struct ")
|
||||
or stripped.startswith("typedef ")):
|
||||
continue
|
||||
# Must end with ';'
|
||||
if not stripped.endswith(";"):
|
||||
continue
|
||||
# Skip lines that are closing braces with semicolons e.g. '} name;'
|
||||
if stripped.startswith("}"):
|
||||
continue
|
||||
# Check for [scope: ...] annotation
|
||||
if "[scope:" not in line:
|
||||
# Extract a clean field name for reporting
|
||||
# Remove array/pointer noise
|
||||
name_m = re.search(r'\b(\w+)\s*(?:\[[^\]]*\])?\s*;', stripped)
|
||||
field = name_m.group(1) if name_m else stripped[:40]
|
||||
missing.append(f" {struct_name}: field '{field}' has no [scope:] annotation")
|
||||
|
||||
return missing
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main validation
|
||||
# ---------------------------------------------------------------------------
|
||||
def main():
|
||||
errors = []
|
||||
|
||||
sample_text = read_file(SAMPLE_CONFIG)
|
||||
config_c_text = read_file(CONFIG_C)
|
||||
sup_config_text = read_file(SUP_CONFIG_FILE)
|
||||
vpn_h_text = read_file(VPN_H)
|
||||
|
||||
# Parse
|
||||
sample_scopes = extract_sample_config_scopes(sample_text)
|
||||
global_in_code = extract_error_on_vhost_options(config_c_text)
|
||||
user_in_code = extract_sup_config_options(sup_config_text)
|
||||
|
||||
# Non-reloadable options live in perm_cfg_st and never go through
|
||||
# error_on_vhost(); they are enforced structurally. Collect from sample.config.
|
||||
perm_global_opts = {
|
||||
opt for opt, scope in sample_scopes.items()
|
||||
if scope and "non-reloadable" in scope
|
||||
}
|
||||
|
||||
# (a) All options in sample.config have a scope annotation
|
||||
for opt, scope in sorted(sample_scopes.items()):
|
||||
if scope is None:
|
||||
errors.append(
|
||||
f"sample.config: '{opt}' has no [scope:] annotation"
|
||||
)
|
||||
|
||||
# (b) [global] options (reloadable only) must have error_on_vhost in config.c;
|
||||
# [global (non-reloadable)] options live in perm_cfg_st and are exempt.
|
||||
for opt, scope in sorted(sample_scopes.items()):
|
||||
if scope and "global" in scope and "non-reloadable" not in scope:
|
||||
if opt not in global_in_code:
|
||||
errors.append(
|
||||
f"sample.config: '{opt}' is annotated [{scope}] but "
|
||||
f"has no error_on_vhost() call in config.c"
|
||||
)
|
||||
|
||||
# (c) Every error_on_vhost option must be annotated [... global] in sample.config
|
||||
for opt in sorted(global_in_code):
|
||||
scope = sample_scopes.get(opt)
|
||||
if scope is None:
|
||||
errors.append(
|
||||
f"config.c: error_on_vhost('{opt}') but option is missing "
|
||||
f"from sample.config annotations"
|
||||
)
|
||||
elif "global" not in scope:
|
||||
errors.append(
|
||||
f"config.c: error_on_vhost('{opt}') but sample.config "
|
||||
f"annotates it [{scope}] (expected '... global')"
|
||||
)
|
||||
|
||||
# (d) [...user...] options must be in sup-config/file.c
|
||||
for opt, scope in sorted(sample_scopes.items()):
|
||||
if scope and "user" in scope:
|
||||
if opt not in user_in_code:
|
||||
errors.append(
|
||||
f"sample.config: '{opt}' is annotated [{scope}] but is not "
|
||||
f"handled in src/sup-config/file.c"
|
||||
)
|
||||
|
||||
# (e) Every option in sup-config/file.c must be annotated [...user...] in sample.config
|
||||
# Some options (like 'iroute', 'hostname', 'explicit-ipv4', 'explicit-ipv6')
|
||||
# are per-user only and don't appear in the global sample.config — skip those.
|
||||
PER_USER_ONLY = {
|
||||
"iroute", "hostname", "explicit-ipv4", "explicit-ipv6",
|
||||
"no-udp",
|
||||
"ipv4-dns", "ipv6-dns", "ipv4-nbns", "ipv6-nbns",
|
||||
}
|
||||
for opt in sorted(user_in_code):
|
||||
if opt in PER_USER_ONLY:
|
||||
continue
|
||||
scope = sample_scopes.get(opt)
|
||||
if scope is None:
|
||||
errors.append(
|
||||
f"sup-config/file.c: handles '{opt}' but option is missing "
|
||||
f"from sample.config annotations"
|
||||
)
|
||||
elif "user" not in scope:
|
||||
errors.append(
|
||||
f"sup-config/file.c: handles '{opt}' but sample.config "
|
||||
f"annotates it [{scope}] (expected '... user ...')"
|
||||
)
|
||||
|
||||
# (f) Every [...user...] option in sample.config must also be [...vhost...]
|
||||
for opt, scope in sorted(sample_scopes.items()):
|
||||
if scope and "user" in scope and "vhost" not in scope:
|
||||
errors.append(
|
||||
f"sample.config: '{opt}' is annotated [{scope}] but "
|
||||
f"user-overridable options must also be settable per-vhost "
|
||||
f"(expected '[scope: vhost user]')"
|
||||
)
|
||||
|
||||
# (g) vpn.h struct field annotations
|
||||
missing_annot = check_vpn_h_annotations(vpn_h_text)
|
||||
if missing_annot:
|
||||
errors.append(
|
||||
"src/vpn.h: the following fields are missing [scope:] annotations:"
|
||||
)
|
||||
errors.extend(missing_annot)
|
||||
|
||||
# Report
|
||||
if errors:
|
||||
print("config-scope-check: FAILED")
|
||||
for e in errors:
|
||||
print(f" ERROR: {e}")
|
||||
return 1
|
||||
|
||||
print(f"config-scope-check: OK ({len(sample_scopes)} options checked)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user