#!/usr/bin/env bash # ----- # - You need the the following sudo permissions (visudo) for that user, # - who starts this script # - # - chris ALL=(root)NOPASSWD:/usr/sbin/openvpn # - chris ALL=(root)NOPASSWD:/bin/rm -f /var/run/* # - chris ALL=(root)NOPASSWD:/bin/kill # ----- script_name="$(basename $(realpath $0))" working_dir="$(dirname $(realpath $0))" log_file="$(mktemp)" conf_file="${working_dir}/conf/${script_name%%.*}.conf" DEFAULT_BASE_OPENVPN_CLIENT_DIR="/etc/openvpn/client-confs" DEFAULT_VPN_User="chris" VPN_Network="" declare -a openvpn_connection_arr=() # ---------- # Base Function(s) # ---------- clean_up() { # - Terminate connection # - if ps -p "$_pid" > /dev/null 2>&1; then sudo kill $_pid > /dev/null 2>&1 fi # - Remove PID file # - sudo rm -f "$PID_FILE" if $terminal ; then echo -en "\033[1G" if [[ "$1" -gt 0 ]]; then blank_line echo -e " \033[41m Connection to \033[1m${VPN_Network}\033[m\033[41m was terminated! \033[m" else echononl "Terminate VPN connection to \033[32m\033[1m${VPN_Network}\033[m" echo_done fi blank_line fi rm -f $log_file exit $1 } echononl(){ if $terminal ; then echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n " $*\\c" 1>&2 else echo -e -n " $*" 1>&2 fi rm /tmp/shprompt$$ fi } fatal(){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mFatal\033[m ] $*" else echo -e " [ Fatal ] $*" fi echo "" if $terminal ; then echo -e " \033[1mScript terminated\033[m.." else echo -e " Script terminated.." fi echo "" rm -f $log_file exit 1 } error (){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mError\033[m ] $*" else echo " [ Error ] $*" fi echo "" } warn (){ if $terminal ; then echo "" if $terminal ; then echo -e " [ \033[33m\033[1mWarn\033[m ] $*" else echo " [ Warn ] $*" fi echo "" fi } echo_done() { if $terminal ; then echo -e "\033[75G[ \033[32mdone\033[m ]" fi } echo_failed(){ if $terminal ; then echo -e "\033[75G[ \033[1;31mfailed\033[m ]" fi } echo_wait(){ if $terminal ; then echo -en "\033[75G[ \033[5m\033[1m..\033[m ]" fi } echo_skipped() { if $terminal ; then echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" fi } blank_line() { if $terminal ; then echo "" fi } is_valid_ipv4() { local -a octets=( ${1//\./ } ) local RETURNVALUE=0 # return an error if the IP doesn't have exactly 4 octets [[ ${#octets[@]} -ne 4 ]] && return 1 for octet in ${octets[@]} do if [[ ${octet} =~ ^[0-9]{1,3}$ ]] then # shift number by 8 bits, anything larger than 255 will be > 0 ((RETURNVALUE += octet>>8 )) else # octet wasn't numeric, return error return 1 fi done return ${RETURNVALUE} } # - Test if given argument is a valid IPv6 Address # - is_valid_ipv6() { local _ipv6="$1" _regex_ipv6='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$' if [[ "$_ipv6" =~ ${_regex_ipv6} ]]; then return 0 else return 1 fi } is_number() { return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1); # - also possible # - #[[ ! -z "${1##*[!0-9]*}" ]] && return 0 || return 1 #return $([[ ! -z "${1##*[!0-9]*}" ]]) } is_int() { return $(test "$@" -eq "$@" > /dev/null 2>&1); } # ---------- # - Jobhandling # ---------- # - Run 'clean_up' for signals SIGHUP SIGINT SIGTERM # - trap clean_up SIGHUP SIGINT SIGTERM # ---------- # - Some checks .. # ---------- # - Running in a terminal? # - if [[ -t 1 ]] ; then terminal=true else terminal=false fi [[ -n "$BASE_OPENVPN_CLIENT_DIR" ]] || BASE_OPENVPN_CLIENT_DIR="$DEFAULT_BASE_OPENVPN_CLIENT_DIR" [[ -n "$VPN_User" ]] || VPN_User="$DEFAULT_VPN_User" # ---------- # - Headline # ---------- if $terminal ; then echo "" echo -e "\033[1m----------\033[m" echo -e "\033[32m\033[1mRunning script \033[m\033[1m$script_name\033[32m .. \033[m" echo -e "\033[1m----------\033[m" fi blank_line echononl "Load Configuration File $(basename ${conf_file}).." if [[ -f "$conf_file" ]]; then source "$conf_file" > $log_file 2>&1 if [[ $? -eq 0 ]]; then echo_done else echo_failed fatal "$(cat $log_file)" fi else echo_skipped fi # ---------- # - Main part of script # ---------- if $terminal ; then # - Get available OpenVPN connections # - while IFS='' read -r -d '' _dir_name ; do [[ "$_dir_name" = ".." ]] && continue [[ "$_dir_name" = "." ]] && continue openvpn_connection_arr+=("$(basename "$_dir_name")") done < <(find "$BASE_OPENVPN_CLIENT_DIR" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) echo "" echo -e " \033[1mWhich Configuration should be loaded?\033[m" echo "" for i in ${!openvpn_connection_arr[@]}; do echo -e " [$i]\033[9G${openvpn_connection_arr[$i]}" done echo "" echo -e " [99]\033[9G\033[33mExit programm\033[m" echo "" echononl "Choose: " _OK=false while ! $_OK ; do read _IN if is_number "$_IN" && [[ -n ${openvpn_connection_arr[$_IN]} ]]; then VPN_Network="${openvpn_connection_arr[$_IN]}" _OK=true elif is_number "$_IN" && [[ $_IN -eq 99 ]] ;then echo"" exit 0 else echo "" echo -e "\tWrong value !" echo "" echononl "Choose again: " fi done fi [[ -n "$VPN_Network" ]] || fatal "No VPN Network was given." PID_FILE="/var/run/${VPN_Network}" ovpn_config_file="$(realpath $(ls ${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/client-*.conf 2>/dev/null) 2> /dev/null )" if [[ ! -f "$ovpn_config_file" ]] ; then fatal "Configurationfile for ${VPN_Network}' not found." fi # - Get hostname and port # - _hostname="$(grep -E "^\s*remote\s+" $ovpn_config_file | awk '{print$2}')" if grep -q -E "^\s*rport\s+" $ovpn_config_file 2> /dev/null ; then remote_port="$(grep -E "^\s*rport\s+" $ovpn_config_file | awk '{print$2}')" else remote_port="$(grep -E "^\s*remote\s+" $ovpn_config_file | awk '{print$3}')" fi if is_valid_ipv4 $_hostname || is_valid_ipv6 $_hostname ; then remote_address="$_hostname" else # - We need '| tail -1' becaus output prints at first line the # - the CNAME of given hostname. remote_address="$(dig +short $_hostname | tail -1)" fi if [[ -f "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" ]]; then OVPN_ASKPASS="${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" elif [[ -f "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/passwd.txt" ]] ; then ovpn_pass="$(grep -E "passw" "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/passwd.txt" | awk '{print$2}')" cat < "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" $ovpn_pass EOF chmod 600 "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" OVPN_ASKPASS="${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" elif [[ -f "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/password.txt" ]] ; then ovpn_pass="$(grep -E "passw" "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/password.txt" | awk '{print$2}')" cat < "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" $ovpn_pass EOF OVPN_ASKPASS="${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" else OVPN_ASKPASS="/root/.openvpn/${VPN_Network}" fi echo "" echo "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/ovpn_pass" echo "${BASE_OPENVPN_CLIENT_DIR}/${VPN_Network}/password.txt" echo "" echo "" blank_line echononl "Establish VPN connection to \033[32m\033[1m${VPN_Network}\033[m" echo_wait sudo /usr/sbin/openvpn \ --config $ovpn_config_file \ --proto udp4 \ --remote $remote_address $remote_port \ --writepid "$PID_FILE" \ --log /var/log/openvpn/${VPN_Network}.log \ --status /var/log/openvpn/status-${VPN_Network}.log \ --askpass "$OVPN_ASKPASS" > $log_file 2>&1 & _pid="$!" declare -i time=0 if ps -p "$_pid" > /dev/null 2>&1; then while ! ip a | grep -q -E "tun[0-9]{1,2}" 2> /dev/null && [[ $time -lt 60 ]] ; do sleep 1 ((time++)) done if [[ $time -ge 60 ]]; then echo_failed clean_up 1 else echo_done fi else echo_failed [[ -s "$log_file" ]] && error "$(cat $log_file)" clean_up 1 fi #while ps -p "$_pid" > /dev/null 2>&1 ; do while ip a | grep -q -E "tun[0-9]{1,2}" 2> /dev/null ; do sleep 3 done clean_up 0