-
-
Notifications
You must be signed in to change notification settings - Fork 56
Client specific configuration
openvpn-auth-oauth2 can load OpenVPN client configuration snippets from a configured directory and send the merged result through the OpenVPN management interface.
Enable the feature with --openvpn.client-config.enabled and set
--openvpn.client-config.path to the directory that contains the configuration
files.
When openvpn.client-config.enabled is false, no client configuration file is
loaded, including DEFAULT.conf.
Each resolved config name loads <name>.conf from that directory. Filenames
must satisfy Go's fs.ValidPath; absolute paths and . or .. path elements
are rejected. Config names containing <, >, ", ', &, `, or
control characters are rejected because names can also be rendered in the
browser-based profile selector. Symbolic links are followed only when their
targets remain inside the configured client config directory.
If the expression returns an empty list, openvpn-auth-oauth2 loads
DEFAULT.conf, matching OpenVPN's client-config-dir default-file pattern. If
a returned <name>.conf file is not found, the default behavior is to ignore the
missing file and continue. Set openvpn.client-config.ignore-not-found: false
to deny the client instead.
Client config names are resolved with
openvpn.client-config.expression. The expression is CEL and must return
an ordered string list.
The resolver receives the same normalized context as the other CEL settings. The most useful fields are:
-
user.username: the resolved OpenVPN username. -
user.groups: the normalized provider groups. -
user.roles: the normalized provider roles. -
openvpn.commonName: the OpenVPN user common name. -
token.claims: the raw OAuth2 ID token claims.
The auth, openvpn, token, and user namespaces are all available. See
CEL Language Features for the complete
contract.
Example:
oauth2:
openvpn-username: user.username
validate:
groups:
- GRP-VPN
openvpn:
override-username: true
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
expression: |
user.groups.filter(g, g in [
"GRP-VPN",
"GRP-ADMIN",
"GRP-NETWORK"
]) +
[user.username]For alice@example.edu with GRP-VPN and GRP-ADMIN, this loads:
GRP-VPN.confGRP-ADMIN.confalice@example.edu.conf
openvpn.client-config.strategy controls how resolved config names are applied.
The default is merge.
merge loads all resolved config files in resolver order.
Duplicate config names are loaded once. Duplicate config lines are sent once,
preserving the first occurrence. Missing config files are skipped when
openvpn.client-config.ignore-not-found is true, so authorization must still be
enforced with oauth2.validate.groups or oauth2.validate.expression.
This lets the expression return every valid role or group name and leave
unconfigured names without a matching .conf file.
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
strategy: merge
expression: |
(["base-vpn"] + user.roles).distinct()With this configuration, a role named admin-routes loads
admin-routes.conf. A role without a matching file is ignored while
openvpn.client-config.ignore-not-found is true.
Groups can also map to several shared config files. This is useful when different groups should receive overlapping route sets:
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
strategy: merge
expression: |
user.groups
.map(g, {
"GRP-VPN": ["base-vpn"],
"GRP-ADMIN": ["base-vpn", "admin-routes"],
"GRP-NETWORK": ["base-vpn", "network-routes"]
}[g])
.flatten()
.distinct()The map returns a list of config names for each group. flatten() converts the
list of lists into one ordered list, and distinct() removes repeated entries
such as base-vpn.
Use this pattern when every input group is expected to exist in the map. If the
token can contain unrelated groups, prefer returning the group names directly and
let openvpn.client-config.ignore-not-found skip missing files.
user-selector shows the profile selector when the resolver returns more than
one config name. If the resolver returns one config name, that config is used
directly. If the resolver returns an empty list, DEFAULT.conf is used.
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
strategy: user-selector
expression: |
["corporate", "guest"] +
user.groups.filter(g, g.startsWith("vpn-profile-"))This wiki is synced with the docs folder from the code repository! To improve the wiki, create a pull request against the code repository with the suggested changes.