VPN setup in Linux: openconnect + NetworkManager + ssh config

2025-03-02

Linux networking

As an occasional oversleeper, I am grateful for the possibility to connect to my institution’s VPN and pretend to work from home. Recently, I’ve decided to improve my set up, and I wanted to share some tips with my future self and anyone else who might be interested. Specifically, I will focus on three tips:

SSH config

It all started when I realized that my ssh_config, which was rather unkempt, had no less than 13 Host blocks for the machines at my workplace. I was basically repeating for all of them the same configuration, which looked roughly like this:

Host bridge
    User marotta
    Hostname bridge.company.domain.org
    IdentityFile ~/.ssh/id_company
    IdentitiesOnly yes

that seems redundant, and indeed it can be simplified by using a glob in the Host name, like so:

Host *.company.domain.org
    ...

However, that would mean that we have to write the fully qualified domain name as the ssh argument, which seems like too much typing. Another ssh_config option comes to the rescue: by setting CanonicalizeHostname yes, ssh will try to resolve the full domain of a hostname, in case no matching Host is found. In order to make it work, we also need to explicitly set the domains where this is allowed, and we do this by setting, for example, CanonicalDomains domain.org company.domain.org (whitespace separated). Adding these two lines, the final configuration looks like this:

CanonicalizeHostname yes
CanonicalDomains company.domain.org

Host *.company.domain.org
    User marotta
    IdentityFile ~/.ssh/id_company
    IdentitiesOnly yes

and we can simply say ssh bridge to log into the machine called bridge.

Now this config will work for all machines in the company domain, which is a great simplification. Another possibility could have been to hard-code the hostnames that I commonly connect to, but I think this wouldn’t have worked because at my institution we upgrade the hardware quite frequently and we rely on some virtual machines, so the hostnames change quite often.

From AnyConnect to openconnect

Another part of my upgrade involved moving from AnyConnect to openconnect. At my institution, Linux support is offered on a “best effort” basis. That is, the IT squad will prioritize Windows and Mac users, leaving us penguin fans basically on our own if we have technical issues. As such, the recommended client for connecting to the VPN is Cisco’s AnyConnect. After the institution moved to mandatory two-factor authentication (2FA), my initial setup stopped working, and I submitted to using the AnyConnect client, hoping to find the time to fiddle with openconnect some time. Well, that time has finally come!

The reasons for the change are manyfolds. First, AnyConnect is not at all integrated with the rest of my Gnome desktop environment. It overrides the network configuration without informing the other programs, with the result that my network indicator notifies me that I am not connected to the internet every time the VPN is active. Moreover, AnyConnect keeps an extremely annoying window with the VPN status always open, and I cannot close it. In 2025, I consider this a crime.

Luckily, there is a much better alternative: NetworkManager with openconnect! NetworkManager handles the network configuration (either from a GUI or the command line), while openconnect provides the VPN connectivity. At first I tried using openconnect from the command line, but I wasn’t able to make it work. One of the problems I ran into was that, if I called openconnect as root, the browser (where I had to enter my 2FA token) refused to start. Another problem was that the authorization kept failing due to Login denied. Your environment does not meet the access criteria defined by your administrator. I tried various options, including --useragent, but to no avail. What ended up working was installing a NetworkManager plugin to configure openconnect from a GUI (the name of the package differs by distro, but it could be something like networkmanager-openconnect, plus the dependencies to make the applets work). Then, I simply added a new VPN connection, followed the instructions in the GUI, and it almost worked.

The only issue was, when I clicked on the “VPN” button in the network manager applet, the web browser opened automatically and I could enter my username, password, and OTP code, but then I’d get an error saying “Your environment does not meet the criteria defined by your system administrator.” However, if I tried again, it misteriously worked on the second or third try. Luckily I found a way to fix this: in the configuration menu for the VPN connection, in the “Identity” tab, rather than using just the gateway URL, add /<group> at the end (for example, vpn.company.org/USERS instead of just vpn.company.org), although I have no idea why it works.

Resolving hostnames everywhere

I am so glad that I don’t see that unclosable Cisco window anymore. The cherry on top would be being able to resolve my institution’s hostnames without specifying the full domain. This is an itch that the I got after I learned about CanonializeHostname in the ssh_config. Wouldn’t it be nice if I could just use bridge instead of bridge.company.domain.org everywhere, not just for SSH? And Wouldn’t it be nice if I didn’t have to explicitly set the CanonicalDomains in the ssh_config? These questions brought me into a rabbit hole about domain resolution. The topic was too vast and complex for me, so I felt like I wasn’t understanding much, but I managed to find something useful anyway. One of the network configuration options is something called “search domains”, which is a list of domains where hostnames are automatically searched. For example, when I’m connected to my home’s wifi, NetworkManager automatically adds fritz.box, which is the domain of my router, to this list, so that I can simply go ping raspi to reach raspi.fritz.box, a raspberry pi that is also connected to my home network. This list can be customized on a per-connection basis, so what I had to do was find the right NetworkManager option to add company.domain.org as a search domain when I am connected to the institution’s VPN. To do this, I had to install the nm-connection-editor package, which provides a GUI for tweaking this setting, and that was it! Now I can also just ping bridge instead of bridge.company.domain.org. This is nice to have, but, sadly, I still have to explicitly set the CanonicalDomains in the ssh_config, which leaves me with yet another itch… For now, however, I have scratched enough and I should go back to my day job, so bye for now and see you next time.

:wq

 Marginalia

Leave a comment