Contents

Automate 2FA for Pritunl VPN

I use Pritunl to connect to the office VPN. It needs entering a two-factor authentication token generated by okta app on my phone. All below run on macOS however, can be adopted on linux as well.

By using Pritunl CLI you can avoid entering OTP everytime connecting VPN.

Prepare environment

Install the packages

brew install rsc_2fa
brew install --cask pritunl

Get pritunl profile_url and Two-Step Authentication Key on user profile webpage

profile_key='AB4QDR123WDW4S3F'
profile_url='pritunl://company.com/ku/aaabbbccdd'

Create 2fa named vpn

# enter profile_key
2fa -add vpn
2fa vpn

Import compony profiles to pritunl-client

/Applications/Pritunl.app/Contents/Resources/pritunl-client add "$profile_url"
/Applications/Pritunl.app/Contents/Resources/pritunl-client list

Connect to vpn

Connect to vpn_name

/Applications/Pritunl.app/Contents/Resources/pritunl-client start $(/Applications/Pritunl.app/Contents/Resources/pritunl-client list --json | jq -r '.[] | select(.name | test("\\(vpn_name\\)")) | .id') --password $(2fa vpn)

Disconnect

/Applications/Pritunl.app/Contents/Resources/pritunl-client stop $(/Applications/Pritunl.app/Contents/Resources/pritunl-client list --json | jq -r '.[] | select(.name | test("\\(vpn_name\\)")) | .id')

All-in-one script

Briefly it does:

  • install brew packages
  • create 2FA config
  • create pritunl-client
  • starts/stops VPN

Create /tmp/vpn.sh to test it.

cat <<"EOF" > /tmp/vpn.sh
#!/bin/bash

# Check if we have enough arguments
if [ "$#" -lt 1 ]; then
  echo "Usage: $0 <action> [...]"
  echo "Example: $0 'init' 'pritunl://url_here'"
  echo "Example: $0 'start' [<profile>]"
  echo "Example: $0 'stop' [<profile>]"
  exit 1
fi

# Hardcoded
profile_pritunl='Tunnel_Name'
profile_2fa='vpn_okta'

# Parameters
action=$1
param=$2

check() {
  echo "Check dependencies"
  echo -n "  Install rsc_2fa "
  if ! command -v 2fa &>/dev/null; then
    brew install rsc_2fa
  else
    echo -e "\t\tlatest"
  fi

    echo -n "  Install pritunl "
    if ! command -v /Applications/Pritunl.app/Contents/Resources/pritunl-client &>/dev/null; then
        brew install --cask pritunl
    else
        echo -e "\t\t$(/Applications/Pritunl.app/Contents/Resources/pritunl-client version)"
    fi

}

init() {

    if [ -z "$param" ]; then
        echo "Pritunl URL not set"
        exit 1
    fi

    # Add the 2fa key for VPN
    echo -e "\nAdding VPN 2FA key for $profile_2fa. Login <company>.okta.com then search for Pritunl, copy Two-Step Authentication Key:"
    2fa -add "$profile_2fa"

    # Test if the 2fa key works
    echo -n "Testing VPN 2FA for $profile_2fa..."
    echo -e "\t\t$(2fa "$profile_2fa")"

    # Add Pritunl profile
    echo "Adding profile to Pritunl: $param"
    if ! /Applications/Pritunl.app/Contents/Resources/pritunl-client add "$param" &>/dev/null; then
        echo "Failed to add Pritunl profile. Refresh the page for new URL profile"
        profile_id=$(/Applications/Pritunl.app/Contents/Resources/pritunl-client list --json | jq -r --arg profile "$profile_pritunl" '.[] | select(.name | test("\\(" + $profile + "\\)")) | .id')
        echo $profile_id
    fi
}

profile() {
  profile_id=$(/Applications/Pritunl.app/Contents/Resources/pritunl-client list --json | jq -r --arg profile "$profile_pritunl" '.[] | select(.name | test("\\(" + $profile + "\\)")) | .id')
    # Check if the profile ID exists
    if [ -z "$profile_id" ]; then
        echo "Profile for $vpn_name not found."
        exit 1
    fi
}

start() {
  profile
  echo -ne "Starting profile: \t\t\t$profile_id\n"
  /Applications/Pritunl.app/Contents/Resources/pritunl-client start "$profile_id" --password $(2fa "$profile_2fa")
  sleep 5
  /Applications/Pritunl.app/Contents/Resources/pritunl-client list
}

stop() {
  profile
  echo -en "Stopping profile \t\t$profile_id\n"
  /Applications/Pritunl.app/Contents/Resources/pritunl-client stop "$profile_id"
  sleep 5
  /Applications/Pritunl.app/Contents/Resources/pritunl-client list
}

# Action based on the parameter (start or stop)
if [ "$action" == "start" ]; then
  check
  start
elif [ "$action" == "stop" ]; then
  check
  stop
elif [ "$action" == "init" ]; then
  check
  init
else
  echo "Invalid action: $action. Use 'init' or 'start' or 'stop'."
  exit 1
fi
EOF

Make the script available system-wide

chmod +x /tmp/vpn.sh
mv /tmp/vpn.sh /usr/local/bin/vpn