diff --git a/cvpn b/cvpn index cbb4f20..21a4c35 100755 --- a/cvpn +++ b/cvpn @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ${HOME}/code/bashtools/bashtools.sh if [[ -z $HAVE_BASHTOOLS ]]; then @@ -7,8 +7,13 @@ if [[ -z $HAVE_BASHTOOLS ]]; then fi CONFDIR=${HOME}/.vpn LOGFILE=${CONFDIR}/log +VPNPIDFILE=${CONFDIR}/f5pid DEFAULTCONF=${CONFDIR}/config CONFFILE=${DEFAULTCONF} +VALID_VENDORS="cisco|f5" + +cp -p "$LOGFILE" "${LOGFILE}.0" +cp /dev/null "$LOGFILE" VPN=/opt/cisco/anyconnect/bin/vpn @@ -40,9 +45,6 @@ function load_config() { DEFAULTPROFILE="" if [[ -e ${CONFFILE} ]]; then DEFAULTPROFILE=$(grep '^#default:' "${CONFFILE}" | sed 's/^#default://') - if [[ -n ${DEFAULTPROFILE} ]]; then - inform "[default VPN: ^b$DEFAULTPROFILE^p]" - fi CONFLINES=$(egrep -v "^#" ${CONFFILE} 2>/dev/null | awk NF) nvpns=0 while read line; do @@ -51,12 +53,23 @@ function load_config() { thisuser=$(cut -d, -f2 <<< "${line}") thispw=$(cut -d, -f3 <<< "${line}") thisgroup=$(cut -d, -f4 <<< "${line}") + thisvendor=$(cut -d, -f5 <<< "${line}") + thisserver=$(cut -d, -f6 <<< "${line}") + [[ -z $thisvendor ]] && thisvendor="cisco" + [[ -z $thisserver ]] && thisvendor="n/a" + if [[ ! $thisvendor =~ $VALID_VENDORS ]]; then + error "invalid VPN vendor '$thisvendor', must be $VALID_VENDORS" + cecho -s "$RED" "^bBad line:^p $line" + exit 1 + fi profile[$nvpns]="${thisprofile}" user[$nvpns]="${thisuser}" pw[$nvpns]="${thispw}" group[$nvpns]="${thisgroup}" + vendor[$nvpns]="${thisvendor}" + server[$nvpns]="${thisserver}" nvpns=$((nvpns + 1)) - done <<< ${CONFLINES} + done <<< "${CONFLINES}" else error "Config file '$CONFFILE' not found" return 1 @@ -74,11 +87,14 @@ function usage() { echo " -c file Use selected config file (default is ${DEFAULTCONF})" echo echo "Config file format:" + echo " #Specify default profile like this:" echo " #default:myvpn2" - echo " myvpn1,username_1,password_1,vpngroup_1" - echo " myvpn2,username_2,password_2,vpngroup_2" - echo " # This one has no 'group'" - echo " myvpn3,username_3,password_3" + echo " #Profile,Username,Password,VPNGroup,VPNType,ServerIP" + echo " # Cisco VPNs have just user 'server' for status, and group is optional" + echo " myvpn1,username_1,password_1,vpngroup_1,cisco,3.3.3.3" + echo " myvpn2,username_2,password_2,vpngroup_2,cisco,1.1.1.1" + echo " # F5 VPNs must have a 'server'" + echo " myvpn3,username_3,password_3,,f5,1.2.3.4" echo } @@ -96,6 +112,49 @@ function get_profile_id() { #1=profile_to_check return 1 } +function get_vpn_status() { # populates vprofile vuser vstatus vserver vvendor, return 0 if vpn up + local rv=0 fpid pname + vprofile="" + vuser="" + vstatus="Disconnected" + vserver="" + vvendor="" + + if [[ -e $VPNPIDFILE ]]; then + fpid=$(cat $VPNPIDFILE 2>/dev/null) + pname=$(ps -p $fpid -o command="" 2>/dev/null) + if [[ $pname =~ gof5 ]]; then + vstatus="Connected" + vserver=$(echo "$pname" | sed 's/^.*--server //;s/ .*//g') + vuser=$(echo "$pname" | sed 's/^.*--username //;s/ .*//g') + vvendor=F5 + fi + else + # no f5 vpn, check anyconnect + res=$(${VPN} stats 2>&1) + if [[ $? -eq 0 ]]; then + vstatus=$(grep 'Connection State:' <<< "$res" | grep -v Manage | awk '{ print $3 }' ) + if [[ $vstatus == "Connected" ]]; then + vvendor="Cisco" + vserver=$(grep 'Server Address:' <<< "$res" | awk '{ print $3 }' ) + fi + else + rv=1 + fi + fi + + if [[ $vstatus == "Connected" ]]; then + # find matching profile + for x in ${!profile[@]}; do + if [[ $vserver == ${server[$x]} ]]; then + vprofile="${profile[$x]}" + [[ -z $vuser ]] && vuser=${user[$x]} + [[ -z $vvendor ]] && vvendor=${vvendor[$x]} + fi + done + fi + return $rv +} PROFILE="" @@ -126,20 +185,47 @@ fi cmd=$(alias_to_cmd "$1") load_config || exit 1 -PROFILE="$DEFAULTPROFILE" + +usingdefault=0 +PROFILE="$2" +if [[ -z $PROFILE ]]; then + if [[ -n ${DEFAULTPROFILE} ]]; then + PROFILE="${DEFAULTPROFILE}" + usingdefault=1 + fi +fi + + +pgrep -q gof5 && f5up=1 || f5up=0 if [[ $cmd == "list" ]]; then - F="%s,%s,%s,%s\n" - ( - printf "${F}" "Profile" "Username" "Password" "Group" - for x in ${!profile[@]}; do - printf "${F}" "${profile[$x]}" "${user[$x]}" "${pw[$x]}" "${group[$x]:-(n/a)}" - done - ) | column -s, -t + F="%s,%s,%s,%s,%s,%s\n" + table=$( + ( + printf "${F}" "Profile" "Username" "Password" "Group" "Server" "Type" + for x in ${!profile[@]}; do + p="${pw[$x]:0:2}....${pw[$x]: (-2)}" + prof="${profile[$x]}" + [[ $prof == $DEFAULTPROFILE ]] && prof="*${prof}" + printf "${F}" "${prof}" "${user[$x]}" "${p}" "${group[$x]:-(n/a)}" "${server[$x]:-(n/a)}" "${vendor[$x]}" + done + ) | column -s, -t + ) + table=$(echo "$table" | sed "s/^\(Profile.*\)$/^b^u\1^p/;s/\(n\/a\)/^i\1^p/g") + csecho "$WHITE" "$table" + echo + echo "(* = default VPN)" exit 0 elif [[ $cmd == "on" ]]; then + [[ $usingdefault -eq 1 ]] && inform "[using default VPN: ^b$DEFAULTPROFILE^p]" + get_vpn_status + if [[ $vstatus == "Connected" ]]; then + error "VPN profile $vprofile is already connected" + exit 1 + fi + if [[ -z ${PROFILE} ]]; then - error "No default VPN is set." + error "No VPN profile provided and no default VPN profile set." csecho "$RED" "Either specify on commandline, or add this to ^b${CONFFILE}^p:" csecho "$RED" "#default:^ivpn_name^p" exit 1 @@ -149,6 +235,12 @@ elif [[ $cmd == "on" ]]; then curuser=${user[$id]} curpw=${pw[$id]} curgroup=${group[$id]} + curvendor=${vendor[$id]} + if [[ $curvendor == "f5" ]]; then + curserver=${server[$id]} + else + curserver="" + fi else exit 1 fi @@ -156,49 +248,102 @@ elif [[ $cmd == "on" ]]; then nstr="Connecting to ^b${PROFILE}^p as user ^b${curuser}^p" [[ -n $curgroup ]] && nstr="${nstr} group ${curgroup}" notify "${nstr}" - if [[ -n $curgroup ]]; then - answers="y\n${curgroup}\n${curuser}\n${curpw}\n" + + if [[ $curvendor == "f5" ]]; then + rm -f "$VPNPIDFILE" + nohup sudo gof5 --server "${curserver}" --username "${curuser}" --password "${curpw}" >>${LOGFILE} 2>&1 & + f5pid=$! + nlines=0 + res="" + while IFS= read -r line || [[ -n "$line" ]]; do + nlines=$((nlines + 1)) + if [[ ${line,,} =~ stablished ]]; then + rv=0 + res="$line" + break + elif [[ ${line,,} =~ failed ]]; then + rv=1; + res="$line" + break; + elif [[ ${nlines} -ge 30 ]]; then + rv=4; + res="$line" + break; + fi + ps -p $f5pid >/dev/null 2>&1 + if [[ $? -ne 0 ]]; then + rv=2; + res="$line" + break; + fi + done < <(timeout 300 tail -f ${LOGFILE} ) + + if [[ -z $res ]]; then + rv=3; + res="$(tail -n 10 ${LOGFILE})" + fi + if [[ $rv -eq 0 ]]; then + echo "$f5pid" > "$VPNPIDFILE" + fi else - answers="y\n${curuser}\n${curpw}\n" + if [[ -n $curgroup ]]; then + answers="y\n${curgroup}\n${curuser}\n${curpw}\n" + else + answers="y\n${curuser}\n${curpw}\n" + fi + res=$(echo -e "$answers" | ${VPN} -s connect "$PROFILE" 2>&1 | tee ${LOGFILE}) + grep -q 'state: Connected' <<< "$res$" + rv=$? fi - res=$(echo -e "$answers" | ${VPN} -s connect "$PROFILE" 2>&1 | tee ${LOGFILE}) - grep -q 'state: Connected' <<< "$res$" - if [[ $? -eq 0 ]]; then + if [[ $rv -eq 0 ]]; then ok "connected" else fail - error "Connection failed, logs are in ^b${LOGFILE}^p." + if [[ $curvendor == "cisco" ]]; then + error "Connection failed, logs are in ^b${LOGFILE}^p." + else + error "Connection failed, logs are below:" + cecho -s "$RED" "$res" | sed 's/^/ /' + fi fi elif [[ $cmd == "status" ]]; then + BASECOL="$ORANGE" notify "Checking VPN status" - res=$(${VPN} stats 2>&1) - if [[ $? -eq 0 ]]; then - ok -echo "$res" > z - status=$(grep 'Connection State:' <<< "$res" | grep -v Manage | awk '{ print $3 }' ) - [[ -z $status ]] && status="Unknown" - if [[ $status == "Connected" ]]; then - col="$GREEN" - dst=$(grep 'Server Address:' <<< "$res" | awk '{ print $3 }' ) - else - col="$RED" - dst="" - fi - BASECOL="$ORANGE" - csecho -n "$BASECOL" "VPN status: ${col}${BOLD}${status}^p" - [[ -n $dst ]] && csecho "$BASECOL" " to ${dst}" || echo + get_vpn_status + [[ $? -eq 0 ]] && ok || fail + if [[ $vstatus == "Connected" ]]; then + col="$GREEN" else - fail - error "Status check failed." + col="$RED" + fi + csecho -n "$BASECOL" "VPN status: ${col}${BOLD}${vstatus}^p" + if [[ ${vstatus,,} =~ "connected" ]]; then + [[ -n $vserver ]] && csecho "$BASECOL" " to ^b${vprofile}^p (${vvendor}:${vuser}@${vserver})" || echo fi elif [[ $cmd == "off" ]]; then - notify "Disconnecting from VPN" - ${VPN} disconnect > ${LOGFILE} 2>&1 - if [[ $? -eq 0 ]]; then - ok + rv=1 + get_vpn_status + if [[ $vstatus == "Connected" ]]; then + notify "Disconnecting from ${vvendor} VPN ^b${vprofile}^p" + + if [[ ${vvendor,,} == "f5" ]]; then + #sudo gof5 --server ${s} --close-session + sudo kill $(cat $VPNPIDFILE) + rm -f "${VPNPIDFILE}" + rv=0 + else + ${VPN} disconnect > ${LOGFILE} 2>&1 + rv=$? + fi + if [[ $rv -eq 0 ]]; then + ok + else + fail + error "Connection failed, logs are in ^b${LOGFILE}^p." + fi else fail - error "Connection failed, logs are in ^b${LOGFILE}^p." + error "All VPNs are already disconnected" fi else usage