This is probably very specific to my workplace. However, at least some others have the same setup, because I was able to find resources online that apply to it.
- Install the
openconnect
package. - Install the
pan-globalprotect-okta
python script.- There does not appear to be an official package for easy installation, so I'm just going to use git.
- Make sure
git
andpython-pip
are installed. It seems thatpython
is already installed from one of the Arch Linux base packages. - Choose a directory to install the script. A subdirectory named
pan-globalprotect-okta
will be created, so change to the parent directory of your choosing.- In order to be accessible to the
nm-openconnect
user, if you plan to integrate with Network Manager later, the location should be accessible to that user, so outside your home directory. I am using/usr/local/lib/
currently.
- In order to be accessible to the
git clone https://github.com/arthepsy/pan-globalprotect-okta.git
pip install requests lxml pyotp
to install dependencies of the script.
- Edit
./pan-globalprotect-okta/gp-okta.conf
. - Set
vpn_url
andokta_url
according to your workplace. - Set
username
. - Comment out
password
so that it will prompt you for it. - Set
totp.okta
andtotp.google
if you wish to do automatic 2-factor authentication. - Set
gateway
. When connecting to the VPN, openconnect may prompt for the gateway to connect to. Since the script is piping the authentication token into openconnect, it will not be able to receive input from the user. If you setgateway
, then the script will also pipe a response to the gateway question. If you're not sure, leave it blank and continue. If openconnect has an error along the lines offgets (stdin): Resource temporarily unavailable
, look for a prompt with a list of gateways and enter the name of one in the configuration file. - Set
openconnect_args
to--os win --csd-wrapper=hipreport.sh
. The VPN may not be configured to allow Linux, so--os win
makes it report that it is Windows. More on the--csd-wrapper
option later. - By default,
gp-okta.py
will print the command to run openconnect. If you want it to execute the command instead of simply printing it, setexecute
to1
. I found it easier to leave if off for now until I got it working. - You may want to set
openconnect_fmt
to avoid executingopenconnect
just to find out the version. Later instructions around NetworkManager integration will also break this auto-detection. Foropenconnect
version 8.10+, setopenconnect_fmt = <cookie><gateway_name><cookie>
.
GlobalProtect uses a feature called "Host Integrity Protection" (HIP) to enforce certain policies on the client that is connecting to the VPN. The idea is that in order to connect to the VPN, your computer has to meet certain requirements such as an encrypted file system, anti-virus software, and up-to-date patches. I personally feel like this should be a separate concern from actual VPN connectivity. Having those requirements met does generally increase security, but not for the VPN itself, since the HIP report can be spoofed easily. It is essentially a very basic "trusted client", which is fundamentally insecure.
Nevertheless, in order for the VPN connectivity to work, a HIP report that satisfies the policies in place will have to be generated and sent to the server after connecting. Note that some servers will claim to require a HIP report but not actually enforce it, while others may enforce it by blocking access through the VPN without actually preventing the VPN connection or providing any error feedback. The latter is a bit of a pain, because it won't tell you which policies are not met.
The easiest way to get a good HIP report is to take one from a working system. If you have access to a Windows one, here is how I got it:
- Open GlobalProtect using the tray icon.
- Use the gear icon to go to
Settings
. - Go to the
Troubleshooting
tab. - For
Logging Level
selectDump
. - Click
Start
. - Use the tray icon to connect to your VPN.
- Try to access an internal web page in order to verify that the VPN is working.
- Disconnect the VPN.
- Back in settings, click
Stop
. - Go to
C:\Program Files\Palo Alto Networks\GlobalProtect\PanGPS.log
. - Find the last instance of
<hip-report name="hip-report">
and copy this entire XML tag. It's pretty long. - The log appears to break the output into chunks, so you will have to remove some of the logging junk. Every so often you will see something like:
Remove that junk. It could be in the middle of a line, so make sure it's valid.
(T16280) 10/18/19 11:38:58:015 Dump (4847): ResponseToClient.txt_output:
- You now have a known good HIP report. However, it will be missing some tags at the beginning and it will have things like dates and IP addresses hard-coded. But it is working as of now.
Next, make a copy of the example script included with openconnect located at /usr/lib/openconnect/hipreport.sh
. I put mine in the same directory as the pan-globalprotect-okta
repo for convenience. If you choose a different location, be sure to edit gp-okta.conf
accordingly.
Edit the copy of hipreport.sh
that you made. The script basically does some calculations and then outputs the XML with a few values substituted. You will want some combination of this example and script and the known good HIP report you got earlier.
- You could start by replacing the
<categories>
tag with the one from your known good HIP report. From there, decide what information to trim out or change. Be sure to add substitutions for$IP
,$IPV6
,$COMPUTER
, etc. and for dates use$NOW
,$DAY
,$MONTH
, and$YEAR
. - Alternatively, you can just adjust what's there in the script by comparing it to your known good HIP report.
- Either way, make sure that the tags from the script before
<categories>
are preserved, because for some reason they do not appear in the HIP report printed to the log, but they appear to be necessary. - Finally, you may want to use the host ID from the known good HIP report. The example script uses the value
deadbeef-dead-beef-dead-beefdeadbeef
which may or may not be accepted by the server. Either way, it looks suspicious. If you got the known good HIP report from someone else's computer, then you may want to get a HIP report from your computer if possible (even if it doesn't work) to use for unique IDs such as this.
Openconnect needs permission to access the network devices. The easiest way is to just run openconnect as root, but that presents some security concerns. To do it anyway for testing purposes, for example, just uncomment the line openconnect_cmd = sudo openconnect
in gp-okta.conf
.
Options for running it not as root are documented here: https://www.infradead.org/openconnect/nonroot.html
./gp-okta.py ./gp-okta.conf
to run the script.- If you have not configured it to execute, copy the printed command and run it.
Doing this makes it easy to connect and disconnect with a few simple clicks. It also solves the problem of having to run it as root. However, it won't work out-of-the-box because of the extra Okta authentication steps.
- Install the
networkmanager-openconnect
package. systemctl restart NetworkManager.service
.- Right-click the network tray icon, click
Configure Network Connections
. - Use the plus icon to create a new connection.
- Select
PAN Global Protect (openconnect)
andCreate
. - Enter a name and gateway.
- Note that "gateway" is a bit ambiguous. In this context it means the DNS host name to connect to. After connecting to the GlobalProtect VPN server in openconnect, it will ask you for another "gateway" selection which determines which server to establish the VPN connection with after authenticating.
- The other settings don't really matter. The only one that is applicable to our VPN setup is the
CSD Wrapper Script
, but in my experience, it does not actually pass this value to openconnect. - Click
Save
. - Edit the
.nmconnection
file so that the openconnect plugin for NetworkManager thinks it has all the credentials it needs. If you don't do this, it will display an authentication window when you try to connect, and there is no way to get past this with the Okta authentication in place.- The
.nmconnection
files are located under/etc/NetworkManager/system-connections/
. Edit the one for the connection you created. - Set these values under
[vpn]
:cookie-flags=0 gateway-flags=0 gwcert-flags=0
- Create a section
[vpn-secrets]
after the[vpn]
section if it does not already exist. Set these values under it:cookie=junk gateway=my.real.gateway.com gwcert=junk
- Save the file.
systemctl restart NetworkManager.service
- The
- Create a wrapper script to replace the
openconnect
binary.- Unfortunately, this is the only way I found to hook into the connection process. It means that updates to
openconnect
may potentially break things. A better solution would probably be to enhance the openconnect plugin for Network Manager to support Okta / SAML authentication.- Since writing this, I wrote a pacman hook to fix this whenever
openconnect
is updated. More on that later.
- Since writing this, I wrote a pacman hook to fix this whenever
- Note that this will also affect any use of openconnect anywhere anytime. In my case, this is the only thing I am using openconnect for anyway, so it doesn't matter to me.
- Since writing this, I have improved the wrapper script to run
openconnect
normally when not running as thenm-openconnect
user. Now, for example, I can runopenconnect
from the terminal and it will work normally as expected.
- Since writing this, I have improved the wrapper script to run
- Rename
/usr/bin/openconnect
toopenconnect.real
. - Create the wrapper script file
/usr/bin/openconnect.wrapper.sh
.- In this repository, openconnect.wrapper.sh is provided. It takes parameters and standard input from the Network Manager plugin, runs
gp-okta.py
, does some processing, and runsopenconnect.real
with the correct arguments. It also traps signals and passes them on to openconnect so that it will terminate properly. - Make sure it has execute permissions.
- Edit any variables, such as file paths, as needed.
- In this repository, openconnect.wrapper.sh is provided. It takes parameters and standard input from the Network Manager plugin, runs
- Create a symbolic link to the wrapper script.
ln -s /usr/bin/openconnect.wrapper.sh /usr/bin/openconnect
- In this repository, openconnect.wrapper.install.sh will perform the above steps to move the real
openconnect
binary and replace it with a link to the wrapper. This is handy for restoring the function afteropenconnect
is upgraded. Copy it to/usr/bin/
. - In this repository, openconnect.wrapper.pacman.hook will run this install script whenever
/usr/bin/openconnect
is changed when installing or upgrading a package. Copy it to/usr/share/libalpm/hooks/
.
- Unfortunately, this is the only way I found to hook into the connection process. It means that updates to
- The wrapper script uses
kdialog
to prompt for the password.- Install the
kdialog
package. xhost +si:localuser:nm-openconnect
to grant access to the localnm-openconnect
user to use the X window system.- Add this command to a script under
/etc/profile.d/
to make the setting persist across reboots.
- Add this command to a script under
- Install the
- Test it.
- Click on the network tray icon.
- You should see the VPN connection listed. Hover over it and click
Connect
.
Normally, the VPN will redirect all network traffic over the VPN. This prevents access to resources on your home network or whatever other network you are connected to. The solution is split tunneling.
Another reason to do split tunneling is to take full advantage of your local internet speed rather than being limited to the internet speed through the VPN.
There are two parts to this: routing and DNS.
Routes determine where IP packets go. We want most traffic to stay on your regular network while routing specific subnets through the VPN.
- Configure the VPN network.
- Go to the
IPv4
tab. - Click the
Routes...
button. - Add the subnets you want routed through the VPN. Here are all private (non-internet) subnets; but you may not want to include all of them depending on your VPN network and home networks:
Address Netmask Gateway Metric 10.0.0.0 255.0.0.0 0.0.0.0 0 172.16.0.0 255.240.0.0 0.0.0.0 0 192.168.0.0 255.255.0.0 0.0.0.0 0
- Tick
Ignore automatically obtained routes
.- The automatic routes may include routing all traffic, or may route too much traffic.
- Tick
User only for resources on this connection
.- If you don't tick this, you may end up with a default route making it possible for other traffic to still end up on the VPN.
- Click
OK
andApply
. - You will have to re-edit the
.nmconnection
file as above, since the manual changes will be overwritten. RestartNetworkManager.service
afterward.
This will allow you to lookup host names on all of your networks, not just the VPN.
- Install
dnsmasq
package. - Create the file
/etc/NetworkManager/conf.d/dns.conf
with the content:[main] dns=dnsmasq
- Restart
NetworkManager.service
.
By default, NetworkManager will favor non-VPN networks for DNS lookup. You will have to specify which domains to use the VPN network for. This can be done through the NetworkManager GUI by configuring the "Search Domains"; however, it will break the modifications made to the .nmconnection
file. Here's how to do it manually instead:
- The
.nmconnection
files are located under/etc/NetworkManager/system-connections/
. Edit the one for the connection you created. - Set a value like this under
[ipv4]
:dns-search=domain1.com;domain2.com;
- The special value
~
can be used to indicate that any domains that do not match any of the configured search domains for any connection should be resolved through this connection. - Save the file.
systemctl restart NetworkManager.service