Skip to content

Instantly share code, notes, and snippets.

@yi-jiayu
Created December 21, 2018 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yi-jiayu/e4c46a638508e3a864b903eea766873e to your computer and use it in GitHub Desktop.
Save yi-jiayu/e4c46a638508e3a864b903eea766873e to your computer and use it in GitHub Desktop.
Bash script with completion to connect to VPNs using TOTP passwords on MacOS
#!/usr/bin/env bash
config_file="${config_file:-$HOME/vpn.conf}"
if [ ! -f $config_file ]; then
echo "could not find config file at $config_file"
exit 1
fi
username=$(grep -m1 username <$config_file | awk -F\= '{print $2}')
if [ -z "$username" ]; then
echo "username not set in config file"
exit 1
fi
totp_key=$(grep -m1 totp_key <$config_file | awk -F\= '{print $2}')
if [ -z "$totp_key" ]; then
echo "totp key not set in config file"
exit 1
fi
old_ifs=$IFS
IFS=$'\n'
vpns=($(grep vpn <$config_file | awk -F\= '{print $2}'))
IFS=$old_ifs
if [ ${#vpns[@]} -eq 0 ]; then
echo "no vpns specified in config file"
exit 1
fi
list_vpns() {
for vpn in "${vpns[@]}"; do
echo $vpn
done
}
connect_vpn() {
if [ -z "$1" ]; then
echo "usage: $(basename $0) connect vpn-name"
exit 1
fi
vpn_name=$1
xauth_key=$(security find-generic-password -l "$vpn_name" | grep svce | awk -F\" '{print $4}')
if [ -z "$xauth_key" ]; then
echo "Profile not found!"
exit 1
fi
otp=$(/usr/local/bin/oathtool --totp -b -d 6 "$totp_key")
security add-generic-password -a "$username" -s "$xauth_key" -w "$otp" -D "IPSec XAuth Password" -l "$vpn_name" -U
connAction=\"$2\"
connName=\"$vpn_name\"
/usr/bin/env osascript >/dev/null <<-EOF
tell application "System Events"
tell process "SystemUIServer"
set vpnMenu to (menu bar item 1 of menu bar 1 where description is "VPN")
tell vpnMenu to click
if ($connAction as string = "connect") then
try
click menu item ("Connect " & ${connName}) of menu 1 of vpnMenu
on error errMsg
display dialog "ERROR: Profile not found or already connected!"
key code 53
error number -128
end try
delay 2
else if ($connAction = "disconnect") then
try
click menu item ("Disconnect " & ${connName}) of menu 1 of vpnMenu
on error errMsg
display dialog "ERROR: Profile not found or already disconnected!"
key code 53
error number -128
end try
delay 2
end if
end tell
end tell
EOF
}
case "$1" in
"list")
list_vpns
;;
"connect")
connect_vpn "$2" connect
;;
"disconnect")
connect_vpn "$2" disconnect
;;
*)
printf "usage:\n $(basename $0) list\n $(basename $0) connect vpn-name\n"
exit 1
;;
esac
# vpn(1) completion
_vpn() {
# based on https://stackoverflow.com/a/17881946
local cur prev
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD - 1]}
case ${COMP_CWORD} in
1)
COMPREPLY=($(compgen -W "list connect disconnect" -- ${cur}))
;;
2)
case ${prev} in
connect) ;&
disconnect)
# based on https://stackoverflow.com/a/40944195
mapfile -t patterns < <(vpn list)
mapfile -t COMPREPLY < <(compgen -W "$(printf '%q ' "${patterns[@]}")" -- "$cur" | awk '/ / { print "\""$0"\"" } /^[^ ]+$/ { print $0 }')
;;
esac
;;
*)
COMPREPLY=()
;;
esac
}
complete -F _vpn vpn
totp_key=abcdef0123456
username=my.username
vpn=A VPN Profile Name
vpn=Another VPN Profile Name
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment