-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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