Bash: VPN Config Manager

January 14, 2018

Recently hopped on the VPN train and needed a better way to manage my settings via the command-line. Cobbled together a bash script to make controlling OpenVPN a little more convenient. The script gets a list of all your configs, then lets you enable or disable individual or multiple instances of OpenVPN using the selected config file(s).

I've only used it on Ubuntu 16.04, so it relies on systemd for starting and stopping the OpenVPN service. Take a look through the file to see how it works and what settings you need to tweak. I tried to comment everything to make modifying easier.

View File:

vpn_switch.txt

  #!/bin/bash
  ## OpenVPN Config Manager
  ## Script to help manage multiple configuration files for OpenVPN
  ##
  ## Last Updated: April 23 2018
  ## – Found a different way to get external ip
  ##
  ## January 12 2018
  ## Notes/Issues/Things to fix:
  ## – Only tested on Ubuntu 16.04
  ## – Uses systemd to control services
  ## – Also uses proxychains-ng to check proxy/vpn ip
  ## but can be edited to use curl, wget, etc.
  ## – Multiple VPN shows “ready” message even though
  ## second, third, etc interfaces are still starting up
  ## – Use the option to shutdown all VPNs to start from
  ## a clean slate if it feels like nothing is happening
  ##
  ##
  ################################################
  ## Settings ##
  ################################################

  # Set directory that stores your config files
  CONFIGDIR=/etc/openvpn/

  # Change extension to .conf or .ovpn based on your setup
  CONFIGS=($CONFIGDIR*.conf)

  ################################################
  ## Colors! ##
  ################################################
  c1=’\e[0;32m’ c1b=’\e[1;32m’ #green bold
  c2=’\e[0;31m’ c2b=’\e[1;31m’ #red bold
  c3=’\e[0;34m’ c3b=’\e[1;34m’ #blue bold
  c4=’\e[0;37m’ c4b=’\e[1;37m’ #white bold
  endc=’\e[0m’ #end color

  ################################################
  ## Activate Single VPN ##
  ################################################

  # Ask user to select a config, list files or exit
  function confSelect {

  while [[ ! $USRCONF =~ ^[0-9]+$ ]] || [[ “$USRCONF” -ge ${#CONFIGS[@]} ]];
  do
  printf “${c4}Input Config Number %s\n(L)ist configs (M)ain Menu (Q)uit):${endc} ”
  read USRCONF

  if [[ “$USRCONF” =~ ^[Ll]+$ ]]
  then
  confList
  elif [[ “$USRCONF” =~ ^[Mm]+$ ]]
  then
  menuMain
  elif [[ “$USRCONF” =~ ^[Qq]+$ ]]
  then
  exit
  elif
  ! [[ “$USRCONF” =~ ^[0-9]+$ ]] && [[ ! “$USRCONF” =~ ^[Ll]+$ ]]
  then
  echo -e “${c2b}— Invalid input: numbers only —${endc}”
  elif [[ “$USRCONF” -ge ${#CONFIGS[@]} ]];
  then
  echo -e “${c2b}— Invalid input: config not found —${endc}”
  fi
  done

  echo -e “${c4b}You selected:${endc} ${c1b} ${CONFIGS[$USRCONF]##*/} ${endc}\n”
  }

  # Launch OpenVPN with the selected config
  function confRun {
  printf “${c3b}Running${endc} OpenVPN with selected config… %s\t%s\n\n”

  # Stop active instances of OpenVPN
  printf “${c2b}Stopping${endc} active process… %s\t%s\n\n”
  sudo systemctl stop openvpn
  sleep 2

  printf “${c1}Starting${endc} service using ${c1b}${CONFIGS[$USRCONF]##*/}${endc}…
  %s\t%s\n\n”

  # Filter out file extension for use in openvpn@ command
  CONFSEL=${CONFIGS[$USRCONF]##*/}

  # Start OpenVPN service using the selected config file
  sudo systemctl start openvpn@${CONFSEL%.*}

  }

  ################################################
  ## Activate Multiple VPNs ##
  ################################################

  # Ask user to select multiple configs, list files or exit
  function confMultiSel {

  while [[ ! “$usrinput” == “D” ]]
  do
  printf “${c4}Input Config Number (press enter after each input) %s\n(L)ist configs (D)one Adding Configs (R)eset selection (M)ain menu (Q)uit):${endc} ”
  read usrinput
  # Check if user wants to list configs, quit, or reset selection
  # Then validate input for numbers or valid configs
  if [[ “$usrinput” =~ ^[Ll]+$ ]]
  then
  confList
  elif [[ “$usrinput” =~ ^[Qq]+$ ]]
  then
  exit
  elif [[ “$usrinput” =~ ^[Mm]+$ ]]
  then
  menuMain
  elif [[ “$usrinput” =~ ^[Rr]+$ ]]
  then
  unset ‘USRCONF[@]’
  elif ! [[ “$usrinput” =~ ^[0-9]+$ ]] && [[ ! “$usrinput” =~ ^[Dd]+$ ]]
  then
  echo -e “${c2b}— Invalid input: numbers only —${endc}”
  elif [[ “$usrinput” -ge “${#CONFIGS[@]}” ]]
  then
  echo -e “${c2b}— Invalid input: config not found —${endc}”
  else
  USRCONF+=($usrinput)
  fi

  done
  # Remove end command (D) from array, leaving only selected conigs
  unset ‘USRCONF[-1]’

  # Output list of selected configs
  printf “%s\n${c4b}You selected the following configs:${endc} ”
  for x in “${USRCONF[@]}”
  do
  printf “${c1b}${CONFIGS[$x]##*/}${endc} ”
  CONFSEL+=”${CONFIGS[$x]##*/} ”
  done
  printf “\n”

  }

  # Launch OpenVPN using the selected configs
  function confMultiRun {
  printf “${c3b}Running${endc} OpenVPN using selected configs… %s\t%s\n\n”

  # Stop active instances of OpenVPN
  printf “${c2b}Stopping${endc} active processes… %s\t%s\n\n”
  sudo systemctl stop openvpn
  sleep 2

  printf “${c1}Starting${endc} service using ${c1b}${CONFSEL}${endc}…
  %s\t%s\n\n”

  # Filter out file extension for use in openvpn@ command
  #CONFSEL=${CONFIGS[$USRCONF]##*/}

  # Start OpenVPN services using the selected config files
  # Edit filename to remove path, then remove extension
  for z in “${USRCONF[@]}”
  do
  CONFSEL=”${CONFIGS[$z]##*/}”
  SELMOD=${CONFSEL%.*}
  sudo systemctl start openvpn@${SELMOD%.*}
  done
  }

  ################################################
  ## Loading Feedback ##
  ################################################
  # Display feedack while waiting for network adapter(s) to activate
  function adapterLoading {
  spin=’-\|/’
  sp=0

  while ! (ip link | grep ‘tun\|tap’ > /dev/null)
  do
  sp=$(( (sp+1) %4 ))
  printf “\r[${spin:$sp:1}] Waiting for network adapter(s)…”
  sleep .1
  done
  printf “%s\n\n${c1b}VPN is ready!${endc}%s\n”
  }

  ################################################
  ## Disable Individual VPN ##
  ################################################

  # Display list of running vpn instances and configs
  function vpnList {
  # Get list of running openvpn processes and add to array
  VPNIDS=( $(ps -fe | grep [o]penvpn | awk ‘{print $2}’) )

  # Get info on configs used on running processes
  for o in “${!VPNIDS[@]}”
  do
  VPNCFG+=( $(cat /proc/${VPNIDS[$o]}/cmdline | strings -1 | grep $CONFIGDIR\.*conf) )
  done

  # Print active vpn list for user to choose from
  printf “${c4b}[#] %s\t[PID] %s\t[Config]${endc} %s\n”
  for p in “${!VPNIDS[@]}”;
  do
  printf “%s\t%s\t%s\n” “[$p]” “${VPNIDS[$p]}” “${VPNCFG[$p]}”
  done
  }

  # Get user input for which service to disable
  function vpnOffSelect {

  while [[ ! “$vpnInput” =~ ^[0-9]+$ ]]
  do
  printf “${c4}Input VPN Number to disable %s\n(L)ist active vpn (M)ain menu (Q)uit):${endc} ”
  read vpnInput
  # Check if user wants to list processes, go to main menu or quit
  # Then validate input for numbers or valid running configs
  if [[ “$vpnInput” =~ ^[Ll]+$ ]]
  then
  vpnList
  elif [[ “$vpnInput” =~ ^[Qq]+$ ]]
  then
  exit
  elif [[ “$vpnInput” =~ ^[Mm]+$ ]]
  then
  menuMain
  elif ! [[ “$vpnInput” =~ ^[0-9]+$ ]] && [[ ! “$vpnInput” =~ ^[Ll]+$ ]]
  then
  echo -e “${c2b}— Invalid input: numbers only —${endc}”
  elif [[ “$vpnInput” -ge “${#VPNIDS[@]}” ]]
  then
  echo -e “${c2b}— Invalid input: config not found —${endc}”
  fi
  done
  }

  # Disable the service selected by the user
  function vpnOffRun {
  printf “${c2b}Stopping${endc} service running ${c1b}${VPNCFG[$vpnInput]##*/}${endc}…
  %s\t%s\n\n”

  # Filter out file extension for use in openvpn@ command
  STOPSEL=${VPNCFG[$vpnInput]##*/}

  # Stop service using the selected config file
  sudo systemctl stop openvpn@${STOPSEL%.*}
  }

  ################################################
  ## Disable All VPN ##
  ################################################
  function vpnAllOff {
  # Stop active instances of OpenVPN
  printf “${c2b}Stopping${endc} active processes… %s\t%s\n\n”
  sudo systemctl stop openvpn
  }

  ################################################
  ## Show Adapter Status ##
  ################################################
  function adapterStatus {
  printf “${c1b}Running OpenVPN processes${endc}%s\n”
  vpnList

  printf “${c1b}Default IP Address : ${endc}”
  ##curl v4.ifconfig.co
  curl -s checkip.dyndns.org | sed -e ‘s/.*Current IP Address: //’ -e ‘s/<.*$//'
  printf "${c1b}VPN/Proxy IP Address: ${endc}"
  ##proxychains4 -q curl v4.ifconfig.co proxychains4 -q curl -s checkip.dyndns.org | sed -e 's/.*Current IP Address: //' -e 's/<.*$//' } ################################################
  ## confList ##
  ################################################
  # Output list of configuration files as an array
  function confList {
  echo "+--------------------------------------+"
  echo "| Config List                          |"
  echo "+--------------------------------------+"
  printf "${c4b}[${#CONFIGS[@]} Available Configuration Files] ${endc}%s\n"
  printf "${c4b}[#]%-2s[Filename]${endc} %s\n"
  for i in "${!CONFIGS[@]}";
  do
  printf "%-4s %s\n" "[$i]" "${CONFIGS[$i]##*/}"
  done | column
  printf "%s\n" }

  ################################################
  ## Menus ##
  ################################################

  # Main Menu

  function menuMainDisplay {
  # Obligatory ascii art
  clear
  echo "+-----------------------------+"
  echo "| ██╗ ██╗██████╗ ███╗ ██╗ |"
  echo "| ██║ ██║██╔══██╗████╗ ██║ |"
  echo "| ██║ ██║██████╔╝██╔██╗ ██║ |"
  echo "| ╚██╗ ██╔╝██╔═══╝ ██║╚██╗██║ |"
  echo "| ╚████╔╝ ██║ ██║ ╚████║ |"
  echo "| ╚═══╝ ╚═╝ ╚═╝ ╚═══╝ |"
  echo "+-----------------------------+"
  echo "| Config Manager |"
  echo "+-----------------------------+"
  echo "| [1] Activate Single VPN |"
  echo "| [2] Activate Multiple VPNs |"
  echo "| [3] Disable Individual VPN |"
  echo "| [4] Disable All VPN |"
  echo "| [5] Show Adapter Status |"
  echo "| [Q] Exit |"
  echo "+-----------------------------+" }
  function menuMainSel {
  local menuSel
  read -p "Enter menu selection: " menuSel
  case $menuSel in
  1) menuActivateSingle;;
  2) menuActivateMulti;;
  3) menuDisableSingle;;
  4) menuDisableAll;;
  5) adapterStatus;;
  Q) exit 0 ;;
  q) exit 0 ;; *)
  echo -e "${c2b}Input error...${endc}"
  esac
  }

  # Menu Functions
  function menuActivateSingle {
  confList confSelect confRun adapterLoading
  }
  function menuActivateMulti {
  onfList confMultiSel confMultiRun adapterLoading
  }
  function menuDisableAll {
  vpnAllOff
  }
  function menuDisableSingle {
  vpnOffSelect
  vpnOffRun
  }
  function menuMain {
  menuMainDisplay
  menuMainSel
  }
  # Functions to run on start up
  menuMainDisplay
  menuMainSel