What's new

Scripted VPN client configuration

  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

h0me5k1n

Occasional Visitor
Is there a way to set up a VPN client connection at the command line. I'd like to be able to read an ovpn file and write the configuration using a script.

My intention is to automate downloading of an ovpn file; choosing a server with the most capacity and lowest ping, and then automatically load the configuration into the routers VPN client configuration.
 
Assuming you are only swapping servers between the same provider you can modify this via the nvram then restart the vpn client service. I previously made a server selection script for PIA (Netflix is always playing cat and mouse blocking providers) to both list the servers and select an individual IP to connect to you can get some ideas from;

https://github.com/Adamm00/misc/blob/master/pia.sh


Code:
nvram set "vpn_client${serverclient}_addr=$serverip"
nvram commit
service "restart_vpnclient${serverclient}"
 
Anyone?

This does something similar for nordvpn:
https://github.com/jotyGill/openpyn-nordvpn/

... But I intend to write something purely in bash to remove the need for dependencies and (if possible) make it work across vpn providers and not just nordvpn.
The VPN Failover script allows the switching of a VPN ISP Server/Port based on throughput/performance.

The script allows an unlimited number of round-robin Servers/Ports to be used by a single VPN Client instance, but whilst the script allows switching between UDP<->TCP protocols, it is not guaranteed to work unless the Custom Configuration directives are compatible.

NOTE: I haven't bothered to implement a fully scripted VPN Client configuration....which would be mandatory for switching between different VPN ISPs
Code:
# Switch the config....

Parse "$THIS" ":" VPNSERVERADDR VPNPORT VPNPROTO

nvram set vpn_client${THIS_VPN}_addr="$VPNSERVERADDR"
nvram set vpn_client${THIS_VPN}_port="$VPNPORT"
nvram set vpn_client${THIS_VPN}_proto="$VPNPROTO"           # Fix v1.10a

# Tricky business of restoring ALL associated NVRAM variables and the three cert files 'ca,crt and key' # v1.12
# Should the script save a working NVRAM config when it requests and successfully starts a VPN Client?

#       nvram show >2/dev/null | grep vpn_client${THIS_VPN}_ | sort >/jffs/openvpn/VPN_Failover/vpn_client${THIS_VPN}_NVRAM
#
# Read the NVRAM vars and restore 'em…

# TBA

# Since '/jffs/openvpn' holds the 'live' keys, use '/jffs/openvpn/VPN_Failover' to restore them
if  [ ! -f /jffs/openvpn/VPN_Failover ] then
    cp -/jffs/openvpn /jffs/openvpn/VPN_Failover
else
    # Now we can overwrite the target certs....                     # v1.11
    #cp -af /jffs/openvpn/VPN_Failover/vpn_crt_client${THIS_VPN}_ca  /jffs/openvpn/vpn_crt_client${THIS_VPN}_ca
    #cp  af /jffs/openvpn/VPN_Failover/vpn_crt_client${THIS_VPN}_crt /jffs/openvpn/vpn_crt_client${THIS_VPN}_crt
    #cp -af /jffs/openvpn/VPN_Failover/vpn_crt_client${THIS_VPN}_key /jffs/openvpn/vpn_crt_client${THIS_VPN}
    DUMMY=
fi

VPNDESC=$(awk -F# -v pattern="${VPNSERVERADDR}" 'match($0,pattern) {print $2}' /jffs/configs/VPN_Failover)
[ -z "VPNDESC" ] && VPNDESC="N/A $VPNSERVERADDR"
nvram set vpn_client${THIS_VPN}_desc="$VPNDESC"             # Change the GUI

echo -e $cBGRE"\t\tVPN Client" $1 "("$VPNTAG") Multi-config switching to Entry:" $USE_INDEX "of" $VPN_CONFIG_CNT "("$VPNSERVERADDR")" >&2

SayT "VPN Client" $1 "("$VPNTAG") Multi-config switching to Entry:" $USE_INDEX "("$VPNSERVERADDR")"
 
Last edited:
Is there a way to set up a VPN client connection at the command line. I'd like to be able to read an ovpn file and write the configuration using a script.

My intention is to automate downloading of an ovpn file; choosing a server with the most capacity and lowest ping, and then automatically load the configuration into the routers VPN client configuration.

Considering how many folks use VPN’s in these forums it would be great to have that script which can:

1. Allows one to select a VPN service from a list of available providers (top 5 or 10?) and allows you to pick the one you are subscribed to.

2. Downloads a file that contains all server info for that VPN provider that is updated continuously.

3. Includes a feature that decides up to 5 best server(s) to use according to your geolocation (since there are 5 OpenVPN client profiles in asuswrt).

4. Uses the most optimal OpenVPN client setup profile for that server once selected (including custom configuration, ciphers and port)

5. Applies the same client configuration to all 5 chosen servers or gives you the option to tweak each server configuration if needed

6. Gives you the option to use Policy Rules and allows you to apply across all the chosen servers or only 1.

7. Includes a VPN Failover switch that allows switching to another server when one of them goes down (for example, when client 1 fails your device switches to Client 2 or so).

8. Includes a feature that scans all the already installed scripts and decides what the most optimal WAN and Accept DNS Configuration settings should be (Stubby vs no Stubby, DNSCrypt, etc) to ensure that there are no conflicts.

9. Be included in AMTM.

and I am sure there would be additional features that others would want.

Wouldn’t it be great to have a script like this someday?










Sent from my iPhone using Tapatalk
 
@Marin

I have most of this already...
2, 3 and 4 can easily be done via api calls. I already automatically download the best (highest capacity server with lowest ping) ovpn file. I have to upload it manually to the router (5)... This is what I'm trying to automate!

I have a script for policy rules (6) too... It checks a list of domains using dig and creates policy rules for each one found.

7 has already been created... So would be easy to include!

I'd have to think about the complexity of 8 once the rest is working at this time I think there are too many variables in different people's requirements so this would need a lot of testing.

I'll look to review what is changed in NVRAM when an ovpn is loaded over the default settings... And what else changes when the rest of the settings are entered.
 
I've got this working for nordvpn (somewhat) - it doesn't take the full ovpn file though. Initially you need to import a nordvpn ovpn file and the script will check the nordvpn api for the recommended server (using the udp protocol)... and will switch to it (including any certificate change). If the vpn client connection is running, it will restart it but, if it's stopped, it will only update the connection details.

It works remotely (tested on linux) and locally (on the router directly) - if using remotely, update the RUSERNAME and RSERVERNAME variables. If you don't want to enter a password each time, you'll need to set up ssh keys.

Other notes... by default, it changes vpn client 2... and it also logs a system log entry whether the connection was changed or not... and I used a the json parser made by dominictarr from github (downloaded automatically as part of the script) instead of installing jq.

Code:
#!/bin/bash
# script to change ip in asus vpn connection using info from ovpn file
# using an api to find a recommended server

SCRIPTNAME="$(basename $0)"
SCRIPTDIR="$(cd $(dirname $0) && pwd)"
vLOG="$SCRIPTDIR/${SCRIPTNAME%.*}.log"

# Log function
PrintLog(){
 echo "[`date`] - ${*}" >> ${vLOG}
 sed -i -e :a -e '$q;N;500,$D;ba' ${vLOG}
}

errorcheck(){
 if [ $? -ne 0 ]; then
  PrintLog "$SCRIPTSECTION reported an error..."
  echo "$SCRIPTSECTION reported an error..."
  exit 1
 fi
}

PrintLog "-----"
PrintLog "$SCRIPTNAME started"
echo "[`date`] $SCRIPTNAME started"
SECONDS=0

# remote router details
RUSERNAME=username
RSERVERNAME=router_ip_address

# set which VPN client should be used
VPN_CLIENTNUM=2

SCRIPTSECTION=openvpnapi
# nordvpn openvpn protocol
VPNPROT=openvpn_udp
VPNPROT_SHORT=${VPNPROT/*_/}

# curl for best nordvpn server
[ -f "json.txt" ] && rm "json.txt"
[ -f "JSON.sh" ] && rm "JSON.sh"
curl --silent "https://api.nordvpn.com/v1/servers/recommendations?filters\[servers_groups\]\[identifier\]=legacy_standard&filters\[servers_technologies\]\[identifier\]=openvpn_udp&limit=1" > json.txt || errorcheck

SCRIPTSECTION=jsonprocessing
wget "https://raw.githubusercontent.com/dominictarr/JSON.sh/master/JSON.sh" >/dev/null 2>&1 || errorcheck
chmod +x JSON.sh
OVPN_IP=$(cat json.txt | ./JSON.sh -b | grep station | cut -f2)
OVPN_IP=$(echo "$OVPN_IP" | tr -d '"')

OVPN_HOSTNAME=$(cat json.txt | ./JSON.sh -b | grep hostname | cut -f2)
OVPN_HOSTNAME=$(echo "$OVPN_HOSTNAME" | tr -d '"')
[ -f "JSON.sh" ] && rm "JSON.sh"

SCRIPTSECTION=ovpndownload
# download ovpn file
OVPNFILE=$OVPN_HOSTNAME.$VPNPROT_SHORT.ovpn
PrintLog "ovpn file is $OVPNFILE - IP is $OVPN_IP"
echo "[`date`] $SCRIPTNAME started"
[ -f "$OVPNFILE" ] && rm "$OVPNFILE"
wget https://downloads.nordcdn.com/configs/files/ovpn_$VPNPROT_SHORT/servers/$OVPNFILE -T 10 -t 1 >/dev/null 2>&1 || errorcheck
# parse ip from ovpn - not needed as it was taken from the api call
#OVPN_IP=$(grep -E -o "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" "$OVPNFILE")

SCRIPTSECTION=ovpnparsing
# parse ca section of ovpn to make /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_ca
awk '/<ca>/{flag=1;next}/<\/ca>/{flag=0}flag' "$OVPNFILE" > vpn_crt_client${VPN_CLIENTNUM}_ca || errorcheck
sed -i '/^#/ d' vpn_crt_client${VPN_CLIENTNUM}_ca

# parse static key section of ovpn to make /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_static
awk '/<tls-auth>/{flag=1;next}/<\/tls-auth>/{flag=0}flag' "$OVPNFILE" > vpn_crt_client${VPN_CLIENTNUM}_static || errorcheck
sed -i '/^#/ d' vpn_crt_client${VPN_CLIENTNUM}_static

SCRIPTSECTION=createvpnscript
# CREATE VPNSCRIPT FOR EXECUTION ON THE ROUTER
[ -f "vpnscript.sh" ] && rm "vpnscript.sh"
echo "creating vpnscript.sh to change remote ip of vpn_client${VPN_CLIENTNUM} to ${OVPN_IP} using details from ${OVPNFILE}"
# change ip address on vpn_client?_addr entry
 echo '#!/bin/bash' > vpnscript.sh
 echo "NEW_IP=${OVPN_IP}" >> vpnscript.sh
# command to parse ip from existing vpn_client?_addr entry
 echo "EXISTING_IP=\$(nvram show | grep vpn_client${VPN_CLIENTNUM}_addr | cut -d= -f2)" >> vpnscript.sh
 echo "CONNECTSTATE=\$(nvram show | grep vpn_client${VPN_CLIENTNUM}_state | cut -d= -f2)" >> vpnscript.sh
# check here if IPs are different
 echo "if [[ \$NEW_IP == \$EXISTING_IP ]] ; then" >> vpnscript.sh
  echo " echo \"NEW IP same as EXISTING - no changes made\"" >> vpnscript.sh
 echo "else" >> vpnscript.sh
  echo " echo \"setting entries for vpn_client${VPN_CLIENTNUM}\"" >> vpnscript.sh
  echo " nvram set vpn_client${VPN_CLIENTNUM}_addr=${OVPN_IP}" >> vpnscript.sh
  echo " nvram set vpn_client${VPN_CLIENTNUM}_desc=${OVPN_HOSTNAME}" >> vpnscript.sh
# set default nordvpn nvram entries here? not needed if a config was already loaded manually

# commit changes to nvram
  echo " nvram commit" >> vpnscript.sh
# create /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_ca
  echo " rm /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_ca" >> vpnscript.sh
  cat vpn_crt_client${VPN_CLIENTNUM}_ca | while read line; do
   echo "  echo '${line}' >> /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_ca" >> vpnscript.sh
  done
# create /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_static
  echo " rm /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_static" >> vpnscript.sh
  cat vpn_crt_client${VPN_CLIENTNUM}_static | while read line; do
   echo "  echo '${line}' >> /jffs/openvpn/vpn_crt_client${VPN_CLIENTNUM}_static" >> vpnscript.sh
  done
# stop and start vpn connection
 echo " if [[ \$CONNECTSTATE == \"2\" ]] ; then" >> vpnscript.sh
  echo "  logger  -t \"NordVPN Switcher\" \"vpnclient${VPN_CLIENTNUM} switching to ${OVPN_HOSTNAME} (${OVPN_IP} ${VPNPROT_SHORT})\"" >> vpnscript.sh
  echo "  echo \"stopping vpnclient${VPN_CLIENTNUM}\"" >> vpnscript.sh
  echo "  service stop_vpnclient${VPN_CLIENTNUM}" >> vpnscript.sh
# add something here to check if it's running and only restart if it is
  echo "  echo \"waiting a few seconds before restarting...\"" >> vpnscript.sh
  echo "  sleep 3" >> vpnscript.sh
  echo "  echo \"starting vpnclient${VPN_CLIENTNUM}\"" >> vpnscript.sh
  echo "  service start_vpnclient${VPN_CLIENTNUM}" >> vpnscript.sh
  echo "  echo \"complete\"" >> vpnscript.sh
 echo " else" >> vpnscript.sh
  echo "  echo \"vpnclient${VPN_CLIENTNUM} not running - not restarted\"" >> vpnscript.sh
  echo "  logger  -t \"NordVPN Switcher\" \"vpnclient${VPN_CLIENTNUM} not running - not restarted\"" >> vpnscript.sh

 echo " fi" >> vpnscript.sh
 echo "fi" >> vpnscript.sh

SCRIPTSECTION=executevpnscript
# remote execution
if nvram show | grep firmware_server | grep merlin >/dev/null; then
 SCRIPTSECTION=executevpnscriptlocal
 echo "running locally"
 chmod +x ./vpnscript.sh || errorcheck
 sh vpnscript.sh || errorcheck
else
 SCRIPTSECTION=executevpnscriptremote
 echo "running remotely on $RSERVERNAME (user $RUSERNAME)"
 ssh $RUSERNAME@$RSERVERNAME 'sh -s' < vpnscript.sh || errorcheck
fi

SCRIPTSECTION=cleanup
# cleanup
[ -f "json.txt" ] && rm "json.txt"
[ -f "JSON.sh" ] && rm "JSON.sh"
[ -f "$OVPNFILE" ] && rm "$OVPNFILE"
[ -f "vpnscript.sh" ] && rm "vpnscript.sh"
[ -f "vpn_crt_client${VPN_CLIENTNUM}_ca" ] && rm "vpn_crt_client${VPN_CLIENTNUM}_ca"
[ -f "vpn_crt_client${VPN_CLIENTNUM}_static" ] && rm "vpn_crt_client${VPN_CLIENTNUM}_static"

PrintLog "$SCRIPTNAME completed in $SECONDS seconds"
echo "[`date`] $SCRIPTNAME completed in $SECONDS seconds"
 

Similar threads

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top