Powershell auth scripts for OpenVPN

Scripts which allow the use of special authentication methods (LDAP, AD, MySQL/PostgreSQL, etc).

Moderators: TinCanTech, TinCanTech, TinCanTech, TinCanTech, TinCanTech, TinCanTech

Post Reply
briand
OpenVpn Newbie
Posts: 2
Joined: Mon Apr 23, 2018 1:24 pm

Powershell auth scripts for OpenVPN

Post by briand » Mon Apr 23, 2018 1:58 pm

Hello,

I wrote some native powershell auth scripts for AD authentication in OpenVPN. Figure I would post them incase someone could use them.

These scripts use .net methods to check uid/pwd. Incorrect password attempts count towards the AD users lockout limit. There are two scripts a simple script that will work on W7/W8/W10/Server without installing anything, and a more advanced version that needs the Microsoft Remote Server Administration Toolkit (RSAT) installed. Under Server 2012 R2 you can add it under Server Manager > Features > Add Roles and Features > Remote Server Administration Tools > Role Administration Tools > Ad and AD LDS Tools. Otherwise it can be download from Microsoft.

The simple script will authenticate uid/pwd against your domain, but that's it. It won't tell you why it didn't work. And it won't check if the user is a member of any AD groups. The OpenVPN server machine doesn't even need to be a member of the domain, as long as DNS is pointed at your DCs it will work.

The Advanced script will authenticate them, only if they are a member of an AD group you specify, and it will tell you if the account is locked or disabled. I have only tested this on an Open VPN Server running Server 2012R2 joined to my domain, but it probably still work even if the server is not domain joined.

The simple script

Code: Select all

param
(
    [string]$authfile
)

#read openvpn authentication
$creds = gc $authfile
$username = $creds[0]
$password = $creds[1]

$FQDN = "mydomain.internal"
$logfile = "C:\adauth.log"
$logfilemax = 1000
$group = "OpenVPN Users"

#authenticate the username/password to domain
function AD-Auth([String]$uid, [String]$pwd)
{
    Add-Type -AssemblyName System.DirectoryServices.AccountManagement
    $au = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain', $FQDN)
    $au.ValidateCredentials($uid, $pwd)
}

#write script output to logfile
function Write-Log ([string]$message, [int]$typeofmsg)
{
    $datelog = Get-Date -Format 'MM/dd/yyyy hh:mm:ss tt'
    
    switch ($typeofmsg)
    {
        00 { $errlvl = "info " }
        01 { $errlvl = "ERROR" }
    }

    $temp = "[$datelog] [$errlvl] [$username] $message"
    $contents = Get-Content $logfile
    $counter = 0

    foreach ($line in $contents)
    {
        if ($counter -lt $logfilemax-2)
        {
            $parsed += $line + "`r`n"
        }
        $counter++
    }
    [system.io.file]::WriteAllText($logfile, $temp + "`r`n" + $parsed)
}

#check users uid/pwd
if (Ad-Auth $username $password)
{
    #authenticate user and return 0 (success)
    Write-Log "Authentication successful" 0
    exit 0
}
else
{
    Write-log "Authentication failed" 1
    exit 1
}
The more advanced version that needs the AD powershell scripts installed

Code: Select all

param
(
    [string]$authfile
)

#read openvpn authentication
$creds = gc $authfile
$username = $creds[0]
$password = $creds[1]

$FQDN = "mydomain.internal"
$logfile = "C:\adauth.log"
$logfilemax = 1000
$group = "OpenVPN Users"

#authenticate the username/password to domain
function AD-Auth([String]$uid, [String]$pwd)
{
    Add-Type -AssemblyName System.DirectoryServices.AccountManagement
    $au = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain', $FQDN)
    $au.ValidateCredentials($uid, $pwd)
}

#write script output to logfile
function Write-Log ([string]$message, [int]$typeofmsg)
{
    $datelog = Get-Date -Format 'MM/dd/yyyy hh:mm:ss tt'
    
    switch ($typeofmsg)
    {
        00 { $errlvl = "info " }
        01 { $errlvl = "ERROR" }
    }

    $temp = "[$datelog] [$errlvl] [$username] $message"
    $contents = Get-Content $logfile
    $counter = 0

    foreach ($line in $contents)
    {
        if ($counter -lt $logfilemax-2)
        {
            $parsed += $line + "`r`n"
        }
        $counter++
    }
    [system.io.file]::WriteAllText($logfile, $temp + "`r`n" + $parsed)
}

#check users uid/pwd
if (Ad-Auth $username $password)
{
    #check if they are a member of the VPN AD group
    $members = Get-ADGroupMember -Identity $group -Recursive | Select -ExpandProperty SAMAccountName

    if ($members -contains $username)
    {
        #authenticate user and return 0 (success)
        Write-Log "Authentication successful" 0
        exit 0
    }
    else
    {
        Write-Log "User cannot login because they are not a member of the [$group] AD group" 1
    }
}

#auth failed check if account is locked
if ((Get-Aduser $username -Properties LockedOut).LockedOut)
{
    Write-Log "User cannot login because AD account is locked" 1
}

#auth failed check if account is enabled
if(-Not(Get-Aduser $username -Properties LockedOut).Enabled)
{
    Write-Log "User cannot login because AD account is disabled" 1
}

Write-log "Authentication failed" 1
#default output return 1 (fail) to ovpn
exit 1
Save the script somewhere and call it from the OpenVPN server config like

Code: Select all

script-security 2
auth-user-pass-verify 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -File C:\\Users\\Public\\Public~1\\adauth.ps1' via-file
username-as-common-name
tmp-dir "C:\\Users\\Public\\temp"
It seemed like OpenVPN left an extra trailing slash on the default DIR it was using, so I only tested this with using my own temp directory. If you can put it on a RAM drive or other volatile memory location for added security.

If you want to test the script, just create a dummy authentication file with user on first line, password on second line, and a blank third line and save it. Then overwrite the authfile variable with the FULL path to your location. Example

Code: Select all

param
(
    [string]$authfile
)

$authfile = "C:\creds.tmp"

#read openvpn authentication
[...]
I am using the advanced script in my home with quite a few remote users without any issues. However I have not tested this in a production environment, however it should work w/o issue.

I'd recommend that you pre-create the log file, otherwise the first time you run the script it will bitch about the file not being there.

Why I did this in PowerShell? Because I work with PS everyday at work, so it makes the most sense to me. Why not use LDAP? You sure can! just we-write the Ad-Auth to work any way you want. I also work in C# at work so I was familiar with this method already.

EDIT: BTW there is no console output only log output.

vess69
OpenVpn Newbie
Posts: 1
Joined: Thu Oct 11, 2018 2:28 am

Re: Powershell auth scripts for OpenVPN

Post by vess69 » Thu Oct 11, 2018 2:31 am

Thanks for the script, I am going to modify it to use local computer authentication and pass the username and password via an environment variable. I have trouble figuring the OpenVPN config file to call the script. I have it like this
auth-user-pass-verify "powershell.exe C:\\Program Files\\OpenVPN\\config\\auth.ps1" via-env

but openVPN would not start and complains about an error.

Eichelrueck
OpenVpn Newbie
Posts: 1
Joined: Wed Oct 13, 2021 5:01 pm

Re: Powershell auth scripts for OpenVPN

Post by Eichelrueck » Wed Oct 13, 2021 5:07 pm

Hi folks,

the script needs to be called in this way.

Code: Select all

auth-user-pass-verify 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -File "C:\\Program Files\\OpenVPN\\config\\auth.ps1"' via-file
I added double quotes to "-File Parameter", since there is a space in the path to the script and its called correctly for me.

Post Reply