AnyConnect VPN up/down single command + service cleanup
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 | |
set -euo pipefail | |
IFS=$'\n\t' | |
readonly _CISCO_VPN="/opt/cisco/anyconnect/bin/vpn" | |
readonly _GREEN='\033[0;32m' | |
readonly _RED='\033[0;31m' | |
readonly _NC='\033[0m' | |
bg_services() { | |
if [[ "$(uname)" == 'Darwin' ]]; then | |
local COMMAND="$1" | |
echo 'Managing background services - you may need to enter your system password' | |
for BG_TASK in ciscod64 vpnagentd; do | |
sudo launchctl "$COMMAND" "/Library/LaunchDaemons/com.cisco.anyconnect.$BG_TASK.plist" >/dev/null 2>/dev/null | |
done | |
fi | |
} | |
vpn_up() { | |
local VPN_PROFILE="$1" | |
local VPN_USERNAME="$2" | |
local VPN_PASSWORD="$3" | |
bg_services 'load' | |
"$_CISCO_VPN" -s connect "$VPN_PROFILE" <<EOF | |
1 | |
$VPN_USERNAME | |
$VPN_PASSWORD | |
y | |
exit | |
EOF | |
} | |
vpn_down() { | |
"$_CISCO_VPN" disconnect | |
bg_services 'unload' | |
} | |
vpn_verify() { | |
local VPN_PROFILE="$1" | |
local VPN_USERNAME="$2" | |
local CISCO_CERT_DIR="$HOME/.cisco/certificates" | |
local CLIENT_CERT="$CISCO_CERT_DIR/client/$VPN_USERNAME.pem" | |
local CLIENT_KEY="$CISCO_CERT_DIR/client/private/$VPN_USERNAME.key" | |
local RESULT=0 | |
mkdir -p "$CISCO_CERT_DIR/ca" "$CISCO_CERT_DIR/client/private" | |
echo -n "Checking client certificate exists..." | |
if [[ ! -f "$CLIENT_CERT" ]]; then | |
echo -e "${_RED}FAILED${_NC}" | |
echo "Cannot find client certificate at $CLIENT_CERT" | |
RESULT=1 | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
echo -n "Checking client key exists..." | |
if [[ ! -f "$CLIENT_KEY" ]]; then | |
echo -e "${_RED}FAILED${_NC}" | |
echo "Cannot find client key at $CLIENT_KEY" | |
RESULT=1 | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
if [[ $RESULT -eq 1 ]]; then | |
echo "If you have your certificate as a PFX file you can extract the components via:" | |
echo " openssl pkcs12 -in $VPN_USERNAME.pfx -clcerts -nokeys -out $CLIENT_CERT" | |
echo " openssl pkcs12 -in $VPN_USERNAME.pfx -nocerts -nodes -out $CLIENT_KEY" | |
fi | |
local DIGI_CERT_ROOT="DigiCertAssuredIDRootCA.crt.pem" | |
echo -n "Checking root certificate $DIGI_CERT_ROOT exists..." | |
if [[ ! -f "$CISCO_CERT_DIR/ca/$DIGI_CERT_ROOT" ]]; then | |
local DIGI_CERT_URI="https://cacerts.digicert.com/DigiCertAssuredIDRootCA.crt.pem" | |
echo -e "${_RED}FAILED${_NC}" | |
echo -n "Downloading from $DIGI_CERT_URI..." | |
curl -Lso "$CISCO_CERT_DIR/ca/$DIGI_CERT_ROOT" "$DIGI_CERT_URI" | |
if [[ ! -f "$CISCO_CERT_DIR/ca/$DIGI_CERT_ROOT" ]]; then | |
echo -e "${_RED}FAILED${_NC}" | |
echo "Cannot find expected root certificate at $CISCO_CERT_DIR/ca/$DIGI_CERT_ROOT" | |
echo " You can download it from $DIGI_CERT_URI" | |
RESULT=1 | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
for ROOT_CERT_URI in https://pki.springernature.com/certenroll/senldogo5092_SpringerNatureRootCA.crt \ | |
https://pki.springernature.com/certenroll/senldogo5337.springernature.com_SpringerNatureCA4.crt; do | |
local LOCAL_CERT="$(echo $ROOT_CERT_URI | cut -d_ -f2 | cut -d. -f1 ).pem" | |
echo -n "Checking root certificate $LOCAL_CERT exists..." | |
if [[ ! -f "$CISCO_CERT_DIR/ca/$LOCAL_CERT" ]]; then | |
echo -e "${_RED}FAILED${_NC}" | |
echo -n " Downloading from $ROOT_CERT_URI..." | |
curl -Ls "$ROOT_CERT_URI" | openssl x509 -inform DER -outform PEM -out $CISCO_CERT_DIR/ca/$LOCAL_CERT | |
if [[ ! -f "$CISCO_CERT_DIR/ca/$LOCAL_CERT" ]]; then | |
echo -e "${_RED}FAILED${_NC}" | |
echo "Cannot find expected root certificate at $CISCO_CERT_DIR/ca/$LOCAL_CERT" | |
echo " You can download it from $ROOT_CERT_URI and then convert to PEM format with" | |
echo " openssl x509 -inform DER -outform PEM -in senldogo????_$LOCAL_CERT.crt -out $LOCAL_CERT.pem" | |
RESULT=1 | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
done | |
if [[ -f "$CLIENT_CERT" && -f "$CLIENT_KEY" ]]; then | |
echo -n "Checking client certificate and key match..." | |
local CLIENT_CERT_HASH=$(openssl x509 -noout -modulus -in "$CLIENT_CERT" | openssl md5) | |
local CLIENT_KEY_HASH=$(openssl rsa -noout -modulus -in "$CLIENT_KEY" | openssl md5) | |
if [[ "$CLIENT_CERT_HASH" != "$CLIENT_KEY_HASH" ]]; then | |
echo -e "${_RED}FAILED${_NC}" | |
echo "Client certificate and client key do not appear to match" | |
RESULT=1 | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
fi | |
if [[ $RESULT == 0 ]]; then | |
echo -n "Checking certificate chain..." | |
local VERIFY_OUTPUT=$(openssl verify -CAfile "$HOME/.cisco/certificates/ca/SpringerNatureRootCA.pem" \ | |
-untrusted "$HOME/.cisco/certificates/ca/SpringerNatureCA4.pem" \ | |
"$HOME/.cisco/certificates/client/$VPN_USERNAME.pem" 2>&1) | |
if echo "$VERIFY_OUTPUT" | grep OK >/dev/null 2>&1; then | |
echo -e "${_GREEN}OK${_NC}" | |
else | |
echo -e "${_RED}FAILED${_NC}" | |
echo " Output was: $VERIFY_OUTPUT" | |
RESULT=1 | |
fi | |
else | |
echo "WARN: Certificate chain check will be skipped due to previous errors" | |
fi | |
echo -n "Checking profile $VPN_PROFILE exists..." | |
if [[ ! -f "/opt/cisco/anyconnect/profile/$VPN_PROFILE.xml" ]]; then | |
echo -e "${_RED}FAILED${_NC}" | |
echo "Profile $VPN_PROFILE was not found - please connect once via '$_CISCO_VPN -s connect vpn.springernature.com' to download this" | |
RESULT=1 | |
else | |
echo -e "${_GREEN}OK${_NC}" | |
fi | |
return $RESULT | |
} | |
verify_prerequisites() { | |
if [[ ! -f "$_CISCO_VPN" ]]; then | |
echo "Cannot find AnyConnect at $_CISCO_VPN - please ensure you've installed it." | |
echo "You can download binaries from https://anyconnect.springernature.com" | |
return 1 | |
fi | |
} | |
main() { | |
local ACTION="${1:-}" | |
verify_prerequisites | |
if [[ "$(uname)" == 'Darwin' ]]; then | |
local VPN_USERNAME="$(security find-generic-password -a "$USER" -s sn-vpn-username -w 2>/dev/null)" | |
local VPN_PASSWORD="$(security find-generic-password -a "$USER" -s sn-vpn-password -w 2>/dev/null)" | |
else | |
local VPN_USERNAME="$(secret-tool lookup $USER sn-vpn-username)" | |
local VPN_PASSWORD="$(secret-tool lookup $USER sn-vpn-password)" | |
fi | |
if [[ -z "$VPN_USERNAME" || -z "$VPN_PASSWORD" ]]; then | |
if [[ "$(uname)" == 'Darwin' ]]; then | |
echo "Error: you must add your VPN username/password to Keychain!" | |
echo ' security add-generic-password -a "$USER" -s sn-vpn-username -w "<your username>"' | |
echo ' security add-generic-password -a "$USER" -s sn-vpn-password -w "<your password>"' | |
else | |
echo "Error: you must add your VPN username/password to secret-tool!" | |
echo ' echo "<your username>" | secret-tool store --label="$USER sn-vpn-username" $USER sn-vpn-username' | |
echo ' echo "<your password>" | secret-tool store --label="$USER sn-vpn-password" $USER sn-vpn-password' | |
fi | |
exit 1 | |
fi | |
local VPN_PROFILE="SpringerNature-Int" | |
case "$ACTION" in | |
up) | |
vpn_up "$VPN_PROFILE" "$VPN_USERNAME" "$VPN_PASSWORD" | |
;; | |
down) | |
vpn_down | |
;; | |
verify) | |
vpn_verify "$VPN_PROFILE" "$VPN_USERNAME" | |
;; | |
*) | |
echo "Usage: $0 <up|down|verify>" | |
exit 1 | |
;; | |
esac | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment