VPN setup in Linux: openconnect + NetworkManager + ssh config
2025-03-02
Linux networkingAs 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:
- Simplifying the SSH config
- Ditching Cisco’s AnyConnect for the open-source openconnect
- Resolving hostnames in the VPN without specifying the full domain
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