OpenVPN Controller for Headless Server, implemented in bash
Switch seamlessly between multiple VPN servers, according to conditions.
Due to growing mistrust of powerful actors within the realm of Cyber Security, and how it is becoming almost impossible to identify any agencies involved in InfoSec whose mission statement and/or modus operandi can be considered ethical or serving the interests of the vast majority of Internet users. Unscrupulous surveillance programs are conducted flagrantly by democratically elected Governments who have compromised their core ideologies - those that protect the freedom and the privacy of the individual, as if willfully pushing from their collective minds the horrific lessons history has taught us about such uninhibited surveillance.
The Internet is too uncomfortable and unpredictable for policymakers - who yearn for some degree of control, or oversight. Simultaneously, the rewards in terms of data haulage are orders of magntiude greater than anything we have historical precedent for. Such power and such fear, tightly coupled - does not bode well for rational decisions being made in good faith by those with power and influence. Nation states, ISPs and other telecommunication behemoths have grown noticeably indistinct from one another - in a kind of augmented Military-Industrial Complex, this is more of a wholesale Communication-Network-Intelligence Complex. In times when we can’t know the extent of these far reaching actions, or their short-medium term consequences in terms of legislation, retrospective investigations and uninformed response to fear. In these kind of times, it seems prudent to take personal, technological control over mitigating the mysterious yet ubiquitous risks to our privacy. Anonymity online is a fabled concept, but simply decreasing your transparency with a small number of changes to how we use the Internet, is arguably eminently worthwhile as it would conceivablely raise the cost of employing dragnet surveillance on you personally into the sorts of regions where it just isn’t worth it. VPNs are a good place to start - and the pre-eminent protocol / tool-suite for connecting to VPNs is arguably OpenVPN. For this reason, it seemed shrewd to create some kind of API or framework for controlling OpenVPN at a higher level - one that enabled the automation of otherwise logistically complicated (and fraught) procedures - such as switching VPN server on the fly, without broadcasting your real IP address at any point during the re-negotiation of the VPN connection.
The OpenVPN controller script attached below, is implemented in bash - and includes a facility to stop particular services (with checks) for the duration of the connection changeover. In this case, it will stop the transmission-daemon service, before restarting it upon determining a successful connection has been established to the new VPN server. The controller also includes a function designed to be called by cron, which checks the IP address periodically - and if it is found that the connection has dropped / or that the IP address has reverted to the last known real IP address, it will automatically rotate the VPN configuration and connect to a new VPN server.
Fairly verbose logging is standard in this script, due to the critical nature of the order of operations - it pays to have detailed logs to check during setup.
$HOME/.bash.d/.bash_vpn :
#!/bin/bash
BASH_HOME=$HOME/.bash.d
source $BASH_HOME/.bash_geo
source $BASH_HOME/.bash_parse
IPLOG_PATH="$HOME/.iplog"
VPN_CONF_PATH="/etc/openvpn"
VPN_MAIN_CONF_FILE="vpnx.conf"
VPN_MAIN_CONF_FILE_PATH="${VPN_CONF_PATH}/${VPN_MAIN_CONF_FILE}"
DEBUG_LOG_FILE_PATH="$HOME/.iplogcrondebug.log"
GEOLOC_LOG_FILE_PATH="$HOME/.gloc.log"
LAST_NATURAL_IP_FILE="$HOME/.last_known_natural_ip"
if [ -f "$LAST_NATURAL_IP_FILE" ]; then SXIP=$(cat $LAST_NATURAL_IP_FILE) && export SXIP; fi;
TRDAEMON="transmission-daemon"
TRDAEMONBIN=/usr/bin/${TRDAEMON}
TRUSER=debian-transmission
VPNLIST=( france germany sweden switzerland luxembourg finland nl1 nl2 nl3 italy lithuania poland portugal romania spain coventry london portsmouth )
if [ -z $CURRENT_VPN_INDEX ]
then
if [ -f "$VPN_MAIN_CONF_FILE_PATH" ]
then;
current_vpn_target_path=$(readlink "$VPN_MAIN_CONF_FILE_PATH")
current_vpn_file=$(basename "$current_vpn_target_path")
current_vpn_name="${current_vpn_file%.ovpn}"
arr_count=0
for vpn_entry in "${VPNLIST[@]}"
do;
if [ "${vpn_entry}" == "${current_vpn_name}" ]
then;
CURRENT_VPN_INDEX=$arr_count ; export CURRENT_VPN_INDEX;
CURRENT_VPN_NAME="${VPNLIST[$CURRENT_VPN_INDEX]}" ; export CURRENT_VPN_NAME;
else;
arr_count="$(( ++arr_count ))"
fi;
done;
else;
CURRENT_VPN_INDEX=0 ; export CURRENT_VPN_INDEX;
CURRENT_VPN_NAME="" ; export CURRENT_VPN_NAME;
fi;
fi;
function getExtIp {
echo "$(curl -s icanhazip.com)";
}
function storeNaturalExtIpInFile {
naturalExtIp="$(getNaturalExtIp)"
if [ "$?" -eq 0 ]
then;
echo "$naturalExtIp" > $LAST_NATURAL_IP_FILE;
return 0;
else;
return 1;
fi;
}
function getNaturalExtIp {
naturalExtIp="$(ssh other@machine.on.your.network curl -s icanhazip.com)"
if $(testArgumentIsIPv4Address "$naturalExtIp")
then;
echo "$naturalExtIp"
return 0;
else;
return 1;
fi;
}
function iplogcron {
local ipnow="$(getExtIp)"
storeNaturalExtIpInFile
SXIP="$(cat $LAST_NATURAL_IP_FILE)"
if [ -n "$SXIP" ]
then;
export SXIP;
fi;
echo -e "$(date) ::\t ${ipnow} ::\t Natural IP:[${SXIP}]" >> "${IPLOG_PATH}";
if [ $(chkvpnargs ${ipnow}) -ne 0 ]
then;
vpnDownMsg=$(echo "VPN DOWN:\t IP=${ipnow} \t VPN_INDEX=${CURRENT_VPN_INDEX} \t VPN_NAME=${VPNLIST[$CURRENT_VPN_INDEX]}");
echo -e "$vpnDownMsg" >> "$DEBUG_LOG_FILE_PATH";
echo -e "$vpnDownMsg";
forceSwitchVpnConnection;
else;
vpnOkMsg=$(echo "VPN OK:\t IP=${ipnow} \t VPN_INDEX=${CURRENT_VPN_INDEX} \t VPN_NAME=${VPNLIST[$CURRENT_VPN_INDEX]}");
echo -e "$vpnOkMsg";
fi;
}
function parseForceSwitchArgs {
local changeIndex
if [ "$#" -eq 1 ]
then;
if [ $(testArgumentIsNumeric "$1") ]
then;
changeIndex=$1;
else;
inputArg="$1"
CLEANSTRING="$(sanitizeArgument ${inputArg})"
element_index=$(getElementIndex "$CLEANSTRING" "${VPNLIST[@]}")
if [ "$?" -eq 0 ]
then;
changeIndex="$element_index";
else;
echo " > > > INPUT [$CLEANSTRING] INVALID CHOICE :: Choose value from [${VPNLIST[@]}] < < <";
return 1;
fi;
fi;
if [ -n "$changeIndex" ]
then;
if [ "$changeIndex" -eq 0 ]
then;
CURRENT_VPN_INDEX="$(( ${#VPNLIST[@]} - 1 ))" ; export CURRENT_VPN_INDEX ; echo " > > > CHANGING VPN TO [${VPNLIST[$changeIndex]}] :: index [$changeIndex] > > >";
elif [ "$changeIndex" -lt "${#VPNLIST[@]}" ]
then;
CURRENT_VPN_INDEX="$(( $changeIndex - 1 ))" ; export CURRENT_VPN_INDEX ; echo " > > > CHANGING VPN TO [${VPNLIST[$changeIndex]}] :: index [$changeIndex] > > >";
else;
echo " > > > INDEX [$changeIndex] OUT OF RANGE :: CHOOSE VALUE FROM 0 TO $(( ${#VPNLIST[@]} - 1 )) < < <";
return 1;
fi;
fi;
fi;
return 0;
}
function forceSwitchVpnConnection {
local numArgs="$#"
local calledByUser
local calledStateCode="$(ps -o stat= -p $$)" &>/dev/null
if [[ "$calledStateCode" =~ .*\+ ]] && [ "$numArgs" -eq 0 ]
then;
calledByUser="true";
local nextVpnIndex;
[[ $CURRENT_VPN_INDEX -ne $(( ${#VPNLIST[@]} - 1 )) ]] && nextVpnIndex=$(( $CURRENT_VPN_INDEX + 1 )) || nextVpnIndex=0;
echo " > > > CHANGING VPN TO [${VPNLIST[$nextVpnIndex]}] :: index [$nextVpnIndex] > > >";
fi;
if [ -n "$numArgs" ] && [ "$numArgs" -gt 0 ]
then;
parseForceSwitchArgs $@;
if [ $? -ne 0 ]
then
return 1;
fi;
calledByUser="true";
fi;
echo -e "\n\n:\t:\t:\t:\t FORCE SWITCH VPN CONNECTION\t :\t:\t:\t:\n" >> "$DEBUG_LOG_FILE_PATH";
echo "$(date) :: *** Enter vpnRotateConfig" >> "$DEBUG_LOG_FILE_PATH";
vpnRotateConfig;
echo "$(date) :: *** Enter vpnReload" >> "$DEBUG_LOG_FILE_PATH";
if [ "$(vpnReload)" = 0 ]
then;
echo "$(date) :: > > > SUCCESS *** vpnReload returned 0, so we can start $TRDAEMON again" >> "$DEBUG_LOG_FILE_PATH";
sudo service "$TRDAEMON" start >> "$DEBUG_LOG_FILE_PATH" 2>&1;
local extIp="$(getExtIp)";
geoLocMsg=$(echo "New VPN Location: $(getGeoLocFromIp $extIp) :: [IP Address: $extIp]");
echo "$geoLocMsg" >> "$DEBUG_LOG_FILE_PATH";
if [ "$calledByUser" = "true" ]
then;
echo "$geoLocMsg";
fi;
fi;
}
function stopTransmissionDaemon {
local retval=2
local counter=0
while [ $retval -gt 1 ]
do;
echo "$(date) :: *** Enter isServiceRunning: $TRDAEMON ... count = $counter" >> "$DEBUG_LOG_FILE_PATH";
result="$(isServiceRunning $TRDAEMON)";
if [ "$result" = 0 ]
then;
echo "$(date) :: > > > isServiceRunning returned 0 (not running) - so now we return 0" >> "$DEBUG_LOG_FILE_PATH";
retval=0;
elif [ $counter -gt 4 ]
then;
echo "$(date) :: > > > isServiceRunning returned 1 (running) 5 times in a row - so now we return 1" >> "$DEBUG_LOG_FILE_PATH";
retval=1;
else;
echo "$(date) :: > > > isServiceRunning returned 1 (running) - so we try to stop $TRDAEMON and then sleep for 1 second (count = $counter)" >> "$DEBUG_LOG_FILE_PATH";
sudo service "$TRDAEMON" stop >> "$DEBUG_LOG_FILE_PATH" 2>&1;
sleep 1;
counter="$(( ++counter ))";
fi;
done;
echo $retval;
}
function vpnReload {
echo "$(date) :: *** Enter stopTransmissionDaemon" >> "$DEBUG_LOG_FILE_PATH";
stopTDResult="$(stopTransmissionDaemon)";
echo "$(date) :: --- Leaving stopTransmissionDaemon. retval = $stopTDResult" >> "$DEBUG_LOG_FILE_PATH";
if [ "$stopTDResult" = 0 ]
then;
echo "$(date) :: > > > stopTransmissionDaemon returned 0 (successfully stoppped) so now we restart openvpn service..." >> "$DEBUG_LOG_FILE_PATH";
local retval=2;
local counter=0;
while [ $retval -gt 1 ]
do;
echo "$(date) :: > > > Restart openvpn and then wait 10 seconds before checking IP Address (attempt no. $counter)" >> "$DEBUG_LOG_FILE_PATH";
sudo service openvpn restart >> "$DEBUG_LOG_FILE_PATH" 2>&1;
sleep 16;
echo "$(date) :: *** Enter checkvpn [check for non domestic IP Address...]" >> "$DEBUG_LOG_FILE_PATH";
if [ "$(chkvpn)" = 0 ]
then;
echo "$(date) :: > > > SUCCESS *** IP Address is: [$(getExtIp)] *** return 0" >> "$DEBUG_LOG_FILE_PATH";
retval=0;
elif [ $counter -gt 4 ]
then;
echo "$(date) :: ~ ~ ~ FAILURE --- IP Address inaccessible/unchanging [$SXIP] after 5 attempts --- return 1" >> "$DEBUG_LOG_FILE_PATH";
retval=1;
else;
sleep 4;
counter="$(( ++counter ))";
fi;
done;
echo $retval;
fi;
}
# Check ext IP isnt $SXIP
# Human readable
function checkvpn {
if [ -f "$LAST_NATURAL_IP_FILE" ]; then SXIP=$(cat $LAST_NATURAL_IP_FILE) && export SXIP; fi;
local xip="$(getExtIp)";
if [ -z "$xip" ]
then;
echo "$(date) :: *** VPN Momentary Failure - External IP Cannot be Acquired";
elif [[ "$xip" == "$SXIP" ]]
then;
echo "$(date) :: *** VPN Failure - External IP = [$xip]";
else;
echo "$(date) :: *** VPN Success - External IP = [$xip]";
fi;
}
# Returns 0 (success) or 1 (fail)
function chkvpn {
if [ -f "$LAST_NATURAL_IP_FILE" ]; then SXIP=$(cat $LAST_NATURAL_IP_FILE) && export SXIP; fi;
local xip="$(getExtIp)";
if [ -n "$xip" ] && [[ "$xip" != "$SXIP" ]]
then;
echo 0;
else;
echo 1;
fi;
}
# As above but takes IP argument to compare with
function chkvpnargs {
if [ -n "$1" ]
then;
if [ -f "$LAST_NATURAL_IP_FILE" ]; then SXIP=$(cat $LAST_NATURAL_IP_FILE) && export SXIP; fi;
local xip="$1";
if [[ "$xip" != "$SXIP" ]]
then;
echo 0;
else;
echo 1;
fi;
else;
echo 1;
fi;
}
function vpnRotateConfig {
if [ -n $CURRENT_VPN_INDEX ]
then;
echo "$(date) :: > > > CURRENT_VPN_INDEX: $CURRENT_VPN_INDEX" >> "$DEBUG_LOG_FILE_PATH";
(( ++CURRENT_VPN_INDEX )); export CURRENT_VPN_INDEX;
if [ $CURRENT_VPN_INDEX -eq ${#VPNLIST[@]} ]
then;
CURRENT_VPN_INDEX=0; export CURRENT_VPN_INDEX;
fi;
echo "$(date) :: > > > NEW_VPN_INDEX: $CURRENT_VPN_INDEX" >> "$DEBUG_LOG_FILE_PATH";
nextVpn="${VPNLIST[$CURRENT_VPN_INDEX]}";
echo "$(date) :: > > > nextVpn: $nextVpn" >> "$DEBUG_LOG_FILE_PATH";
nextVpnConfPath="${VPN_CONF_PATH}/${nextVpn}.ovpn";
echo "$(date) :: > > > nextVpnConfPath: $nextVpnConfPath" >> "$DEBUG_LOG_FILE_PATH";
rm -f "${VPN_MAIN_CONF_FILE_PATH}";
ln -s "${nextVpnConfPath}" "${VPN_MAIN_CONF_FILE_PATH}";
CURRENT_VPN_NAME=${nextVpn}; export CURRENT_VPN_NAME;
fi;
}
function isServiceRunning {
if [ -n $1 ]
then;
local service="$1";
echo "$(date) :: > > > isServiceRunning? service: $service" >> "$DEBUG_LOG_FILE_PATH";
if (( $(ps -ef | grep -v grep | grep $service | wc -l) > 0 ))
then;
echo "$(date) :: > > > YES *** $service IS running." >> "$DEBUG_LOG_FILE_PATH";
echo 1;
else;
echo "$(date) :: ~ ~ ~ NO --- $service is NOT running." >> "$DEBUG_LOG_FILE_PATH";
echo 0;
fi;
else;
>&2 echo "$(date) :: *** Usage: $0 <name_of_service>";
fi;
}
###############################################################################################
# ALIASES #
###############################################################################################
alias extip='curl -s icanhazip.com'
alias gextip='curl freegeoip.net/json/$(extip)'
alias fsvpn='forceSwitchVpnConnection'$HOME/.bash.d/.bash_parse :
#########################################################################
# >>> .bash_parse #
#-----------------------------------------------------------------------#
# bash utility functions to aid parsing input / manipulating arrays #
#########################################################################
function getElementIndex {
local e
local arr_count=0
for e in "${@:2}"
do;
if [[ "$e" == "$1" ]]
then;
echo $arr_count;
return 0;
else;
arr_count="$(( ++arr_count ))";
fi;
done;
echo "";
return 1;
}
function sanitizeArgument {
#-----------------------------------------------------------#
# NB: ${string//substring/replacement} #
# Replace all matches of $substring with $replacement. #
#-----------------------------------------------------------#
local retval
if [ "$#" -ne 1 ]
then;
return 1;
fi;
suspectArgument="$1";
cleanArgument="${suspectArgument//[^a-zA-Z0-9]/}";
echo "$cleanArgument";
return 0;
}
function testArgumentIsNumeric {
local retval;
if [ "$#" -ne 1 ]
then;
retval=1;
fi;
if [ "$1" -eq "$1" ] 2> /dev/null
then;
retval=0;
else;
retval=2;
fi;
return $retval;
}
function testArgumentIsIPv4Address {
if [ "$#" -ne 1 ]
then;
return 1;
fi;
local ip=$1;
local retval=1;
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
then;
OIFS=$IFS;
IFS='.';
ip=($ip);
IFS=$OIFS;
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]];
retval=$?;
fi;
return $retval;
}$HOME/.bash.d/.bash_geo :
#!/bin/bash
###############################################################################################
# IP UTILITY FUNCTIONS #
###############################################################################################
function validip()
{
local ip=$1;
local stat=1;
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
then;
OIFS=$IFS;
IFS='.';
ip=($ip);
IFS=$OIFS;
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]];
stat=$?;
fi;
return $stat;
}
function getWanIp() {
echo "$(curl -s icanhazip.com)";
}
###############################################################################################
# GEOCODING FUNCTIONS #
###############################################################################################
function getGeoLocFromIp()
{
local print_lat_lon
if [ "$#" -eq 1 ] && [ "$1" != "-h" ] && $(validip $1)
then;
print_lat_lon=false;
elif [ "$#" -eq 2 ] && [ "$2" = "-l" ] && $(validip $1)
then;
print_lat_lon=true;
else;
echo -e "getGeoLocFromIp (alias: glocip)\nReturns: reverse geocoded city and country of IP address\nUsage: getGeoLocFromIp <valid ipv4 address> [-h] [-l]\n -h: Print this usage stub\n -l: Additionally output latitude and longitude" >&2;
return 1;
fi;
local ip="$1";
local lat_lon=$(getLatLonFromIp ${ip});
local stat=$?;
if [ $stat -ne 0 ]; then return $stat; fi;
local city_country=$(getCityCountryFromLatLon "${lat_lon}");
local stat=$?;
if [ $stat -ne 0 ]; then return $stat; fi;
if [ "$print_lat_lon" = true ]
then;
echo "${city_country} [${lat_lon}]";
else;
echo "${city_country}";
fi;
return 0;
}
function getLatLonFromIp()
{
if [ "$#" -ne 1 ] || [ $(validip $1) ]
then;
echo -e "Usage: getLatLonFromIp <valid ipv4 address>" >&2;
return 1;
fi;
local ip="$1";
local lat_lon=$(curl -s -m 1 "freegeoip.net/json/${ip}");
if [ -z "${lat_lon}" ] || [[ "${lat_lon}" =~ Backend\ not\ available ]] || [[ ! "${lat_lon}" =~ .*latitude.* ]]
then;
echo -e "Failure to access geocoding API; either:\n - Network connectivity is compromised\n - API calls to freegeoip.net are being blocked\n - freegeoip.net have changed their API\nInvestigate." >&2;
return 1;
else;
lat_lon="$(echo ${lat_lon} | jq -r '.latitude, .longitude')";
lat_lon="$(echo ${lat_lon} | tr ' ' ',')";
fi;
echo "${lat_lon}";
return 0;
}
function getCityCountryFromLatLon()
{
local latlon="";
if [ "$#" -eq 1 ] && [ -n "$1" ]
then;
latlon="$1";
elif [ "$#" -eq 2 ] && [ -n "$1" ] && [ -n "$2" ]
then;
local lat="$1";
local lon="$2";
latlon="${lat},${lon}";
else;
echo -e "Usage: getCityCountryFromLatLon {<latitude> <longitude>|<latitude,longitude>}" >&2;
return 1;
fi;
mapfile -t city_country < <(curl -G -k --data "latlng=$latlon&sensor=false" http://maps.googleapis.com/maps/api/geocode/xml 2>/dev/null | xmlstarlet sel -t -v '/GeocodeResponse/result[1]/address_component[type="political"][type="country"]/long_name' -n -t -v '/GeocodeResponse/result[1]/address_component[type="political"][type="locality"]/long_name');
if [ "${#city_country[@]}" -lt 2 ]
then;
echo -e "Failed to parse geocoding API, either:\n - Network connectivity is compromised\n - API calls to maps.googleapis.com are being blocked\n - maps.googleapis.com has changed its API\nInvestigate." >&2;
return 1;
fi;
local country="${city_country[0]}";
local city="${city_country[1]}";
echo "${city}, ${country}";
return 0;
}
###############################################################################################
# ALIASES #
###############################################################################################
alias gloc='getGeoLocFromIp $(getExtIp)'
alias glocip='getGeoLocFromIp'
alias latlonip='getLatLonFromIp'These should all be sourced in your $HOME/.bashrc or $HOME/.bash_profile initialisation scripts, for example:
$HOME/.bashrc :
if [ -d ~/.bash.d ]
then;
. ~/.bash.d/.bash_aliases;
. ~/.bash.d/.bash_vpn;
. ~/.bash.d/.bash_parse;
. ~/.bash.d/.bash_geo;
fi;Then, running crontab -e as a regular user, the cron function in the .bash_vpn source file can be called (in this example, every 5 minutes) by inserting the following:
SHELL=/bin/bash
###############################################################
# m h dom mon dow command
#==============================================================
# very frequent (every minute or 5)
*/5 * * * * . $HOME/.bash.d/.bash_vpn; iplogcronNext, for the script to work completely, a couple of changes need to be made to your sudoers file, which should always be edited using the visudo command. Once this editor is opened, the following entries need to be inserted:
%vpnadmin ALL=(root) NOPASSWD: /usr/sbin/openvpn
%vpnadmin ALL=(ALL) NOPASSWD: /usr/sbin/service openvpn *
vpnuser ALL=(ALL) NOPASSWD: /usr/sbin/service transmission-daemon *%vpnadmin is a group that must be created - and then vpnuser is the user who will be running all these scripts, and they must be a member of the %vpnadmin group, this can be done with the following commands:
root@srv:~# adduser vpnuser
root@srv:~# addgroup vpnadmin
root@srv:~# usermod -a -G vpnadmin vpnuserWe will also need acl (Access Control List) utilities for manipulating extended attributes of /etc/openvpn and it’s contents (specifically allowing vpnuser to access them with elevated privileges). First:
root@srv:~# apt update
root@srv:~# apt install acl libaclThen:
root@srv:~# cd /etc
root@srv:~# getfacl --all-effective openvpn
# file: openvpn/
# owner: root
# group: root
user::rwx
group::r-x
other::r-x
root@srv:~# setfacl -dm group:vpnadmin:rwx /etc/openvpn
root@srv:~# setfacl -dm group:vpnadmin:rwx /run/openvpn
root@srv:~# getfacl --all-effective openvpn
# file: openvpn/
# owner: root
# group: root
user::rwx
group::r-x #effective:r-x
group:vpnadmin:rwx #effective:rwx
mask::rwx
other::r-x
default:user::rwx
default:group::r-x #effective:r-x
default:group:vpnadmin:rwx #effective:rwx
default:mask::rwx
default:other::r-xAt this point, everything is well set up. All that remains to be done is to populate your /etc/openvpn directory with a set of placename.ovpn files, where each placename maps directly to an element from the VPNLIST array declared at the top of $HOME/.bash.d/.bash_vpn:
VPNLIST=( france germany sweden switzerland luxembourg finland nl1 nl2 nl3 italy lithuania poland portugal romania spain coventry london portsmouth )So, for instance, in my /etc/openvpn directory:
dev@srv:/etc/openvpn$ ls -l
total 84
-rwxrwx--- 1 root root 2992 Sep 1 02:45 coventry.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:15 finland.ovpn
-rwxrwx--- 1 root root 3099 Apr 10 02:31 france.ovpn
-rwxrwx--- 1 root root 3110 Apr 10 02:32 germany.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:38 italy.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:38 lithuania.ovpn
-rwxrwx--- 1 root root 2992 Sep 1 02:45 london.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:15 luxembourg.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:15 nl1.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:15 nl2.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:16 nl3.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:38 poland.ovpn
-rwxrwx--- 1 root root 2992 Sep 1 02:46 portsmouth.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:39 portugal.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:39 romania.ovpn
-rwxrwx--- 1 root root 2992 Jun 25 03:39 spain.ovpn
-rwxrwx--- 1 root root 2981 Jun 8 04:51 sweden.ovpn
-rwxrwx--- 1 root root 3014 Jun 8 04:52 switzerland.ovpn
lrwxrwxrwx 1 vpnuser vpnuser 25 Sep 13 19:44 vpnx.conf -> /etc/openvpn/poland.ovpnAnd contained within each of these ovpn files are:
- auth
- auth-user-pass /path/to/vpn.credentials
Thus each ovpn file is self-contained and contains all necessary information to create the openvpn connection.
In the Linux shell, you can run:
dev@srv:~$ fsvpn portugal
> > > CHANGING VPN TO [portugal] :: index [12] > > >
New VPN Location: Lisboa, Portugal :: [IP Address: 94.1.2.3]Finally, to use the geoCoding / location capabilities, you’ll need to install jq and xmlstarlet which are used to parse the API responses:
dev@srv:~$ sudo apt update
dev@srv:~$ sudo apt install jq xmlstarletThen try out the functions by themselves:
dev@srv:~$ gloc
Stockholm, Sweden
dev@srv:~$ glocip 8.8.8.8
Mount Hope, United StatesAny questions, just drop a comment below.