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

237
cvpn
View File

@ -70,6 +70,7 @@ function load_config() {
thisroutes=$(cut -d, -f7 <<< "${line}")
thisdomains=$(cut -d, -f8 <<< "${line}")
thisservercert=$(cut -d, -f9 <<< "${line}")
thispidfile=${CONFDIR}/${thisprofile// /}.pid
case $thisvpntype in
"") thisvpntype="anyconnect";;
"cisco") thisvpntype="anyconnect";;
@ -90,6 +91,7 @@ function load_config() {
routes[$nvpns]="${thisroutes}"
domains[$nvpns]="${thisdomains}"
servercert[$nvpns]="${thisservercert}"
pidfile[$nvpns]="${thispidfile}"
nvpns=$((nvpns + 1))
done <<< "${CONFLINES}"
else
@ -132,36 +134,51 @@ function get_profile_id() { #1=profile_to_check
return 1
}
function get_vpn_status() { # populates vprofile vuser vstatus vserver vvpntype, return 0 if vpn up
local rv=0 vpnpid pname
vprofile=""
vuser=""
vstatus="Disconnected"
vserver=""
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
function vpnisup() { # 1=profile
local idx pf rv pid
idx=$(get_profile_id "$1")
rv=$?
if [[ $rv -ne 0 || -z $idx ]]; then
return 1
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 $vvpntype ]] && vvpntype=${vvpntype[$x]}
fi
done
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 $rv
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
for x in ${!profile[@]}; do
vpnisup "${profile[$x]}"
if [[ $? -eq 0 ]]; then
vprofile+=(${profile[$x]})
vpid+=($(cat ${pidfile[$x]}))
vstatus+=("Connected")
vserver+=(${server[$x]})
vuser+=(${user[$x]})
vvpntype+=(${vpntype[$x]})
nup=$((nup + 1))
fi
done
[[ $nup -eq 0 ]] && return 1
return 0
}
PROFILE=""
@ -196,7 +213,7 @@ load_config || exit 1
usingdefault=0
PROFILE="$2"
if [[ -z $PROFILE ]]; then
if [[ -z $PROFILE && $cmd == "up" ]]; then
if [[ -n ${DEFAULTPROFILE} ]]; then
PROFILE="${DEFAULTPROFILE}"
usingdefault=1
@ -204,28 +221,56 @@ if [[ -z $PROFILE ]]; then
fi
if [[ $cmd == "list" ]]; then
F="%s,%s,%s,%s,%s,%s\n"
F="%s,%s,%s,%s,%s,%s,%s,%s\n"
table=$(
(
printf "${F}" "Profile" "Username" "Password" "Group" "Server" "Type"
printf "${F}" "Profile" "Up?" "Username" "Password" "Group" "Server" "Type" "Scope"
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)}" "${vpntype[$x]}"
p="${pw[$x]:0:2}....${pw[$x]: (-2)}"
prof="${profile[$x]}"
[[ $prof == $DEFAULTPROFILE ]] && prof="${prof}*"
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
) | 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"
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"
vpnisup "${PROFILE}"
if [[ $? -eq 0 ]]; then
error "VPN profile $PROFILE is already connected"
exit 1
fi
@ -246,6 +291,7 @@ elif [[ $cmd == "on" ]]; then
curservercert=${servercert[$id]}
curroutes=${routes[$id]}
curdomains=${domains[$id]}
curpidfile=${pidfile[$id]}
#echo "got curprofile=${profile[$id]}"
#echo "got curuser=${user[$id]}"
#echo "got curpw=${pw[$id]}"
@ -255,6 +301,7 @@ elif [[ $cmd == "on" ]]; then
#echo "got curservercert=${servercert[$id]}"
#echo "got curroutes=${routes[$id]}"
#echo "got curdomains=${domains[$id]}"
#echo "got curpidfile=${pidfile[$id]}"
else
error "Could not determine VPN ID for profile ^b${PROFILE}^p."
exit 1
@ -314,22 +361,26 @@ elif [[ $cmd == "on" ]]; then
notify "${nstr}"
rm -f "$VPNPIDFILE"
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
rm -f "$curpidfile"
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
rv=$?
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
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
printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=$curvpntype -u "$curuser" --passwd-on-stdin $grouparg --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1
rv=$?
fi
rv=$?
res=$(cat "$LOGFILE")
if [[ $rv -eq 0 ]]; then
cpid=$(pgrep openconnect)
cpid=$(pgrep -f "openconnect.* ${curserver}")
if [[ $? -eq 0 ]]; then
echo "$cpid" > "$VPNPIDFILE"
echo "$cpid" > "$curpidfile"
rv=0
else
rv=1
fi
@ -345,55 +396,75 @@ elif [[ $cmd == "status" ]]; then
BASECOL="$ORANGE"
notify "Checking VPN status"
get_vpn_status
[[ $? -eq 0 ]] && ok || fail
if [[ $vstatus == "Connected" ]]; then
col="$GREEN"
ok
if [[ $nup -eq 0 ]]; then
csecho "$ORANGE" "Not connected to any VPNs"
exit 0
elif [[ $nup -eq 1 ]]; then
ess=""
else
col="$RED"
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
ess="s"
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
rv=1
get_vpn_status
if [[ $vstatus == "Connected" ]]; then
notify "Disconnecting from ${vvpntype} VPN ^b${vprofile}^p"
errstring=""
errdata=""
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
ntried=$((ntried + 1))
notify "Disconnecting from ^b${profile[$x]}^p VPN"
errstring=""
errdata=""
thepid=$(cat $VPNPIDFILE)
if [[ -n $thepid ]]; then
sudo kill $thepid
sleep 2 # give it some time
res=$(ps -p $thepid 2>&1)
if [[ $? -ne 0 ]]; then
rm -f "${VPNPIDFILE}"
rv=0
thepid=$(cat ${pidfile[$x]})
if [[ -n $thepid ]]; then
sudo kill $thepid
sleep 2 # give it some time
res=$(ps -p $thepid 2>&1)
if [[ $? -ne 0 ]]; then
rm -f "${pidfile[$x]}"
rv=0
else
errstring="could not kill pid $thepid:"
errdata="$res"
rv=1
fi
else
errstring="could not kill pid $thepid:"
errdata="$res"
errstring="Cannot determine current VPN PID to kill."
rv=1
fi
else
errstring="Cannot determine current VPN PID to kill."
rv=1
fi
if [[ $rv -eq 0 ]]; then
ok
else
fail
error "$errstring"
[[ -n $errdata ]] && cecho -s "$RED" "$errdata" | sed 's/^/ /'
if [[ $rv -eq 0 ]]; then
ok
else
fail
error "$errstring"
[[ -n $errdata ]] && cecho -s "$RED" "$errdata" | sed 's/^/ /'
fi
elif [[ -n $PROFILE ]]; then
error "The VPN ^b${PROFILE}^p is already disconnected"
exit 1
fi
else
fail
done
if [[ $ntried -eq 0 && -z $PROFILE ]]; then
error "All VPNs are already disconnected"
exit 1
fi
else
usage
exit 1
fi
exit 0