Pragmatism in the real world

OpenConnect on Mac

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.

DNS for a specific domain

If you need to use a particular DNS server for a specific domain you can use this in use-vpn-dns:

scutil<<HERE
d.init
d.add ServerAddresses * 1.2.3.4 5.6.7.8 
d.add SupplementalMatchDomains * specific-searchdomain.com
set State:/Network/Service/my-special-dns/DNS
exit
HERE

killall -HUP mDNSResponder

1.2.3.4 and 5.6.7.8 are the DNS servers and specific-searchdomain.com is the domain in question.

and to remove:

scutil<<HERE
remove State:/Network/Service/my-special-dns/DNS
exit
HERE

killall -HUP mDNSResponder

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.

7 thoughts on “OpenConnect on Mac

  1. Hi,
    Thanks for sharing your valuable notes. I installed openconnect using homebrew on Big Sur, but when I'm trying to enter this command "sudo openconnect –user=test https://test.com" I just get this error:
    Sorry, try again.

    I used to connect to my office VPN using openconnect on Linux many times without any problem.
    Can you please share your comment?

  2. The doc works like a charm. However, I do notice that the connection drops every couple hours during inactivity and then I have to redo it again. Not a terrible experience but would rather avoid it if I can. Any suggestion there?

  3. you can store vpn password in Mac' truststore (keychain) and retrieve it from there when you need it which is much more secure than a file on a disk:

    #add vpn password to login keychain
    mac % security add-generic-password -s ‘test_vpn’ -a $USER -w 'password'

    # gets the password from truststore
    mac % security find-generic-password -w -s 'test_vpn' -a $USER
    password

    It works in similar way with openconnect:
    security find-generic-password -w -s 'test_vpn' -a "$USER" | sudo -A openconnect -u "$USER" -s /usr/local/etc/vpnc-script –passwd-on-stdin vpn-gw.my.network

    password

  4. Beware that placing this script in the sudoers file will make the system vulnerable to attackers/privilege escalation.
    The –script functionality in openconnect can be used to run arbitrary commands as root.

  5. Not working for me on MacOS Monterey 12.4
    Can connect by IP in web browser and do nslookup, but cannot for the life of me get DNS resolution working :(
    Might have to go back to the clunky, official FortiClient for Mac.

Comments are closed.