Rewritten to support connecting to multiple vpns simultaneously.

This commit is contained in:
Rob Pearce 2023-10-13 20:38:55 +11:00
parent 839244cd61
commit 3852304c59
1 changed files with 154 additions and 83 deletions

177
cvpn
View File

@ -70,6 +70,7 @@ function load_config() {
thisroutes=$(cut -d, -f7 <<< "${line}") thisroutes=$(cut -d, -f7 <<< "${line}")
thisdomains=$(cut -d, -f8 <<< "${line}") thisdomains=$(cut -d, -f8 <<< "${line}")
thisservercert=$(cut -d, -f9 <<< "${line}") thisservercert=$(cut -d, -f9 <<< "${line}")
thispidfile=${CONFDIR}/${thisprofile// /}.pid
case $thisvpntype in case $thisvpntype in
"") thisvpntype="anyconnect";; "") thisvpntype="anyconnect";;
"cisco") thisvpntype="anyconnect";; "cisco") thisvpntype="anyconnect";;
@ -90,6 +91,7 @@ function load_config() {
routes[$nvpns]="${thisroutes}" routes[$nvpns]="${thisroutes}"
domains[$nvpns]="${thisdomains}" domains[$nvpns]="${thisdomains}"
servercert[$nvpns]="${thisservercert}" servercert[$nvpns]="${thisservercert}"
pidfile[$nvpns]="${thispidfile}"
nvpns=$((nvpns + 1)) nvpns=$((nvpns + 1))
done <<< "${CONFLINES}" done <<< "${CONFLINES}"
else else
@ -132,36 +134,51 @@ function get_profile_id() { #1=profile_to_check
return 1 return 1
} }
function get_vpn_status() { # populates vprofile vuser vstatus vserver vvpntype, return 0 if vpn up function vpnisup() { # 1=profile
local rv=0 vpnpid pname local idx pf rv pid
vprofile="" idx=$(get_profile_id "$1")
vuser="" rv=$?
vstatus="Disconnected" if [[ $rv -ne 0 || -z $idx ]]; then
vserver="" return 1
vvpntype=""
if [[ -e $VPNPIDFILE ]]; then
vpnpid=$(cat $VPNPIDFILE 2>/dev/null)
pname=$(ps -p $vpnpid -o command="" 2>/dev/null)
vstatus="Connected"
vserver=$(echo "$pname" | awk '{ print $NF }')
vuser=$(echo "$pname" | sed 's/^.*-u //;s/ .*//g')
vvpntype=$(echo "$pname" | sed 's/^.*--protocol=//;s/ .*//g')
else
rv=1
fi fi
pf=${pidfile[$idx]}
if [[ -n $pf && -e $pf ]]; then
pid=$(cat $pf)
ps -p $pid >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
return 0
else
return 1
fi
fi
return 1
}
function get_vpn_status() { # populates vprofile[] vpid[] vuser[] vstatus[] vserver[] vvpntype[] nup, return true if any vpns are up
local rv=0 vpnpid pname x
unset vprofile
unset vpnpid
unset vstatus
unset vserver
unset vuser
unset vvpntype
nup=0
if [[ $vstatus == "Connected" ]]; then
# find matching profile
for x in ${!profile[@]}; do for x in ${!profile[@]}; do
if [[ $vserver == ${server[$x]} ]]; then vpnisup "${profile[$x]}"
vprofile="${profile[$x]}" if [[ $? -eq 0 ]]; then
[[ -z $vuser ]] && vuser=${user[$x]} vprofile+=(${profile[$x]})
[[ -z $vvpntype ]] && vvpntype=${vvpntype[$x]} vpid+=($(cat ${pidfile[$x]}))
vstatus+=("Connected")
vserver+=(${server[$x]})
vuser+=(${user[$x]})
vvpntype+=(${vpntype[$x]})
nup=$((nup + 1))
fi fi
done done
fi [[ $nup -eq 0 ]] && return 1
return $rv
return 0
} }
PROFILE="" PROFILE=""
@ -196,7 +213,7 @@ load_config || exit 1
usingdefault=0 usingdefault=0
PROFILE="$2" PROFILE="$2"
if [[ -z $PROFILE ]]; then if [[ -z $PROFILE && $cmd == "up" ]]; then
if [[ -n ${DEFAULTPROFILE} ]]; then if [[ -n ${DEFAULTPROFILE} ]]; then
PROFILE="${DEFAULTPROFILE}" PROFILE="${DEFAULTPROFILE}"
usingdefault=1 usingdefault=1
@ -204,28 +221,56 @@ if [[ -z $PROFILE ]]; then
fi fi
if [[ $cmd == "list" ]]; then if [[ $cmd == "list" ]]; then
F="%s,%s,%s,%s,%s,%s\n" F="%s,%s,%s,%s,%s,%s,%s,%s\n"
table=$( table=$(
( (
printf "${F}" "Profile" "Username" "Password" "Group" "Server" "Type" printf "${F}" "Profile" "Up?" "Username" "Password" "Group" "Server" "Type" "Scope"
for x in ${!profile[@]}; do for x in ${!profile[@]}; do
p="${pw[$x]:0:2}....${pw[$x]: (-2)}" p="${pw[$x]:0:2}....${pw[$x]: (-2)}"
prof="${profile[$x]}" prof="${profile[$x]}"
[[ $prof == $DEFAULTPROFILE ]] && prof="*${prof}" [[ $prof == $DEFAULTPROFILE ]] && prof="${prof}*"
printf "${F}" "${prof}" "${user[$x]}" "${p}" "${group[$x]:-(n/a)}" "${server[$x]:-(n/a)}" "${vpntype[$x]}" vpnisup "${prof}"
if [[ $? -eq 0 ]]; then
yn="Yes"
c="${GREEN}"
else
yn="No"
c="${GREY}"
fi
scope="${domains[$x]} ${routes[$x]}"
vt=${vpntype[$x]}
[[ ${vt,,} == "anyconnect" ]] && vt=Cisco
printf "${F}" "${prof}" "$yn" "${user[$x]}" "${p}" "${group[$x]:-(n/a)}" "${server[$x]:-(n/a)}" "${vt}" "${scope%% *}"
# show one route/domain per row
scope_a=( ${scope} )
#echo "${profile[$x]} scope: [${scope}]" >&2
#echo "${profile[$x]} scope_a: [${scope_a[@]}]" >&2
if [[ ${#scope_a[@]} -ge 2 ]]; then
i=1 # skip first one
while [[ $i -lt ${#scope_a[@]} ]]; do
printf "${F}" " " " " " " " " " " " " " " "${scope_a[$i]}"
i=$((i + 1))
done
fi
done done
) | column -s, -t ) | column -s, -t
) )
table=$(echo "$table" | sed "s/^\(Profile.*\)$/^b^u\1^p/;s/\(n\/a\)/^i\1^p/g")
# underline heading
table=$(echo "$table" | sed "s/^\(Profile.*\)$/^b^u\1^p/;")
table=$(awk -v G="$GREEN" -v P="$PLAIN" '($2 == "Yes") { printf(G); } ($2 == "No") { printf(P); } { print }' <<<"$table")
csecho "$WHITE" "$table" csecho "$WHITE" "$table"
echo echo
echo "(* = default VPN)" echo "(* = default VPN)"
exit 0 exit 0
elif [[ $cmd == "on" ]]; then elif [[ $cmd == "on" ]]; then
[[ $usingdefault -eq 1 ]] && inform "[using default VPN: ^b$DEFAULTPROFILE^p]" [[ $usingdefault -eq 1 ]] && inform "[using default VPN: ^b$DEFAULTPROFILE^p]"
get_vpn_status vpnisup "${PROFILE}"
if [[ $vstatus == "Connected" ]]; then if [[ $? -eq 0 ]]; then
error "VPN profile $vprofile is already connected" error "VPN profile $PROFILE is already connected"
exit 1 exit 1
fi fi
@ -246,6 +291,7 @@ elif [[ $cmd == "on" ]]; then
curservercert=${servercert[$id]} curservercert=${servercert[$id]}
curroutes=${routes[$id]} curroutes=${routes[$id]}
curdomains=${domains[$id]} curdomains=${domains[$id]}
curpidfile=${pidfile[$id]}
#echo "got curprofile=${profile[$id]}" #echo "got curprofile=${profile[$id]}"
#echo "got curuser=${user[$id]}" #echo "got curuser=${user[$id]}"
#echo "got curpw=${pw[$id]}" #echo "got curpw=${pw[$id]}"
@ -255,6 +301,7 @@ elif [[ $cmd == "on" ]]; then
#echo "got curservercert=${servercert[$id]}" #echo "got curservercert=${servercert[$id]}"
#echo "got curroutes=${routes[$id]}" #echo "got curroutes=${routes[$id]}"
#echo "got curdomains=${domains[$id]}" #echo "got curdomains=${domains[$id]}"
#echo "got curpidfile=${pidfile[$id]}"
else else
error "Could not determine VPN ID for profile ^b${PROFILE}^p." error "Could not determine VPN ID for profile ^b${PROFILE}^p."
exit 1 exit 1
@ -314,22 +361,26 @@ elif [[ $cmd == "on" ]]; then
notify "${nstr}" notify "${nstr}"
rm -f "$VPNPIDFILE" rm -f "$curpidfile"
if [[ -n $curroutes && -n $curdomains ]]; then if [[ -n $curroutes && -n $curdomains ]]; then
printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg -s "$VPNSLICE --domains-vpn-dns ${curdomains// /,} $curroutes" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1 printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg -s "$VPNSLICE --domains-vpn-dns ${curdomains// /,} $curroutes" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1
rv=$?
elif [[ -n $curroutes ]]; then elif [[ -n $curroutes ]]; then
printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg -s "$VPNSLICE $curroutes" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1 printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg -s "$VPNSLICE $curroutes" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1
rv=$?
elif [[ -n $curdomains ]]; then elif [[ -n $curdomains ]]; then
printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg -s "$VPNSLICE --domains-vpn-dns $curdomains" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1 printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg -s "$VPNSLICE --domains-vpn-dns $curdomains" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1
rv=$?
else else
printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1 printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1
fi
rv=$? rv=$?
fi
res=$(cat "$LOGFILE") res=$(cat "$LOGFILE")
if [[ $rv -eq 0 ]]; then if [[ $rv -eq 0 ]]; then
cpid=$(pgrep openconnect) cpid=$(pgrep -f "openconnect.* ${curserver}")
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
echo "$cpid" > "$VPNPIDFILE" echo "$cpid" > "$curpidfile"
rv=0
else else
rv=1 rv=1
fi fi
@ -345,31 +396,45 @@ elif [[ $cmd == "status" ]]; then
BASECOL="$ORANGE" BASECOL="$ORANGE"
notify "Checking VPN status" notify "Checking VPN status"
get_vpn_status get_vpn_status
[[ $? -eq 0 ]] && ok || fail ok
if [[ $vstatus == "Connected" ]]; then if [[ $nup -eq 0 ]]; then
col="$GREEN" csecho "$ORANGE" "Not connected to any VPNs"
exit 0
elif [[ $nup -eq 1 ]]; then
ess=""
else else
col="$RED" ess="s"
fi
csecho -n "$BASECOL" "VPN status: ${col}${BOLD}${vstatus}^p"
if [[ ${vstatus,,} =~ "connected" ]]; then
[[ -n $vserver ]] && csecho "$BASECOL" " to ^b${vprofile}^p (${vvpntype}:${vuser}@${vserver})" || echo
fi fi
csecho "$ORANGE" "Connected to ^b$nup^p VPN${ess}:"
for x in ${!vstatus[@]}; do
col="$GREEN"
csecho "$BASECOL" " ${col}${BOLD}${vstatus[$x]}^p ${BASECOL}to ^b${vprofile[$x]}^p (${vvpntype[$x]}:${vuser[$x]}@${vserver[$x]})"
done
elif [[ $cmd == "off" ]]; then elif [[ $cmd == "off" ]]; then
if [[ -n $PROFILE ]]; then
# bring down just one vpn
idxlist=$(get_profile_id "$PROFILE")
else
# bring down all vpns
idxlist="${!profile[@]}"
fi
ntried=0
for x in $idxlist; do
vpnisup "${profile[$x]}"
if [[ $? -eq 0 ]]; then
rv=1 rv=1
get_vpn_status ntried=$((ntried + 1))
if [[ $vstatus == "Connected" ]]; then notify "Disconnecting from ^b${profile[$x]}^p VPN"
notify "Disconnecting from ${vvpntype} VPN ^b${vprofile}^p"
errstring="" errstring=""
errdata="" errdata=""
thepid=$(cat $VPNPIDFILE) thepid=$(cat ${pidfile[$x]})
if [[ -n $thepid ]]; then if [[ -n $thepid ]]; then
sudo kill $thepid sudo kill $thepid
sleep 2 # give it some time sleep 2 # give it some time
res=$(ps -p $thepid 2>&1) res=$(ps -p $thepid 2>&1)
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
rm -f "${VPNPIDFILE}" rm -f "${pidfile[$x]}"
rv=0 rv=0
else else
errstring="could not kill pid $thepid:" errstring="could not kill pid $thepid:"
@ -388,12 +453,18 @@ elif [[ $cmd == "off" ]]; then
error "$errstring" error "$errstring"
[[ -n $errdata ]] && cecho -s "$RED" "$errdata" | sed 's/^/ /' [[ -n $errdata ]] && cecho -s "$RED" "$errdata" | sed 's/^/ /'
fi fi
else elif [[ -n $PROFILE ]]; then
fail error "The VPN ^b${PROFILE}^p is already disconnected"
exit 1
fi
done
if [[ $ntried -eq 0 && -z $PROFILE ]]; then
error "All VPNs are already disconnected" error "All VPNs are already disconnected"
exit 1
fi fi
else else
usage usage
exit 1 exit 1
fi fi
exit 0