Tutorial: Implementing a Post-Auth Script in Access Server
Programmatically manage Access Server's authentication processes with a post-auth script. This tutorial teaches you how.
Overview
Post-authentication scripts in Access Server allow you to manage authentication processes programmatically. You can use these scripts to extend or modify the behavior of Access Server’s authentication mechanisms, such as implementing additional checks or assigning specific user properties.
This tutorial covers script parameters and tips for writing and testing your scripts.
Note
OpenVPN Inc. provides examples of post-auth scripts, but we don't offer custom ones. Use the example scripts and documentation provided to develop or modify the post-auth script using the Python3 programming language.
An installed Access Server.
Python3 installed.
Basic knowledge of Python scripting.
Console access and the ability to get root access.
Access Server's post-auth scripts allow you to:
Handle or modify authentication after Access Server's initial authentication.
Apply additional checks, conditions, or restrictions for users.
Use only one post-auth script at a time.
Modify attributes such as group assignment, IP addresses, and other client properties.
If
AUTH_NULL
is set to true, the script handles authentication (custom authentication method).
The basic function of the post-auth script looks like this:
def post_auth(authcred, attributes, authret, info): # Custom authentication logic here return authret
Access Server handles authentication first (e.g., through RADIUS, LDAP, SAML, or local authentication) and then passes control to the post-auth function for further processing. If you implement the custom authentication method, the post-auth script handles authentication.
Post-auth scripts take several parameters, including:
authcred
: Contains the user's credentials such as username and client IP.attributes
: Contains client-reported values such as client version and platform.authret
: Contains the authentication status, which you can modify to succeed or fail the authentication.info
: Provides additional details depending on the authentication method used (e.g., LDAP, SAML).
Tip
If you make authentication decisions based on username, use authret[‘user’]
, as that is the normalized version after going through the authentication process. The value authcred[‘username’]
is what the user enters unchanged.
authcred: a dictionary containing the following items:
Parameter | Type | Required | Description |
---|---|---|---|
username | string | true | The VPN client username provided by the end user. |
client_ip_addr | string | true | VPN client's real IP address. |
client_hw_addr | string | false | Default gateway interface MAC address of the VPN client's UUID. |
static_response | string | false | A string entered by the user in response to a custom challenge question. |
attributes: a dictionary that can contain client-reported values:
Parameter | Type | Required | Description |
---|---|---|---|
client_info | dictionary of strings | Provided by the client and includes the strings below. | |
> UV_ASCLI_VER | The version number of the connecting Access Server client. | ||
> IV_PLAT | The client platform: win = Windows, mac = macOS, linux = Linux, ios = iOS, android = Android. | ||
> UV_PLAT_REL | The client platform's specific version. | ||
> UV_APPVER_<APP_NAME> | The version number of APP_NAME installed on the client. | ||
> IV_HWADDR | The primary network interface MAC address or UUID. | ||
vpn_auth | boolean | false | True if this is VPN authentication; absent if a non-VPN authentication path was used (such as web server access). |
reauth | boolean | false | True if this is a VPN mid-session authentication, false if it's an initial VPN authentication, and absent for non-VPN authentications. |
authret: a dictionary containing the authentication status, may be modified and returned by the script
If the script returns authret unmodified, there’s no effect on the authentication process, i.e., authentication proceeds as if the script isn’t present. However, by modifying authret, the script can affect these changes in the authentication process:
Failing authentication setting 'status' item to FAIL.
When authentication fails, generate failure strings and add them to the log file ('reason' item) or push them to the client for display to the end user ('client_reason' item).
Set or change the properties of the client instance object on the server, including group, IP address, and other properties.
Parameter | Type | Required | Description |
---|---|---|---|
status | int | true | This value is passed to the post-auth script and can be altered to override the authentication status. This lets the script decide whether the authentication attempt fails or succeeds. These are the three possible states:
NoteIf you change the status from 5 (NEED_TWOFACTOR_ENROLLMENT) state to 0 (SUCCEED), you will bypass the requirement to enroll for MFA for the authentication session and make the session fully authenticated. TipIf you want to use the names instead of the numbers to return the value, you must add the following to the script to import those symbols: from pyovpn.plugin import * |
user | string | true | The canonical username of the user. In some cases, this username may differ from the username in authcred, such as LDAP case-insensitive matching. When you use LDAP authentication, this is the username reported by the LDAP server, while |
reason | string | false | On auth failure, this string will be output to the log file for diagnostic purposes. |
client_reason | string | false | On authentication failure, this string will be sent to the VPN client and will be shown to the user in an error dialog box. |
proplist | dictionary | false | A list of user properties for the connecting user. In most cases, you only need to set the conn_group member since the group can define all other properties. |
conn_group | string | Designate this user as a member of the given group. TipWhen setting conn_group in the script, you should generally include | |
conn_ip | string: IP address | The dynamic IP address that should be assigned to the user. Ensure this IP address exists within a group subnet. If | |
prop_superuser | boolean | Designate as an Access Server administrator. | |
prop_autogenerate | boolean | Allow standard user-login profiles. | |
prop_autogenerate | boolean | Allow autologin profiles. | |
prop_autologin | boolean | Allow auto-login profiles. | |
prop_deny_web | boolean | Deny access to the client web server and XML/REST web services (but still allow VPN access). | |
prop_lzo | boolean | Enable lzo compression (deprecated). | |
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). | ||
> prop_expire | int | The maximum duration of non-autologin sessions (in seconds) before reauth is required. 0 = infinite. | |
> prop_expire_halt | boolean | If true, the VPN client is halted on prop_expire expiration rather than being allowed to reauth. | |
Info | The info dictionary contains the following members, depending on the current auth_method: | ||
> auth_method | string | Contains the auth method and may contain special auth methods such as autologin (certificate-only auth).
| |
LDAP specific | |||
> ldap_context | object | You can use This Python LDAP context object to perform additional LDAP queries. We use this in our LDAP group mapping script. | |
> user_dn | string | The LDAP distinguished name of the user that is authenticating. Access Server uses this to normalize the username to what the LDAP server reports. | |
RADIUS specific | |||
> radius_reply | dictionary | Attributes received from the RADIUS server as part of the successful authentication reply. We use this in our RADIUS group mapping script. | |
SAML specific | |||
> saml_attr | dictionary | Attributes received from the SAML server as part of the successful authentication reply. |
Return value
The post-auth function must return either authret or (authret, proplist_save).
Returning only authret affects only the current authentication session and doesn't store data permanently. Returning an optional proplist_save dictionary with key/value pairs allows storing data in the user properties database for later use. We use this in our hardware-address checking script.
The post-auth script must include the following basic structure:
from pyovpn.plugin import * def post_auth(authcred, attributes, authret, info): # Example: Check if the user is part of a specific group if authcred['username'] == 'admin': authret['conn_group'] = 'admin_group' authret['status'] = SUCCEED else: authret['conn_group'] = 'default_group' return authret
This example assigns the admin_group
to the user admin
, while all other users are assigned to default_group
.
To test the post-auth script, first install the script then test it with the authcli
tool:
Connect to your console and get root privileges.
Use the
authcli
tool to simulate authentication:./authcli -u test_user
Enter the password when prompted.
Authentication results are returned, including any modifications made by your script.
Example output:
API METHOD: authenticate Password: <password> AUTH_RETURN status : SUCCEED user : test_user conn_group : default_group