PHPnews.io

OpenConnect on Mac

Written by Rob Allen / Original link on Mar. 23, 2021

One of my clients has recently moved to AnyConnect VPN and I've been having routing problems with the official Mac client. As my colleagues on Linux on the project have not had these issues, I investigated and installed the OpenConnect client.

These are my notes after scouring the Internet to set it up how I want it.

Installation

I used Homebrew:

$ brew install openconnect

OpenConnect is a CLI tool. If you want a GUI in your menu bar, then also install openconnect-gui. I an not using the GUI as command line works for me.

Starting and stopping

To start the VPN:

$ sudo openconnect --user={username} {VPN URL}

(Replace {username} & {VPN URL} with the relevant details, of course.)

You can then use ctrl+C to stop it.

To avoid having to type your password for the sudo call each time, we can add a new file to /etc/sudoers.d/ to allow no password for openconnect binary.

This one-liner will do it:

$ sudo sh -c 'echo "%admin ALL=(ALL) NOPASSWD: /usr/local/bin/openconnect" > /etc/sudoers.d/openconnect'

This creates /etc/sudoers.d/openconnect with the relevant config.

Run in the background

Alternatively, you can put openconnect into the background with the --background flag. To stop the VPN, we need to find the pid so that we can kill the process:

$ sudo kill -2 `pgrep openconnect`

This will be useful for scripting it.

Avoiding typing the password

The --passwd-on-stdin flag allows us to pipe the password to openconnect like this:

$ echo "mypassword" | sudo openconnect --passwd-on-stdin --user={username} {VPN URL}

Clearly we don't want the password in our history or in our scripts, so we put it in a file such as ~/.vpn_password.

This file needs to contain the plain text password and be readable only by the current user:

$ echo "mypassword > ~/.vpn_password
$ chmod 600 ~/.vpn_password

We can now pipe the output of this file:

$ echo $(cat ~/.vpn_password) | sudo openconnect --passwd-on-stdin --user={username} {VPN URL}

We now have a working connection.

Scripting to make it easier

At this point we have enough to write a couple of functions to start and stop the VPN connection.

Place the following into ~/.bashrc:

function vpn-up() {
  local VPN_HOST={VPN URL}
  local VPN_USER={username}

  if [ ! -f ~/.vpn_password ]; then
    echo "Error: missing ~/.vpn_password"
    return
  fi
  echo "Starting the vpn ..."
  echo $(cat ~/.vpn_password) | sudo openconnect --background --passwd-on-stdin --user=$VPN_USER $VPN_HOST
}

function vpn-down() {
  sudo kill -2 `pgrep openconnect`
}

Replace {VPN URL} with the correct URL for your VPN and {username} with your VPN username.

We can now start the VPN:

$ vpn-up

and then stop it:

$ vpn-down

Nice and simple to remember!

DNS resolution

On Big Sur, I found that the VPN's DNS server wasn't registered, so I had add scripts to do that. OpenConnect will any script in /etc/vpnc/post-connect.d when the VPN connects and any script in /etc/vpnc/post-disconnect.d when the VPN disconnects, so we can create two files to handle DNS. The directories don't exist do you'll need to create them:

$ sudo sh -c 'mkdir -p /etc/vpnc/post-connect.d'
$ sudo sh -c 'mkdir -p /etc/vpnc/post-disconnect.d'

This is the "on connect" script:

/etc/vpnc/post-connect.d/use-vpn-dns:

networksetup -setdnsservers Ethernet {VPN DNS IP1} {VPN DNS IP2} {Usual DNS IP1} {Usual DNS IP2}
networksetup -setdnsservers Wi-Fi {VPN DNS IP1} {VPN DNS IP2} {Usual DNS IP1} {Usual DNS IP2}
killall -HUP mDNSResponder

Replace {VPN DNS IP1}, {VPN DNS IP2}, {Usual DNS IP1} and {Usual DNS IP2} with the correct IP addresses for your setup.

When the VPN is disconnected, we need to reset. I use DHCP, so this worked for me:

/etc/vpnc/post-disconnect.d/use-default-dns:

networksetup -setdnsservers Ethernet Empty
networksetup -setdnsservers Wi-Fi Empty
killall -HUP mDNSResponder

This clears the DNS entries and the DHCP defaults are then used. Be aware that if you use multiple VPNs, you will probably need more complicated logic.

That's it

With this in place, I can now connect and disconnect from my client's VPN with minimal fuss and, so far, everything works as I expect.

roballen roballen roballen

« Using a HasOne over a HasMany relationship in Laravel - Laravel Blade Icons Stable Release »