Access Server Post-Auth Script/Host Checking

The Access Server supports a user-defined Python script called a
“post-auth" script that can determine user properties after
a successful authentication, or deny access altogether.

These are some example use-cases which a post-auth script can be
used to implement:

* Set a connecting user’s Access Server group based on LDAP
group membership for the user.

* Set up a dual-authentication system where initial authentication
is provided by a one-time-password, RADIUS-based token system, and
then group assignment is provided by LDAP.

* Verify that a given Access Server user only logs in using
a known client machine, by using the MAC address of the client
machine as a hardware ID.

* Verify that the client machine contains up-to-date applications
(such as virus checker and other security software) before
allowing it to connect to the VPN server.

See below for a sample post-auth script.

To enable a Python post-auth script, issue the following commands from
/usr/local/openvpn_as/scripts :

Load the script into the config DB:

./sacli [auth-options] -k auth.module.post_auth_script –value_file= ConfigPut

Restart the AS auth module:

./sacli [auth-options] start

where [auth-options] is normally “-a " and will prompt
for a non-echoed password from the console. As an alternative, if you
set “local_root_granted_admin=true" in the AS config file
(/usr/local/openvpn_as/etc/as.conf), then [auth-options] can be omitted
as long as the the sacli caller is root.

The Access Server will look for a Python “post_auth" function in the script,
and will call this function immediately after every successful authentication:

def post_auth(authcred, attributes, authret, info):

Note that while the post_auth script is ONLY called after successful
authentications, the script can veto the authentication and cause it
to fail.

The parameters passed to the post_auth script include:

authcred : a dictionary containing the following items:
1. username (string) — user name of VPN client
2. client_ip_addr (string) — real IP address of VPN client
3. client_hw_addr (string, not always available) — the MAC address
of the default gateway interface on the VPN client

attributes : a dictionary containing the following items:
1. client_info — a dictionary of strings provided by the client, including:
a. UV_ASCLI_VER — version number of connecting AS client
b. IV_PLAT — client platform (‘win’, ‘mac’, or ‘linux’)
c. UV_PLAT_REL — specific version of client platform
d. UV_APPVER_ — version number of APP_NAME installed on client
2. vpn_auth (boolean) — true if this is a VPN authentication, false
if it is another type of authentication (such as web server access)
3. reauth (boolean) — true if this is a VPN mid-session reauthentication,
false if it is an initial VPN authentication, and absent for non-VPN

authret : a dictionary containing the authentication status, and may
be modified and returned by the script.

If the script returns authret unmodified, there will be no effect on the
authentication process, i.e. authentication will proceed as if the script
was not present.

However by modifying authret, the script can effect changes in the
authentication process including:

1. Causing authentication to fail by setting ‘status’ item to FAIL.

2. When failing authentication, generating failure strings that will be
shown in the log file (‘reason’ item) od pushed to the client for
display to the end user (‘client_reason’ item).

3. Setting the properties of the client instance object on the server
including group, IP address, and other properties.

In detail, the authret dictionary contains the following items:

status (int, required) — should be set to SUCCEED or FAIL (these symbols
can be imported with the statement “from pyovpn.plugin import *")

reason (string, optional) — on auth failure, this string will be output
to the log file for diagnostic purposes

client_reason (string, optional) — on auth failure, this string will be
sent to the VPN client and will be shown to the user in an error
dialog box

proplist (dictionary, optional) — a list of user properties for the
connecting user. In most cases, only the conn_group member need be
set, since the group can define all other properties.

* conn_group — (string) designate this user as being a member of the
given group

* conn_ip — (string: IP address) dynamic IP address that should be
assigned to user — this IP address MUST exist within
a group subnet; if conn_group is not specified, AS will
try to derive the group by looking at the set of all
groups, and finding the group for which this IP address
is contained within group_subnets (only in Layer 3 mode)

* prop_superuser (boolean) — designate as AS administrator

* prop_autogenerate (boolean) — allow standard userlogin profiles

* prop_autologin (boolean) — allow autologin profiles

* prop_deny_web (boolean) — deny access to client web server and
XML/REST web services (but not VPN

* prop_lzo (boolean) — enable lzo compression

* prop_reroute_gw_override (string)
‘disable’ — disable reroute_gw for this client
‘dns_only’ — disable reroute_gw for this client, but still
route DNS
‘global’ — use global reroute_gw setting (default)

The info dictionary contains the following members, depending on the
current auth method:

info : a dictionary containing additional information that is specific to
the auth_method.

* auth_method (string) — contains the auth method (‘ldap’, ‘radius’,
‘pam’, or ‘local’) and may contain special auth methods such as
‘autologin’ (certificate-only auth).

* ldap_context (object) — this is a Python LDAP context object that
can be used to perform additional LDAP queries (see example below).
* user_dn (string) — the LDAP distinguished name of the user that is

* radius_reply (dictionary) — attributes received from the RADIUS
server as part of the successful authentication reply

Note that the client will only provide authcred[‘client_hw_addr’] and
attributes[‘client_info’] to trusted servers. Make sure that the
profile the client uses to connect to this server is trusted.

Return Value:

The post_auth function must return either authret or (authret, proplist_save)
where proplist_save is a set of key/value pairs (dictionary) that should be
saved back into the user properties DB for future use. The optional
proplist_save dictionary allows the script to save state in the user
properties DB record. This can be used for such functionality as
enforcing a “hardware lock", i.e. requiring that users only log in from
client machines having a known MAC address. This is demonstrated in the
sample post_auth script.


If a Python exception is thrown by the post_auth function, authentication
will fail, and the reason string will be set to the Python error message.


To test the post_auth script, cd to /usr/local/openvpn_as/scripts.

Use the authcli script to test authentication, after the post_auth
script has been imported into the Access Server using the sacli
commands above.

For example, we will test using the username ‘test’ on the sample
post_auth script below:

$ ./authcli -u test
API METHOD: authenticate
status : SUCCEED
reason : LDAP auth succeeded on ldap://…
user : test
proplist : {‘prop_autogenerate’: ‘true’, ‘prop_deny’: ‘false’,
‘prop_autologin’: ‘true’, ‘conn_group’: ‘default’,
‘type’: ‘user_connect’, ‘prop_superuser’: ‘false’}

Note how ‘conn_group’ is set to ‘default’ due to the actions of the

Host-checker query file

While a post-auth script can verify the existence and version numbers of
applications on the client, it is first necessary to construct a Host-
checker query file to enumerate the applications of interest so
that the client can report on their status. The host-checker query
file uses the following grammar:

# comment


PLATFORM : one of ‘win’, ‘linux’, ‘mac’, or ‘all’ (all matches all platforms)
NAME : short user-defined symbolic name for the application (can contain
alpha-numeric and ‘_’)
REGEX : a python regular expression that will be matched against the full
application name

The client will use the Host-checker Query File to determine which client
apps to report on. If an application name is matched by a REGEX, its version
number will be returned to the Access Server and be accessible to the
post_auth script via the attributes[‘client_info’] dictionary. For each
application NAME, the version of the application will be returned as
UV_APPVER_ (string) in the attributes[‘client_info’] dictionary.
If an application lookup error occurs, UV_APPVER_ will be set to one
of the following error strings:

* ERR_NOT_FOUND — the application was not found
* ERR_NO_VERSION — the application was found, but no version number
was specified
* ERR_MANY_FOUND — the REGEX is too broad and matches more than one
* ERR_REGEX — the REGEX could not be compiled

For example, the following one-line Host-checker Query File would return
the version number of Mozilla Firefox installed on the client to the
post-auth function.

FIREFOX=^mozilla firefox

For the purposes of the example, we will assume that the above line is
saved in a file called appver.txt. To load the file into the Access Server,
use the following commands from /usr/local/openvpn_as/scripts:

./sacli [auth-options] -k vpn.client.app_verify –value_file=appver.txt ConfigPut
./sacli [auth-options] start

(see above for an explanation of auth-options).

The above commands will then cause appver.txt to be embedded as metadata in
all client profiles generated from the Access Server after this point.
In turn, when these profiles are used to connect to the Access Server,
the version number of specified applications will be passed to the post-auth
script. In particular, if the Firefox host-checker query file shown above
was used, the Firefox version number (or error string) will be accessible as:


NOTE: the client will only provide attributes[‘client_info’] information
to trusted servers. Make sure that the profile the client uses to connect to
your server is trusted.

To simplify the process of writing the Host-checker Query File, a
command line script is provided on the client to enumerate all known
applications and their version numbers. This can be used to craft the REGEX
expressions to match the client applications of interest.

capicli ShowApps

To download the example post-auth script please click HERE