-
-
Notifications
You must be signed in to change notification settings - Fork 55
Upgrade V2
Version 2 includes configuration changes for username resolution, client-specific configuration, token validation, and security hardening.
Review the rest of this page even if these hardening items do not apply to your deployment. This section only requires action when you use reverse proxy headers or the OpenVPN plugin.
If http.enable-proxy-headers is enabled, http.trusted-proxies is now
required. openvpn-auth-oauth2 will reject the configuration if proxy headers are
enabled without at least one trusted proxy CIDR.
Use the CIDR ranges of the reverse proxies that connect directly to openvpn-auth-oauth2:
http:
enable-proxy-headers: true
trusted-proxies:
- 127.0.0.1/32
- 10.0.0.0/24Environment variable configuration:
CONFIG_HTTP_ENABLE__PROXY__HEADERS=true
CONFIG_HTTP_TRUSTED__PROXIES=127.0.0.1/32,10.0.0.0/24If openvpn-auth-oauth2 is not behind a reverse proxy, keep
http.enable-proxy-headers disabled.
The OpenVPN plugin is no longer experimental.
The plugin management socket now requires password authentication. Existing
OpenVPN plugin configurations that pass only the listen socket must add a
password file argument, and openvpn-auth-oauth2 must use the same password for
openvpn.password.
Before:
plugin /path/to/openvpn-auth-oauth2.so "unix:///var/run/openvpn-oauth2.sock"
After:
plugin /path/to/openvpn-auth-oauth2.so "unix:///var/run/openvpn-oauth2.sock" "/etc/openvpn/oauth2-plugin-password.txt"
openvpn-auth-oauth2 configuration:
openvpn:
addr: unix:///var/run/openvpn-oauth2.sock
password: "file:///etc/openvpn-auth-oauth2/oauth2-plugin-password.txt"The two password files must contain the same password. They can be separate files so OpenVPN and openvpn-auth-oauth2 can each read a file from the path allowed by their service permissions.
The following options were removed or renamed:
| Version 1 option | Version 2 option |
|---|---|
oauth2.openvpn-username-claim |
oauth2.openvpn-username |
oauth2.openvpn-username-cel |
oauth2.openvpn-username |
oauth2.openvpn-username is a CEL expression and must evaluate to a string.
The default changed from the claim name preferred_username to the equivalent CEL expression oauth2TokenClaims.preferred_username.
If you used oauth2.openvpn-username-claim, convert the claim name into a CEL token claim lookup:
# Version 1
oauth2:
openvpn-username-claim: email
# Version 2
oauth2:
openvpn-username: oauth2TokenClaims.emailIf you used oauth2.openvpn-username-cel, keep the same expression and move it to oauth2.openvpn-username:
# Version 1
oauth2:
openvpn-username-cel: 'oauth2TokenClaims.email.split("@")[0]'
# Version 2
oauth2:
openvpn-username: 'oauth2TokenClaims.email.split("@")[0]'The environment variable for the new option is CONFIG_OAUTH2_OPENVPN__USERNAME.
Version 2 changes OpenVPN client-specific configuration from a single config name lookup to an expression-based resolver.
The following options are removed:
| Version 1 option | Version 2 replacement |
|---|---|
openvpn.client-config.token-claim |
openvpn.client-config.expression |
openvpn.client-config.user-selector.enabled |
openvpn.client-config.strategy: user-selector |
openvpn.client-config.user-selector.static-values |
openvpn.client-config.expression |
openvpn.client-config.expression is a CEL expression and must evaluate
to an ordered list of strings. It receives oauth2TokenClaims, which contains
the OAuth2 ID token claims, and openVPNUserCommonName, which contains the
OpenVPN common name. It also receives username, which contains the resolved
OpenVPN username after oauth2.openvpn-username has been evaluated.
The default strategy is now merge. It loads every resolved config file,
deduplicates repeated config names and identical config lines, and skips missing
config files. Keep authorization in oauth2.validate.groups or
oauth2.validate.expression; client config files only assign OpenVPN settings
such as routes.
When openvpn.client-config.expression returns an empty list, version 2 loads
DEFAULT.conf. This follows OpenVPN's client-config-dir default-file pattern.
When the expression returns one or more config names, missing <name>.conf files
are ignored by default. Set openvpn.client-config.ignore-not-found: false to
deny the client when a returned config file does not exist.
Version 1 could use the OpenVPN common name as the implicit client config name:
# Version 1
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-configIn version 2, configure the resolver explicitly:
# Version 2
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
expression: |
[openVPNUserCommonName]To use the resolved OpenVPN username instead, use username:
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
expression: |
[username]Version 1 could read a config name from one token claim:
# Version 1
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
token-claim: groupsIn version 2, read the claim through client-config.expression:
# Version 2
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
expression: |
oauth2TokenClaims.groupsIf the claim can contain several values, all matching config files are merged by default.
Version 1 enabled the selector with user-selector.enabled:
# Version 1
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
token-claim: groups
user-selector:
enabled: trueIn version 2, use strategy: user-selector:
# Version 2
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
strategy: user-selector
expression: |
oauth2TokenClaims.groupsStatic selector values also move into the expression:
# Version 1
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
user-selector:
enabled: true
static-values:
- corporate
- guest# Version 2
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
strategy: user-selector
expression: |
["corporate", "guest"]To assign several config files without showing the selector, keep the default
merge strategy and return every allowed config name:
oauth2:
validate:
groups:
- GRP-VPN
openvpn:
client-config:
enabled: true
path: /etc/openvpn-auth-oauth2/client-config
expression: |
oauth2TokenClaims.groups.filter(g, g in [
"GRP-VPN",
"GRP-ADMIN",
"GRP-NETWORK"
]) +
[username]Version 2 removes the following dedicated validation options:
| Version 1 option | Version 2 replacement |
|---|---|
oauth2.validate.acr |
oauth2.validate.expression |
oauth2.validate.common-name |
oauth2.validate.expression |
oauth2.validate.common-name-case-sensitive |
oauth2.validate.expression |
oauth2.validate.ipaddr |
oauth2.validate.expression |
oauth2.validate.issuer |
Removed |
oauth2.validate.roles |
oauth2.validate.expression |
oauth2.validate.groups stays available and does not need to be migrated.
Remove oauth2.validate.issuer and CONFIG_OAUTH2_VALIDATE_ISSUER from your
configuration. The setting did not disable issuer checks; issuer validation is
always enforced for OIDC discovery and ID token verification.
Version 1 compared the OpenVPN common name with a configured ID token claim. By default, the comparison was case-insensitive.
# Version 1
oauth2:
validate:
common-name: preferred_usernameUse a CEL expression with lowerAscii() for the same case-insensitive behavior:
# Version 2
oauth2:
validate:
expression: |
has(oauth2TokenClaims.preferred_username) &&
openVPNUserCommonName.lowerAscii() == string(oauth2TokenClaims.preferred_username).lowerAscii()If you used case-sensitive common name validation:
# Version 1
oauth2:
validate:
common-name: preferred_username
common-name-case-sensitive: trueUse a direct CEL comparison:
# Version 2
oauth2:
validate:
expression: |
has(oauth2TokenClaims.preferred_username) &&
openVPNUserCommonName == string(oauth2TokenClaims.preferred_username)Version 1 compared the OpenVPN client IP address with the ipaddr ID token claim:
# Version 1
oauth2:
validate:
ipaddr: trueVersion 2 exposes the OpenVPN client IP as openVPNUserIPAddr and the token IP address claim as oauth2TokenIPAddr:
# Version 2
oauth2:
validate:
expression: 'openVPNUserIPAddr == oauth2TokenIPAddr'Version 1 allowed access if at least one configured role was present in the token roles:
# Version 1
oauth2:
validate:
roles:
- admin
- vpn-userUse CEL to check the roles claim directly:
# Version 2
oauth2:
validate:
expression: |
has(oauth2TokenClaims.roles) &&
('admin' in oauth2TokenClaims.roles || 'vpn-user' in oauth2TokenClaims.roles)For GitHub provider configurations, team validation is also migrated to CEL.
The GitHub provider still fetches teams from the /user/teams API and exposes
them through oauth2TokenClaims.roles in the same org:slug format used by
version 1:
# Version 1
oauth2:
provider: github
validate:
roles:
- my-org:vpn-users
- my-org:admins# Version 2
oauth2:
provider: github
validate:
expression: |
has(oauth2TokenClaims.roles) &&
('my-org:vpn-users' in oauth2TokenClaims.roles || 'my-org:admins' in oauth2TokenClaims.roles)Keep organization checks with oauth2.validate.groups; for GitHub this still
uses the /user/orgs API.
Version 1 used oauth2.validate.acr to require an Authentication Context Class Reference value:
# Version 1
oauth2:
validate:
acr:
- phr
- phrhUse CEL to check the acr claim:
# Version 2
oauth2:
validate:
expression: |
has(oauth2TokenClaims.acr) &&
(oauth2TokenClaims.acr == 'phr' || oauth2TokenClaims.acr == 'phrh')If you used multiple removed validation options, combine them into one CEL expression:
# Version 1
oauth2:
validate:
common-name: preferred_username
ipaddr: true
roles:
- vpn-user# Version 2
oauth2:
validate:
expression: |
has(oauth2TokenClaims.preferred_username) &&
openVPNUserCommonName.lowerAscii() == string(oauth2TokenClaims.preferred_username).lowerAscii() &&
openVPNUserIPAddr == oauth2TokenIPAddr &&
has(oauth2TokenClaims.roles) &&
'vpn-user' in oauth2TokenClaims.rolesYou can keep oauth2.validate.groups alongside CEL:
oauth2:
validate:
groups:
- vpn-users
expression: |
has(oauth2TokenClaims.acr) &&
oauth2TokenClaims.acr == 'phr'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.