-
-
Save nonamed01/0961d8a79955206ebdc00abcaa56aefe to your computer and use it in GitHub Desktop.
#!/bin/bash | |
# Uncomment the following line to debug the script: | |
#set -x | |
##################################################################################### | |
# fuckForticlient.sh | |
# | |
# Script to authenticate against Fortinet SAML servers using Firefox and | |
# openfortivpn. This replaces Forticlient for GNU/Linux completely. | |
# Because openfortivpn does not support SAML login (yet), this script uses Firefox | |
# to authenticate, grabs SVPNCOOKIE and then calls openfortivpn to setup | |
# the VPN service. | |
# | |
# See: | |
# https://github.com/adrienverge/openfortivpn/pull/1042#issuecomment-1344211491 | |
# | |
# 2022-2023 by Toni Castillo Girona (@Disbauxes) | |
# | |
# INSTALLATION | |
# > First, install the following packages: | |
# sudo apt-get install liblz4-dev lz4json jq firefox-esr git inotify-tools \ | |
# libssl-dev autoconf make gcc pkg-config | |
# > Next, install all the dependencies to build openfortivpn: | |
# sudo apt-get build-dep openfortivpn | |
# | |
# Please notice that if your OS provides an openfortivpn package with | |
# support for the "--cookie-on-stdin" parameter, you do not need to | |
# install these dependencies or build openfortivpn from scratch. | |
# | |
# > Now clone and build openfortivpn: | |
# git clone https://github.com/adrienverge/openfortivpn.git | |
# cd openfortivpn | |
# ./autogen.sh | |
# ./configure && make | |
# > And finally, install it system-wide: | |
# sudo make install | |
# | |
# The user running this script must belong to the sudo group. In case they | |
# don't, run: | |
# sudo usermod -aG sudo USER | |
# | |
# HOW IT WORKS | |
# 1. Opens firefox and navigates to https://${SERVER}/remote/login | |
# 2. After a succesful authentication, SVPNCOOKIE | |
# is saved to sessionstore-backups/recovery.jsonlz4 on Firefox's profile. | |
# 2. The script will use openfortivpn to start the tunnel providing it | |
# with SVPNCOOKIE (--cookie-on-stdin < cookie_file) because | |
# it does not support SAML (yet!). | |
# | |
# NOTES | |
# > Firefox will store cookies in sessionstore-backups/recovery.jsonlz4 only when | |
# the following options within "Privacy&Security/History" are not enabled: | |
# "Always use private(...)" and "Clear history when Firefox closes". | |
# > SVPNCOOKIE is saved into ~/.${USER}.svpncookie with 0600 perms and removed | |
# when exiting the script (thanks to trap). | |
# > Remember, this script is a HACK. So if something does not work for you, | |
# change whatever you think needs to be changed and please, READ these notes | |
# BEFORE assuming that it won't work at all! | |
# > This script has been tested on some OS but not all. And remember that, for | |
# some systems, custom configurations of Firefox may affect this script in | |
# different ways. So please UNDERSTAND how this script works and then adjust | |
# whatever you think you need to adjust to make it work. | |
# > Tested on: Raspbian 11; Debian 10, 11; Ubuntu/Kubuntu 20.04,22.04. | |
# | |
# USAGE | |
# Run the script like this to start a new authentication process against | |
# a SAML server and get the VPN up and running automatically: | |
# | |
# ./fuckForticlient.sh -S vpnserver -c | |
# | |
# In case you already have a valid non-expired SVPNCOOKIE, you can re-use | |
# it like this: | |
# | |
# ./fuckForticlient.sh -S myvpnserver -s | |
# | |
# Once the vpn is up and running, you can close the terminal and the | |
# connection will hold; you can disconnect by pressing CTRL+c at any | |
# time. If you close the terminal and the connection is still alive, | |
# you can kill it with: | |
# | |
# sudo kill `pidof openfortivpn` | |
# | |
# Run the script with "-h" to get a list of valid options and some | |
# running examples. | |
# | |
##################################################################################### | |
VERSION="1.1" | |
AUTHOR="Toni Castillo Girona" | |
EMAIL="[email protected]" | |
TWITTER="@Disbauxes" | |
# Fortinet VPN SAML server, replace with your own or use -S SERVER | |
# E.g: /fuckForticlient.sh -S myvpnserver.domain.org -c | |
SERVER="" | |
# Default timeout in seconds to wait for the SVPNCOOKIE to appear: | |
TIMEOUT=120 | |
# Options to pass to the firefox browser (it only applies when there's no | |
# previous Firefox instance already running): | |
OPTIONS="--window-size 150,150" | |
# By default, we don't show the SVPNCOOKIE on screen: | |
SHOWCOOKIE=0 | |
# We get the current distro: | |
DISTRO=`lsb_release -i|cut -d":" -f2|tr -d '\t'` | |
DISTROV=`lsb_release -r|awk '{print $2}'` | |
# No debug by default (use -D to change it) | |
DEBUG=0 | |
##################################################################################### | |
# Colors | |
##################################################################################### | |
clRed='\033[1;31m' | |
clYellow='\033[1;33m' | |
clGreen='\033[1;32m' | |
clNone='\033[0m' | |
##################################################################################### | |
# cleanup() | |
##################################################################################### | |
cleanup(){ | |
# Removes SVPNCOOKIE: | |
test -r $HOME/.${USER}.svpncookie && rm $HOME/.${USER}.svpncookie | |
test -d /tmp/openfortivpn && rm -rf /tmp/openfortivpn | |
} | |
##################################################################################### | |
# banner() | |
# Shows the banner ;-) | |
##################################################################################### | |
banner (){ | |
echo " ___ __ ____ __ _ ___ __ " | |
echo " / _/_ ______/ /__ / __/__ ____/ /_(_)___/ (_)__ ___ / /_" | |
echo " / _/ // / __/ '_// _// _ \/ __/ __/ / __/ / / -_) _ \/ __/" | |
echo -e "/_/ \_,_/\__/_/\_\/_/ \___/_/ \__/_/\__/_/_/\__/_//_/\__/ ${clRed}v${VERSION}" | |
echo "" | |
echo -e "${clYellow}2022-2023 by: $AUTHOR ($TWITTER)" | |
echo -e "${clNone}" | |
} | |
##################################################################################### | |
# getProfilePath() | |
# Gets the Firefox profile path or returns an error. Depending on the distro, | |
# sometimes the profile path is not on the usual path (like with Ubuntu because | |
# it uses SNAPd) | |
##################################################################################### | |
getProfilePath(){ | |
profilepath="" | |
case $DISTRO in | |
Debian|Raspbian|Parrot) | |
if [ ! -d ${HOME}/.mozilla/firefox ]; then | |
return -1 | |
fi | |
profilepath="${HOME}/.mozilla/firefox" | |
;; | |
Ubuntu) | |
if [ "$DISTROV" != "20.04" ]; then | |
if [ ! -d ${HOME}/snap/firefox/common/.mozilla/firefox ]; then | |
return -1 | |
fi | |
profilepath="${HOME}/snap/firefox/common/.mozilla/firefox" | |
else | |
if [ ! -d ${HOME}/.mozilla/firefox ]; then | |
return -1 | |
fi | |
profilepath="${HOME}/.mozilla/firefox" | |
fi | |
;; | |
*) | |
# We return the usual path, but who knows if it even works at all! | |
profilepath="${HOME}/.mozilla/firefox" | |
;; | |
esac | |
echo "${profilepath}" | |
return 0 | |
} | |
##################################################################################### | |
# enumerateProfiles() | |
# Enumerates all profiles available by reading profiles.ini. This can be used | |
# to determine which profile to use to override the automatic detection of the | |
# default profile with -p. | |
##################################################################################### | |
enumerateProfiles(){ | |
profilepath=`getProfilePath` | |
if [ $? -eq 0 ]; then | |
profs=`cat ${profilepath}/profiles.ini|grep Path|cut -d"=" -f2|xargs` | |
for p in ${profs}; do | |
pName=`echo ${p}|cut -d"." -f2` | |
echo -e " [>] Name : ${clGreen}${pName}" | |
echo -en "${clNone}" | |
echo -e " [>] Path (-p): ${clGreen}${profilepath}/${p}" | |
echo -en "${clNone}" | |
echo "" | |
done | |
else | |
echo "[!] Unable to determine Firefox profile PATH!!!" | |
return -1 | |
fi | |
} | |
##################################################################################### | |
# getFirefoxProfile() | |
# Returns the path for the default Firefox profile or "" if it cannot determine | |
# where it is. Using -p overrrides this function. | |
# If the user has decided to write the profile-path to use right in the file | |
# ~/.${USER}.fuckforticlient-profile, this functions simply returns the contents | |
# of ~/.${USER}.fuckforticlient-profile. | |
##################################################################################### | |
getFirefoxProfile(){ | |
# Do we have a saved profile to use? | |
if [ -r ~/.${USER}.fuckforticlient-profile ]; then | |
profilep=`cat -v ~/.${USER}.fuckforticlient-profile` | |
# If it's not empty and the directory is valid: | |
if [ ! -z "${profilep}" -a -d "${profilep}" ]; then | |
echo "${profilep}" | |
return 0 | |
fi | |
fi | |
# First, we get the profile path depending on the Distro: | |
profilepath=`getProfilePath` | |
if [ $? -eq 0 ]; then | |
prof=`cat ${profilepath}/profiles.ini |grep Path=|grep -i default|tail -1|cut -d"=" -f2` | |
if [ ! -z "$prof" ]; then | |
echo "${profilepath}/${prof}/sessionstore-backups" | |
return 0 | |
else | |
echo "" | |
return -2 | |
fi | |
else | |
echo "" | |
return -2 | |
fi | |
} | |
##################################################################################### | |
# getCookie(profile,waitForIt) | |
# Waits up to TIMEOUT seconds for the SVPNCOOKIE to appear. Stores the cookie in | |
# $HOME/${USER].svpncookie and returns 0; returns 1 otherwise. | |
##################################################################################### | |
getCookie(){ | |
# Storage file where the cookie is stored in firefox: | |
storage="$1" | |
waitForIt=$2 | |
# We save the current umask value first: | |
curUmask=`umask` | |
# We change it to 0077: | |
umask 077 | |
# We try to grab the cookie right away: | |
c=`lz4jsoncat ${storage}/recovery.jsonlz4 2>/dev/null|jq '.cookies[]|select(.name!=null)|select(.name|contains("SVPNCOOKIE"))|.value'` | |
if [ ! -z "$c" ]; then | |
echo "SVPNCOOKIE=${c}" > $HOME/.${USER}.svpncookie | |
sed -i 's/\"//g' $HOME/.${USER}.svpncookie | |
# We restore umask: | |
umask $curUmask | |
return 0 | |
fi | |
# We only wait if waitForIt == 1 | |
test $waitForIt -eq 0 && return 1 | |
# We will wait until the cookie is already there... | |
while inotifywait -e modify -t $TIMEOUT --format '%w%f' ${storage} -q >/dev/null 2>&1; | |
do | |
if [ -r ${storage}/recovery.jsonlz4 ]; then | |
# Make sure we check for the cookie once the file changes: | |
c=`lz4jsoncat ${storage}/recovery.jsonlz4 2>/dev/null|jq '.cookies[]|select(.name!=null)|select(.name|contains("SVPNCOOKIE"))|.value'` | |
if [ ! -z "$c" ]; then | |
#echo ${c} | |
echo "SVPNCOOKIE=${c}" > $HOME/.${USER}.svpncookie | |
sed -i 's/\"//g' $HOME/.${USER}.svpncookie | |
# We restore umask | |
umask $curUmask | |
return 0 | |
fi | |
fi | |
done | |
# If we reach this, we exit with error (timeout!): | |
# We restore umask: | |
umask $curUmask | |
return 1 | |
} | |
##################################################################################### | |
# usage() | |
# Shows the help screen and some examples and exits | |
##################################################################################### | |
usage(){ | |
echo "" | |
echo -ne "Usage: `basename $0` -L|-u|-d|[-p][-P][-t][-v][-S][-c][-s] \n" \ | |
"\t-h Shows this help and exits.\n" \ | |
"\t-c Opens firefox to perform SAML Authentication.\n" \ | |
"\t-s Tries to re-use a previous SVPNCOOKIE.\n" \ | |
"\t-p PATH Overrides the detection of the Firefox Profile to use.\n" \ | |
"\t-P Saves chosen Firefox Profile (-p) as the default one.\n" \ | |
"\t-t SECONDS Sets the timeout to wait for the SVPNCOOKIE cookie to SECONDS.\n" \ | |
"\t-v Shows the SVPNCOOKIE cookie on screen.\n" \ | |
"\t-S SERVER Authenticates against VPN server SERVER .\n" \ | |
"\t-L Lists all Firefox profiles detected and exits.\n" \ | |
"\t-d Removes Forticlient from the system and exits.\n" \ | |
"\t-u Updates openfortivpn and exits.\n" \ | |
"\t-i Shows current assigned VPN Ip address and exits.\n" \ | |
"\t-D Runs the script in debug mode.\n" \ | |
"Examples:\n" \ | |
"\t`basename $0` -L \n" \ | |
"\t`basename $0` -S myserver.org.edu -c\n" \ | |
"\t`basename $0` -i\n" \ | |
"\t`basename $0` -t 200 -S myvpnserver.com -c \n" \ | |
"\t`basename $0` -p /home/u1/.mozilla/firefox/myprofile -S vpnsrv -c\n" \ | |
"\t`basename $0` -p /home/u1/.mozilla/firefox/myprofile -P -S vpnsrv -c\n" \ | |
"\t`basename $0` -p /home/u1/.mozilla/firefox/myprofile -S vpnsrv -s\n" \ | |
"\t`basename $0` -t 200 -p /home/u1/.mozilla/firefox/myprofile -S vpnsrv -c\n" \ | |
"\n" \ | |
"Extra options for openfortivpn \n" \ | |
"\t FUCKFORTICLIENT_OPTS=\"--op1 --op2 ...\" `basename $0` options ... \n" \ | |
"Example:\n" \ | |
"\tFUCKFORTICLIENT_OPTS=\"--no-dns\" `basename $0` -S myserver.org.edu -c\n" | |
exit 0 | |
} | |
##################################################################################### | |
# sanityCheck() | |
# Performs some trivial sanity checks before attempting to run the script. | |
##################################################################################### | |
sanityCheck(){ | |
# Test for jq presence: | |
type jq >/dev/null 2>&1|| return 1 | |
# Test for lz4jsoncat | |
type lz4jsoncat >/dev/null 2>&1 || return 1 | |
# Test for openfortivpn: | |
type openfortivpn >/dev/null 2>&1|| return 1 | |
# Make sure openfortivpn supports "--cookie-on-stdin": | |
/usr/local/bin/openfortivpn --help|grep cookie >/dev/null|| return 1 | |
# Make sure we have Firefox installed: | |
type firefox >/dev/null 2>&1 || return 1 | |
# Make sure the user running this script belongs to the sudo group: | |
id -Gn |grep sudo >/dev/null || return 1 | |
return 0 | |
} | |
##################################################################################### | |
# checkFirefoxSettings() | |
# Makes sure the following two options ARE not enabled on the chosen | |
# firefox profile to ensure the SVPNCOOKIE cookie will be stored in the | |
# sessionstore-backups/recovery.jsonlz4 file: | |
# | |
# browser.privatebrowsing.autostart | |
# privacy.sanitize.pending | |
##################################################################################### | |
checkFirefoxSettings(){ | |
# Now, if the sessionstore-backups directory DOES NOT EXIST at all, | |
# it is obvious these options are ALREADY ENABLED! | |
if [ ! -d "${fProfile}" ]; then | |
return 1 | |
else | |
# Otherwise, make sure everything else fits: | |
grep -q "browser.privatebrowsing.autostart" "${fProfile}/../prefs.js" | |
# Not found, maybe the next option? | |
if [ $? -eq 1 ]; then | |
# sanitize pending should'nt have anything between "[]": | |
sa=`cat "${fProfile}/../prefs.js"|grep "privacy.sanitize.pending"|awk '{print $2}'|cut -d"\"" -f2` | |
if [ "${sa}" != "[]" ]; then | |
if [ "${sa}" != "[{\\" ]; then | |
return 1 | |
else | |
return 0 | |
fi | |
else | |
return 0 | |
fi | |
fi | |
return 1 | |
fi | |
} | |
##################################################################################### | |
# getVPNIp() | |
# Returns the current assigned VPN IP Address or error | |
# It assumes the VPN device used by openfortivpn is always "ppp0", quite naive!! | |
##################################################################################### | |
getVPNIp(){ | |
data=`ip a list ppp0 2>/dev/null|grep "inet"` | |
if [ $? -eq 0 ]; then | |
echo ${data}|awk '{print $2}' | |
return 0 | |
else | |
echo "" | |
return 1 | |
fi | |
} | |
##################################################################################### | |
# checkAnotherInstance() | |
# Checks whether there's a running instance of openfortivpn. If there is, | |
# exits with error. Otherwise, returns 0. | |
##################################################################################### | |
checkAnotherInstance(){ | |
pidof -q openfortivpn | |
if [ $? -eq 0 ]; then | |
echo -e "${clRed}[!] Another openfortivpn instance detected!${clNone}" | |
# If there is another instance running, it is probably because there is | |
# an active VPN connection running already: | |
ipvpn=`getVPNIp` | |
if [ $? -eq 0 ]; then | |
echo -e "[+] Current VPN IP: ${clGreen}${ipvpn}${clNone}" | |
echo -e "${clRed}[!] If you kill openfortivpn instance, you will be disconnected!${clNone}" | |
fi | |
echo -e "[!] Run: ${clGreen} sudo kill `pidof openfortivpn`${clNone} or just press CTRL+c on the terminal window..." | |
exit 1 | |
fi | |
return 0 | |
} | |
##################################################################################### | |
# checkOpenfortivpn() | |
# Returns 0 if the installed version of Openfortivpn is from the repo or 1 | |
# otherwise. For some distros, the included openfortivpn from the official repos | |
# does not support "--cookie-on-stdin" | |
##################################################################################### | |
checkOpenfortivpn(){ | |
dpkg -l |grep openfortivpn|grep -E "^ii" >/dev/null 2>&1 | |
return $? | |
} | |
# Tiddy things up if we cancel or finish the script: | |
trap "cleanup" EXIT | |
# We show the banner: | |
banner | |
# We show the detected distro: | |
echo -e "[*] Detected distro: ${clRed}$DISTRO${clNone}, version: ${clRed}$DISTROV" | |
echo -ne "${clNone}" | |
# First of all, we perform a trivial sanity check: | |
sanityCheck | |
if [ ! $? -eq 0 ]; then | |
echo "[!] Sanity check reported an error." | |
echo "[!] Make sure you have installed all the pre-requisites first." | |
echo "[!] Make sure you belong to the sudo group also." | |
exit 0 | |
fi | |
# We get Firefox default profile first thing: | |
fProfile=`getFirefoxProfile` | |
if [ ! $? -eq 0 ]; then | |
# Has the user provided us with a custom profile path? | |
args="$*" | |
if [[ "$args" != *"-p"* ]]; then | |
echo -e "${clRed}[!] Unable to determine the default firefox profile!...${clNone}" | |
echo "[+] Enumerating all profiles now...:" | |
# So we show all the detected profiles just in case: | |
enumerateProfiles | |
echo "[!] Please, re-run this script with the -p option! Aborting..." | |
exit 1 | |
fi | |
else | |
echo -e "[*] Auto-detected firefox profile: ${clRed}$fProfile" | |
echo -ne "${clNone}" | |
fi | |
# If we have a valid Firefox profile (either by auto-detecting it or because the | |
# user has specified the -p and/or -P parameters, we make sure Firefox settings | |
# will save the cookie to the restore file before going further... | |
checkFirefoxSettings | |
if [ $? -eq 1 ]; then | |
echo -e "${clRed}[!] Error; make sure the following two options are disabled on Firefox" | |
echo -e "\t>Never Remember History" | |
echo -e "\t>Always use private browsing mode" | |
echo -e "\t>Clear history when Firefox closes" | |
echo -e "${clNone}[!] Go to ${clGreen}Preferences/Privacy&Security/History ${clNone}to fix it!" | |
echo "[!] Aborting now ... " | |
exit 1 | |
fi | |
# We get openfortivpn version and show it: | |
opv=`openfortivpn --version` | |
echo -e "[*] Openfortivpn version: ${clRed}$opv" | |
echo -en "${clNone}" | |
# Is it installed from a repo or from github? | |
checkOpenfortivpn | |
if [ $? -eq 0 ]; then | |
echo -e "[*] Openfortivpn installed from: ${clRed}REPO" | |
else | |
echo -e "[*] Openfortivpn installed from: ${clRed}GITHUB" | |
fi | |
echo -en "${clNone}" | |
# Show any extra openfortivpn parameter: | |
if [ ! -z "$FUCKFORTICLIENT_OPTS" ]; then | |
echo -e "[*] Openfortivpn extra args: $clGreen$FUCKFORTICLIENT_OPTS " | |
echo -en "${clNone}" | |
fi | |
# Process arguments: | |
while getopts "Licshut:p:PvdDS:" opt; do | |
case "$opt" in | |
# Shows usage message and exits: | |
h) | |
usage | |
exit 0 | |
;; | |
D) | |
echo -e "[*]${clYellow} Debug mode enabled!${clNone}" | |
DEBUG=1 | |
set -x | |
;; | |
u) | |
# Clones the repository first: | |
echo "[*] Updating openfortivpn ... " | |
echo -e "\t[>] Cloning ..." | |
git clone https://github.com/adrienverge/openfortivpn.git /tmp/openfortivpn>/dev/null 2>&1 | |
if [ $? -eq 0 ]; then | |
cd /tmp/openfortivpn >/dev/null 2>&1 | |
echo -e "\t[>] Running autogen.sh ... " | |
./autogen.sh >/dev/null 2>&1 | |
echo -e "\t[>] Running configure & make ..." | |
( ./configure && make )> /dev/null 2>&1 | |
echo -e "\t[>] Running make install ... " | |
sudo make install >/dev/null 2>&1 | |
if [ $? -eq 0 ]; then | |
echo -e "[*] ${clGreen}openfortivpn updated sucessfully!" | |
echo -ne "${clNone}" | |
else | |
echo -e "[!] ${clRed}error updating openfortivpn." | |
echo -ne "${clNone}" | |
fi | |
# We clean the directory: | |
cd .. && rm -rf /tmp/openfortivpn >/dev/null 2>&1 | |
else | |
echo -e "[!] ${clRed}Unable to clone openfortivpn!" | |
exit -1 | |
fi | |
;; | |
# Shows current assigned VPN Ip address (if any) and exits: | |
i) | |
ipvpn=`getVPNIp` | |
if [ $? -eq 0 ]; then | |
echo -e "[+] Current VPN Ip: ${clGreen}${ipvpn}${clNone}" | |
else | |
echo -e "[!] ${clRed}You are not connected to the VPN!${clNone}" | |
fi | |
exit 0 | |
;; | |
# It will show the SVPNCOOKIE on screen: | |
v) | |
SHOWCOOKIE=1 | |
;; | |
# Shows all detected profiles and quits: | |
L) | |
echo "[*] Enumerating Firefox profiles ... " | |
enumerateProfiles | |
exit 0 | |
;; | |
# Firefox default profile override: | |
p) | |
fProfile="$OPTARG/sessionstore-backups" | |
echo "[*] Overriding detected Firefox profile" | |
# Make sure the directory is valid: | |
if [ ! -d "$fProfile" ]; then | |
echo "[!] Error, ${fProfile} does not exist! Aborting..." | |
exit 1 | |
fi | |
;; | |
P) | |
# We do not really care if the fProfile variable has been filled | |
# by autodetecting the Firefox profile or because the user has | |
# used the "-p" parameter; we save it to ~/.${USER}-fuckforticlient-profile | |
# anyways...: | |
echo -e "[+] Saving profile to: ${clGreen}~/.${USER}.fuckforticlient-profile" | |
echo -ne "${clNone}" | |
echo -n "${fProfile}" > ~/.${USER}.fuckforticlient-profile | |
;; | |
# Timeout for the SVPNCOOKIE override: | |
t) | |
TIMEOUT=$OPTARG | |
;; | |
# Overrides SERVER and tries to authenticate against -S SERVER: | |
S) | |
SERVER="$OPTARG" | |
;; | |
# Removes Forticlient: | |
d) | |
echo "[*] Removing Forticlient as requested ... " | |
dpkg -l forticlient >/dev/null 2>&1 | |
if [ $? -eq 0 ]; then | |
sudo dpkg --purge forticlient | |
else | |
echo "[!] Forticlient is not installed!" | |
fi | |
exit 0 | |
;; | |
# Tries to get the SVPNCOOKIE without re-opening firefox and | |
# then uses the cookie to start the VPN: | |
s) | |
# First of all, if there is a running openfortivpn instance, | |
# we exit and we do not try to re-connect: | |
checkAnotherInstance | |
# We do nothing if we do not specify a valid VPN-SSL server: | |
if [ -z "$SERVER" ]; then | |
echo "[!] Please, re-run the script with -S VPNSERVER" | |
exit 0 | |
fi | |
echo -e "[*] Firefox profile: ${clRed}$fProfile" | |
echo -ne "${clNone}" | |
echo "[*] Trying to re-use a previous SVPNCOOKIE..." | |
getCookie "$fProfile" "0" | |
if [ ! $? -eq 0 ]; then | |
echo "[!] Unable to get SVPNCOOKIE; aborting..." | |
exit 0 | |
else | |
test $SHOWCOOKIE -eq 1 && echo "[*] `cat $HOME/.${USER}.svpncookie`" | |
echo -e "[*] ${clGreen}SVPNCOOKIE sucessfully retrieved!" | |
echo -ne "${clNone}" | |
# We save the cookie file to a variable first: | |
cookie=$HOME/.${USER}.svpncookie | |
# We connect to the vpn now: | |
test $DEBUG -eq 1 && dbg="-vvv" | |
sudo /usr/local/bin/openfortivpn $SERVER:443 --cookie-on-stdin < ${cookie} ${dbg} ${FUCKFORTICLIENT_OPTS} | |
if [ ! $? -eq 0 ]; then | |
echo "${clRed}[!] Error, expired cookie probably...${clNone}" | |
echo "[!] Close Firefox and re-lanch the script using -c" | |
exit 1 | |
fi | |
fi | |
;; | |
# Establishes a new connection by opening Firefox first. The user | |
# needs to authenticate against the Fortinet server first: | |
c) | |
# First of all, if there is a running openfortivpn instance, | |
# we exit and we do not try to re-connect: | |
checkAnotherInstance | |
# We do nothing if we do not specify a valid VPN-SSL server: | |
if [ -z "$SERVER" ]; then | |
echo "[!] Please, re-run the script with -S VPNSERVER" | |
exit 0 | |
fi | |
# FIX: make sure to use the right profile!!!! | |
profName=`dirname ${fProfile}|rev|cut -d"." -f1|rev|cut -d"/" -f1` | |
echo -e "[*] Opening Firefox for SAML login with: ${clGreen}-P ${profName}...${clNone}" | |
firefox -P ${profName} ${OPTIONS} https://${SERVER}/remote/login >/dev/null 2>&1 & | |
echo -e "[*] Firefox profile: ${clRed}$fProfile" | |
echo -ne "${clNone}" | |
echo -e "[*] Authenticating against ${clRed}https://$SERVER ..." | |
echo -ne "${clNone}" | |
# There's some delay before firefox stores the cookie unless it is closed, | |
# in which case it's inmediately there. | |
echo -e "[*] Waiting up to ${clRed}$TIMEOUT seconds${clNone} until the cookie appears..." | |
# Gets the cookie: | |
getCookie "$fProfile" "1" | |
if [ ! $? -eq 0 ]; then | |
echo "[!] Unable to get SVPNCOOKIE; aborting..." | |
exit 0 | |
else | |
test $SHOWCOOKIE -eq 1 && echo "[*] `cat $HOME/.${USER}.svpncookie`" | |
echo -e "[*] ${clGreen}SVPNCOOKIE sucessfully retrieved!" | |
echo -ne "${clNone}" | |
# We save the cookie file to a variable first: | |
cookie=$HOME/.${USER}.svpncookie | |
# We connect to the vpn now: | |
test $DEBUG -eq 1 && dbg="-vvv" | |
sudo /usr/local/bin/openfortivpn ${SERVER}:443 --cookie-on-stdin < ${cookie} ${dbg} ${FUCKFORTICLIENT_OPTS} | |
if [ ! $? -eq 0 ]; then | |
echo -e "${clRed}[!] Error, expired cookie probably...${clNone}" | |
echo "[!] Close Firefox and re-lanch the script using -c" | |
exit 1 | |
fi | |
fi | |
;; | |
esac | |
done |
Good stuff!
I've done a similar thing using a main shell and a python snippet (Selenium) helper to fetch the token, but this looks cleaner and more well-rounded with the benefit of less dependencies.
(fuck FortiClient btw)
Hey, would you be able to adapt this script to run on MacOS?
Thanks.
Sorry mate, I don't have access to an OS/X installation. Maybe someone else can help instead....how about you?
Good stuff!
I've done a similar thing using a main shell and a python snippet (Selenium) helper to fetch the token, but this looks cleaner and more well-rounded with the benefit of less dependencies.
(fuck FortiClient btw)
Thanks! XD
Hey, would you be able to adapt this script to run on MacOS?
Thanks.
Well, you can try my workaround. I have a work m8 that uses it with OSX daily.
Create the files below and do the following:
- Replace VPN_HOST inside get_cookie.py with your real VPN host
- Replace both host and trusted cert inside vpn.config
- Run vpn_connect.sh
vpn_connect.sh
#!/usr/bin/env bash
REQUIREMENTS_FILE="requirements.txt"
CONFIG_FILE="vpn.config"
ENV_DIR=".env"
# Creates vevn if it doesn't exist
[[ ! -d .env ]] && python3 -m venv ${ENV_DIR}
source ${ENV_DIR}/bin/activate && pip install -r ${REQUIREMENTS_FILE}
# Retrieves the token
python3 get_cookie.py > .vpn_cookie || exit 1
# Connects to VPN
COOKIE=$(cat .vpn_cookie)
sudo openfortivpn -v -c $CONFIG_FILE --cookie="SVPNCOOKIE=${COOKIE}"
get_cookie.py
#!/usr/bin/env python3
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
VPN_HOST="https://foo.bar.com/"
#driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager(version='114.0.5735.90').install()))
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
driver.get(VPN_HOST)
cookie = WebDriverWait(driver, timeout=120).until(lambda d: d.get_cookie('SVPNCOOKIE'))['value']
driver.quit()
print(cookie)
requirements.txt
selenium==4.10.0
webdriver-manager>=3.8.6
vpn.config
host = foo.bar.com
port = 443
trusted-cert = 015d40adce838fd3dbcdaf7d8d7438aabb6c57e5e9ab020dbb536fff7eba2e1d
I´m really interested on this script and seems to work but, after login, shows this message on Firefox:
"The SSL-VPN portal has been enabled for tunnel mode use only. Forticlient is required to connect".
I´m not a VPN expert. Could anyone help us?
Could be related to this bug?
https://docs.fortinet.com/document/forticlient/7.2.1/linux-release-notes/254811/known-issues
Thanks and best regards
Just in case, from now on the development of this project will continue as a proper git repo:
https://github.com/nonamed01/fuckForticlient
So clone the repo and feel free to create pull requests, issues, or just be a part of its development.... When I wrote this script, the main idea was to solve a problem that I though temporary .... and , well, one year in and the official Forticlient GNU/Linux client is total crap ... So, well, it seems we'll be using this for a long time.....
not working on fedora 39
[*] Detected distro: Fedora, version: 39
[!] Sanity check reported an error.
[!] Make sure you have installed all the pre-requisites first.
[!] Make sure you belong to the sudo group also.
not working on fedora 39
[*] Detected distro: Fedora, version: 39 [!] Sanity check reported an error. [!] Make sure you have installed all the pre-requisites first. [!] Make sure you belong to the sudo group also.
lz4json package does not exist, not sure how to build it, so yeah, no solution for Fedora unfortunately... And official Forticlient for Linux is garbage.
If someone interested
For external browser I implemented a script to retrieve token on repository https://github.com/filippor/XdgOpenSaml
the process is
1 start a server to listen on localhost:8020/?id=
2 open in external browser url + "/remote/saml/start?redirect=1"
3 server receive a call and with retieved id call url + "/remote/saml/auth_id?id=" + id to retrieve cookie
you can see a sample implementation in this repo https://github.com/filippor/XdgOpenSaml/blob/main/XdgOpenSaml.java that write the cookie to standard out like openfortivpn-webview
XdgOpenSaml url:port | sudo openfortivpn url:port --cookie-on-stdin --pppd-use-peerdns=
Just a FYI: I created a script that can arrange the login against Azure AD without starting a browser GUI. Nice to automate the login process. The script is available here:
https://github.com/zizzencs/openfortivpn-azure-ad-login-helper
Hey, would you be able to adapt this script to run on MacOS?
Thanks.