-
-
Save kaleksandrov/3cfee92845a403da995e7e44ba771183 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
case $# in | |
0) | |
echo "Usage: $0 {start|stop}" | |
exit 1 | |
;; | |
1) | |
case $1 in | |
start) | |
echo "Starting GlobalProtect..." | |
launchctl load /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist | |
launchctl load /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist | |
echo "Done!" | |
;; | |
stop) | |
echo "Stopping GlobalProtect..." | |
launchctl remove com.paloaltonetworks.gp.pangps | |
launchctl remove com.paloaltonetworks.gp.pangpa | |
echo "Done!" | |
;; | |
*) | |
echo "'$1' is not a valid verb." | |
echo "Usage: $0 {start|stop}" | |
exit 2 | |
;; | |
esac | |
;; | |
*) | |
echo "Too many args provided ($#)." | |
echo "Usage: $0 {start|stop}" | |
exit 3 | |
;; | |
esac |
Starting GlobalProtect...
Load failed: 5: Input/output error
Try running launchctl bootstrap
as root for richer errors.
Load failed: 5: Input/output error
Try running launchctl bootstrap
as root for richer errors.
Done!
I don't think these commands work for some GlobalProtect clients or OS versions. Running either this script, or either commands individually as root on my macOS 12.2.1 system does not stop or start the GlobalProtect client and it also does not break my GlobalProtect clients VPN connection to our Firewall. Maybe something changed either with the GP client, it's configuration (could ours be configured to not allow this?), or something new in the macOS 12.
@ndutyme What version of GP are you using? I am running macOS 12.4 Beta (21F5071b) and Global Protect 6.0.0-262 and these commands work for me, though I made a script more suitable to my needs. I created the following 2 functions and can call them with ease from the command line:
function unloadGlobalProtect {
launchctl unload -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
launchctl unload -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
}
function loadGlobalProtect {
launchctl load -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
launchctl load -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
}
Note the difference between launchctl unload
and launchctl remove
is the first stops, unloads the job, waits for the job to stop, and allows the job to restart on the next login/reboot. As it waits, it supports error handling. The second, simply removes the job from launchd
but does so asynchronously, as such, since there is no wait, there is no error handling.
@jldeen your scripts result in the same errors for me:
Unload failed: 5: Input/output error
Try running `launchctl bootout` as root for richer errors.
I'm using GP version 5.2.10-6 with macOS 12.3.1
This works for me:
launchctl enable gui/501/com.paloaltonetworks.gp.pangpa
launchctl enable gui/501/com.paloaltonetworks.gp.pangps
launchctl bootout gui/501 /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
launchctl bootstrap gui/501 /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
launchctl bootout gui/501 /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
launchctl bootstrap gui/501 /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
Is there a way to also auto-connect?
After loading GlobalProtect in the terminal I have to still manually connect
Is there a way to also auto-connect?
After loading GlobalProtect in the terminal I have to still manually connect
I have an apple script for that:
#!/bin/sh
osascript <<EOF
tell application "System Events" to tell process "GlobalProtect"
click menu bar item 1 of menu bar 2 -- Activates the GlobalProtect "window" in the menubar
click button 2 of window 1 -- Clicks either Connect or Disconnect
click menu bar item 1 of menu bar 2 -- This will close the GlobalProtect "window" after clicking Connect/Disconnect. This is optional.
end tell
EOF
Works great, thanks!
How to of GlobalProtect + zsh + skhd
- Add the following to ~/.zshenv (create it if doesn't exist already)
# Kill VPN
function unloadGlobalProtect {
launchctl unload -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
launchctl unload -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
}
# Start VPN process
function loadGlobalProtect {
launchctl load -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
launchctl load -w /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
}
# Toggle VPN connection
function toggleGlobalProtectConnection {
osascript <<EOF
tell application "System Events" to tell process "GlobalProtect"
click menu bar item 1 of menu bar 2 -- Activates the GlobalProtect "window" in the menubar
click button 2 of window 1 -- Clicks either Connect or Disconnect
click menu bar item 1 of menu bar 2 -- This will close the GlobalProtect "window" after clicking Connect/Disconnect. This is optional.
end tell
EOF
}
- Add bindings to skhd. For example these are mine (you may need to remove default MacOS bindings)
rcmd + ralt - space : zsh -c "toggleGlobalProtectConnection" # (dis)connect to VPN
# 0x2B = ,
# 0x2F = .
rcmd + ralt - 0x2B : zsh -c "unloadGlobalProtect " # killall global protect
rcmd + ralt - 0x2F : zsh -c "loadGlobalProtect " # start global protect
- Stop skhd service and start it manually to confirm everything is working fine
brew services stop skhd
skhd
-
Play with the keybindings and see the functions outputs in the terminal from 3.
-
Stop manual instance of skhd and restart service
# CTRL + C
brew services start skhd
How can I add drop-down option with diff VPN portals available to choose and connect in GP app via script/code for Mac?
OR
Deploy Mac GP App with list of available VPN portals to be chosen by user and connect ?
Thanks!
Thanks!
Auto connects on recovering from sleep:
brew install sleepwatcher
~/.wakeup
:
#!/usr/bin/env bash
# power on
/usr/local/bin/blueutil --power 1
# try to connect headphones
/usr/local/bin/blueutil --connect 00-1b-66-b0-eb-24
# autoconnect VPN
source ./vpn-connect.sh
./vpn-connect.sh
#!/usr/bin/env bash
# https://gist.github.com/kaleksandrov/3cfee92845a403da995e7e44ba771183
osascript <<EOF
tell application "System Events" to tell process "GlobalProtect"
click menu bar item 1 of menu bar 2 -- Activates the GlobalProtect "window" in the menubar
set frontmost to true -- keep window 1 active
tell window 1
-- Find the status
tell (first UI element whose title is "Connect") to if exists then set message to "VPN is connecting now..."
tell (first UI element whose title is "Disconnect") to if exists then set message to "VPN is connected."
-- Click connect button
tell (first UI element whose title is "Connect") to if exists then click
end tell
click menu bar item 1 of menu bar 2 -- This will close the GlobalProtect "window" after clicking Connect/Disconnect. This is optional.
say message
return message
end tell
EOF
Detect whether the VPN is currently connected:
#!/usr/bin/env bash
#
# check-vpn-connected
#
# Determines if the Global Protect VPN is connected. The detection method uses
# a comparison of the client's configured IP addresses and the active network
# interface addresses.
#
# - If VPN is connected, the script exits with code 0.
#
# - If VPN is NOT connected, the script exits with code 1.
#
# Arguments:
#
# -v | --verbose Prints out a message to STDERR if not connected.
# -d | --debug Prints debugging info. Implies --verbose.
#
# shellcheck disable=SC2155
# (see https://github.com/koalaman/shellcheck/wiki)
#
main() {
local verbose=0;
local debug=0;
local ipv4AddressRegex="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
while [[ $# -gt 0 ]]; do
case "$1" in
--verbose|-v)
# Verbose output.
verbose=1;
;;
--debug|-d)
# Debug output; implies verbose.
debug=1;
verbose=1;
esac;
shift;
done;
# Get the active interface addresses.
# shellcheck disable=SC2207
# IPv6 support would require filtering on 'inet6' and an IPv6 regex.
local interfaceIPs=($(ifconfig -a -u inet | grep -F "inet" | grep -oE "${ipv4AddressRegex}"));
if [[ "${debug}" -ne 0 ]]; then
>&2 echo "Interface Addresses: ${interfaceIPs[*]}";
fi;
# Read Global Protect client configuration, return the listed IPv4 addresses.
# IPv6 support would require filtering on 'PreferredIPv6_' and an IPv6 regex.
IFS=$'\n' read -d '' -r -a vpnIPs < <( \
defaults read /Library/Preferences/com.paloaltonetworks.GlobalProtect.settings.plist | \
grep -F "PreferredIP_" | \
grep -oE "${ipv4AddressRegex}" \
);
if [[ "${debug}" -ne 0 ]]; then
>&2 echo "VPN IPs: ${vpnIPs[*]}";
fi;
# If one of the interface IPs matches one of the VPN IPs then the VPN is connected.
# printf converts the bash array into a pipe-delimited list and XXXX is a never-match last element.
if grep -qE "($(printf '%s|' "${interfaceIPs[@]}")XXXX)" <<< "${vpnIPs[@]}"; then
exit 0;
else
if [[ "${verbose}" -ne 0 ]]; then
>&2 echo "$(tput setaf 208)Active VPN connection required!$(tput op)";
fi;
exit 1;
fi;
}
main "$@";
Update on my version of toggleGlobalProtectConnection
that broke with the update to GlobalProtect of the last few days
function toggleGlobalProtectConnection {
osascript <<EOF
tell application "System Events" to tell process "GlobalProtect"
click menu bar item 1 of menu bar 2 -- Activates the GlobalProtect "window" in the menubar
set frontmost to true -- keep window 1 active
tell window 1
-- Click on the connect or disconnect button, depending on if they exist or not
if exists (first UI element whose title is "Connect") then
tell (first UI element whose title is "Connect") to if exists then click
else
tell (first UI element whose title is "Disconnect") to if exists then click
end if
end tell
click menu bar item 1 of menu bar 2 -- This will close the GlobalProtect "window" after clicking Connect/Disconnect. This is optional.
end tell
EOF
}
(it has duplicate code but it works 🤣)
It's inspired by the previous comments. Thanks to everyone!
Here is what I modified it to , check for connection state after every 30 minutes(you can modify this ) and then connect again
`➜ ~ cat gp.sh
#!/usr/bin/env bash
osascript <<EOF
tell application "System Events" to tell process "GlobalProtect"
click menu bar item 1 of menu bar 2 -- Activates the GlobalProtect "window" in the menubar
set frontmost to false -- keep window 1 active
tell window 1
-- Find the status
tell (first UI element whose title is "Connect") to if exists then set message to "VPN is connecting now..."
--tell (first UI element whose title is "Disconnect") to if exists then set message to "VPN is connected."
-- Click connect button
tell (first UI element whose title is "Connect") to if exists then click
end tell
click menu bar item 1 of menu bar 2 -- This will close the GlobalProtect "window" after clicking Connect/Disconnect. This is optional.
end tell
EOF
➜ ~ cat com.example.globalprotect.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.globalprotect</string>
<key>Program</key>
<string>/Users/Aadil.Mir/gp.sh</string>
<key>StartInterval</key>
<integer>1800</integer>
</dict>
</plist>
cp com.example.globalprotect.plist ~/Library/LaunchAgents
launchctl load ~/Library/LaunchAgents/com.example.globalprotect.plist
launchctl unload ~/Library/LaunchAgents/com.example.globalprotect.plist
`
Hi, Friends.
I found that none of the above worked for me on MacOS Ventura 13.2.1 running GlobalProtect 6.0.5, so I created a simple script that does.
Perhaps I am different in that I have also disabled the underlying system LaunchDaemon service (pangpsd
) when this thing is not running because I like to know what's happening on my machine at all times, to the best extent possible. I have no Login Items enabled and modified the plists directly with a text editor to keep them from launching on startup.
This quick script is part of my ~/.bash_profile
and invoked by calling vpn
on the command line and will start the requisite services, and then launch GlobalProtect. If you have a different install location, you may need to change the open -a /Applications/GlobalProtect.app
line to the appropriate directory.
To quit, vpn unload
will stop and disable all services and kill the app as well. Errors are suppressed, with the routing > /dev/null 2>&1
because I don't care if the shutdown process isn't clean. Your milage may vary.
vpn() {
if [ "$1" = "unload" ]; then
launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist > /dev/null 2>&1
launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist > /dev/null 2>&1
launchctl bootout gui/$(id -u) /Library/LaunchDaemons/com.paloaltonetworks.gp.pangpsd.plist > /dev/null 2>&1
PID="$(launchctl list | grep palo | cut -f 1)"
kill $PID
echo "VPN unloaded"
else
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
launchctl bootstrap gui/$(id -u) /Library/LaunchDaemons/com.paloaltonetworks.gp.pangpsd.plist
open -a /Applications/GlobalProtect.app
fi
}
Is there a way to fill in your 2FA code when prompted for this when connecting to a specific VPN portal?
Is there a way to fill in your 2FA code when prompted for this when connecting to a specific VPN portal?
This script doesn't supports this. All it does is to register/unregister the global protect application as an autostart service. It doesn't use any global protect API.
@kaleksandrov thank you, I understand that. However, do you think it would be possible, seeing that some of the scripts above are using the click functionality?
This is a easier version that works for me in macOS Sonoma
vpn() {
if [ "$1" = "stop" ]; then
launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist > /dev/null 2>&1
launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist > /dev/null 2>&1
echo "VPN unloaded"
elif [ "$1" = "start" ]; then
#statements
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
echo "VPN loaded"
fi
}
I adapted the script to kill globalprotect icon in tray on stop and open globalprotect app when start :
# Add the function below to your .zsh_env or .bash_profile
# Usage : globalprotect start or globalprotect stop
globalprotect() {
if [ "$1" = "stop" ]; then
launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist > /dev/null 2>&1
launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist > /dev/null 2>&1
PID="$(launchctl list | grep palo | cut -f 1)"
# Kill the processes IDs only if found
if [ ! -z "$PID" ]; then
kill -9 $PID
fi
echo "VPN unloaded"
elif [ "$1" = "start" ]; then
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
open -a /Applications/GlobalProtect.app
echo "VPN loaded"
fi
}
Just to share:
Here is my version of start / stop script
# echo "START"
# set -e
# set -x
PORTAL="vpn.nios.net"
MAX_ATTEMPTS=10
SLEEP_INTERVAL=5
WAIT_FOR_APP_TO_BE_READY=5
WAIT_FOR_CONNECTION=15
function check_setup() {
if [ ! -e "/Applications/GlobalProtect.app" ]; then
echo "GlobalProtect app is not installed."
exit 1
fi
if [ ! -e "/Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist" ] || [ ! -e "/Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist" ]; then
echo "GlobalProtect launch agents are not properly set up."
exit 1
fi
}
function ensure_app_running() {
for ((i=1; i<=$MAX_ATTEMPTS; i++)); do
if osascript -e 'tell application "System Events" to (name of processes) contains "GlobalProtect"' &>/dev/null; then
sleep $WAIT_FOR_APP_TO_BE_READY
return 0
fi
echo "Waiting for GlobalProtect app to start..."
sleep $SLEEP_INTERVAL
done
echo "Failed to start GlobalProtect app."
exit 1
}
function start_vpn() {
echo -e "\nStarting GlobalProtect...\n"
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist
launchctl bootstrap gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist
ensure_app_running
connect_vpn
sleep $WAIT_FOR_CONNECTION
if check_vpn_status; then
echo -e "\nVPN is connected to $PORTAL\n"
return 0
fi
for ((i=1; i<=$MAX_ATTEMPTS; i++)); do
connect_vpn
sleep $SLEEP_INTERVAL
if check_vpn_status; then
echo -e "\nVPN is connected to $PORTAL\n"
return 0
fi
done
echo -e "\nFailed to connect to VPN after $MAX_ATTEMPTS attempts\n"
exit 1
}
function connect_vpn() {
osascript <<EOF &>/dev/null
tell application "System Events"
tell process "GlobalProtect"
if not (exists window 1) then
click menu bar item 1 of menu bar 2 -- Activates the GlobalProtect "window" in the menubar
delay 2 -- Wait for 2 seconds
end if
set frontmost to true -- keep window 1 active
tell window 1
if exists (first button whose title is "Connect") then
tell (first button whose title is "Connect") to if exists then click
end if
end tell
end tell
end tell
EOF
}
function check_vpn_status() {
VPN_STATUS=$(osascript <<EOF 2>/dev/null
tell application "System Events"
tell process "GlobalProtect"
set frontmost to true -- keep window 1 active
tell window 1
if exists (first button whose title is "Disconnect") then
return "Connected"
else
return "Not Connected"
end if
end tell
end tell
end tell
EOF
)
echo "VPN status: $VPN_STATUS"
if [ "$VPN_STATUS" == "Connected" ]; then
return 0
elif [ "$VPN_STATUS" == "Not Connected" ]; then
return 1
else
return 1
fi
}
function stop_vpn() {
echo -e "\nStopping GlobalProtect...\n"
if ! launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist 2>/dev/null || \
! launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist 2>/dev/null; then
echo "Failed to bootout as current user, trying with sudo..."
sudo launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist 2>/dev/null
sudo launchctl bootout gui/$(id -u) /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist 2>/dev/null
fi
PIDS=$(launchctl list | grep -E 'com\.paloaltonetworks\.gp\.pangpa|com\.paloaltonetworks\.gp\.pangps' | awk '{print $1}')
if [ ! -z "$PIDS" ]; then
echo "Killing GlobalProtect processes..."
for PID in $PIDS; do
if sudo kill -9 $PID; then
echo "Killed process $PID"
else
echo "Failed to kill process $PID"
fi
done
fi
echo -e "\nVPN stopped\n"
}
case $1 in
start)
check_setup
start_vpn
;;
stop)
stop_vpn
;;
*)
echo "'$1' is not a valid verb."
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
I adapted the script to kill globalprotect icon in tray on stop and open globalprotect app when start :
@damosse31 THANK YOU! That's exactly what I was looking for and it works perfectly on macOS Sonoma and GlobalProtect 6.1.4 🫶
I adapted the script to kill globalprotect icon in tray on stop and open globalprotect app when start :
@damosse31 Thanks, it works perfectly!
macOS Ventura 13.6.6 & GlobalProtect Version: 5.2.13-48
@githubrobbi Thanks for the clearly written code, I adapted your script's connect_vpn
function!
As my org requires manual user name and password entry plus a 2FA, I added rudimentary scripts to advance the GP login screens and entering those values via blind keystrokes. The 2FA key is generated by a separate python script that returns the TOTP value using the python lib pyotp
by printing it within the python function, and again entering those value via blind keystrokes.
There are definitely better ways to not hardcode the user name, password, and TOTP secret key as well as advancing the screen based on the available buttons rather than blind keystrokes... but it got too late at night and this works for now. Would love it if someone can improve upon it!
MacOS Sonoma 14.7.1 (23H222) & GlobalProtect 6.2.1-132
.zshenv
function vpn() {
osascript <<EOF &>/dev/null
tell application "System Events"
tell process "GlobalProtect"
if not (exists window 1) then
click menu bar item 1 of menu bar 2 -- Activates the GlobalProtect "window" in the menubar
delay 2 -- Wait for 2 seconds
end if
set frontmost to true -- keep window 1 active
tell window 1
if exists (first button whose title is "Connect") then
tell (first button whose title is "Connect") to if exists then click
end if
delay 3
set textToType to "USERNAME"
keystroke textToType
keystroke " "
set textToType to "PASSWORD"
keystroke textToType
if exists (first button whose title is "Connect") then
tell (first button whose title is "Connect") to if exists then click
end if
end tell
delay 10
set totp to do shell script "python $HOME/TOTP.py"
keystroke totp
keystroke return
-- if exists (first button whose title is "Verify") then
-- tell (first button whose title is "Verify") to if exists then click
-- end if
end tell
end tell
EOF
}
TOTP.py
import pyotp
secret_key = "SECRET_KEY"
def getToken(secret_key):
totp = pyotp.TOTP(secret_key)
token = totp.now()
print(token) # This is what's returning the value in a shell script execution
# return token
getToken(secret_key)
Special thanks to @kaleksandrov for starting this gist!
I'm getting this error: