Some guidance about configuring/fixing domain name resolution with a corporate Virtual Private Network (VPN), especially OpenVPN and with systemd-based Linux systems. This configuration uses the internal/private corporate resolvers for resolving internal/private domain names while using the ISP resolver for general domain names. This might help if your VPN is struggling these days because of the COVID-19 threat 😷.
I've been helping some people to make corporate VPNs (OpenVPN-based) work correctly, since yesterday. The problem is the resolution of domain names:
either private internal domain names are not resolved because the internal corporate resolver is used;
or general domain name resolution is slowish because the internal corporate resolver is used and the VPN is currently struggling with the new load.
A good solution is to use the internal coporate resolver for internal domain names but another (eg. the ISP) resolver for other domain names. Depending on your configuration and how you are running the VPN (CLI, NetworkManager, etc.), this might be already what is happening (you might find some information in this post in order to check this).
Below are some instructions about one solution to make this work on systemd-based Linux systems using systemd-resolved. There are solution to achieve this without
systemd-resolved (I think it is possible to do that with
If you are using a Linux system with
systemd-resolved, a quick fix is something like this (your need to adjust these values):
# Configure internal corporate domain name resolvers: resolvectl dns tun0 192.0.2.53 192.0.2.54 # Only use the internal corporate resolvers for domain names under these: resolvectl domain tun0 "~foo.example.com" "~bar.example.com" # Not super nice, but might be needed: resolvectl dnssec tun0 off
If you don't have the
resolvectl commands, you might use instead:
systemd-resolve -i tun0 \ --set-dns=192.0.2.53 --set-dns=192.0.2.54 \ --set-domain=foo.example.com --set-domain=bar.example.com \ --set-dnssec=off # <- Not super nice, but might be needed.
First check if you have
systemd-resolved installed and running:
systemctl status systemd-resolved
Check if you have the
An alternative is to use
Check if have the
resolve mechanism enabled in
cat /etc/nsswitch.conf | grep ^hosts: | grep resolve
Install the needed libraries. For example on Debian or Ubuntu AMD64 systems:
dpkg -l | grep libnss-resolve
Note: I am installing both the 64 bit and 32 bit libraries. If you do not install the 32 bit libraries, proper DNS resolution might fail for 32 programs.
Configure DNS resolution
If all these prerequisites are satisfied, you can configure the DNS resolution with something like:
resolvectl dns tun0 192.0.2.53 192.0.2.54 resolvectl domain tun0 "~foo.example.com" "~bar.example.com"
Alternatively, if you don't have
systemd-resolve -i tun0 \ --set-dns=192.0.2.53 --set-dns=192.0.2.54 \ --set-domain=~foo.example.com --set-domain=~bar.example.com
This configures systemd-resolved to use the corporate internal resolvers 192.0.2.53 and 192.0.2.54 for resolving all domain names under
tun0 is the network interface reprensing the VPN. You have to adjust these parameters (IP address, domain names).
~ in front of a domain prevents the domain to be added to the search list (i.e. trying to resolve the
test host name will not search
test.foo.example.com if a
~ was used in front of
You can check your system-resolved configuration with:
If you don't have
You can now test domain name resolution (assuming
some-service.foo.example.com actually exists):
resolvectl query some-service.foo.example.com
If you don't have
If this works, you should have:
some-service.foo.example.com: 192.0.2.22 -- link: tun0 -- Information acquired via protocol DNS in 1.7ms. -- Data is authenticated: no
In my case, this failed because of:
some-service.foo.example.com: resolve call failed: DNSSEC validation failed: failed-auxiliary
A quick/dirty fix is to disabled DNSSEC:
resolvectl dnssec tun0 off
If you don't have
systemd-resolve -i tun0 \ --set-dns=192.0.2.53 --set-dns=192.0.2.54 \ --set-domain=foo.example.com --set-domain=bar.example.com \ --set-dnssec=off
Hooking with OpenVPN (CLI)
If you are using OpenVPN directly from the CLI (not through Network Manager), you can automate this with the
openvpn --config client.ovpn --script-security 2 --up ./manual-config
./manual-config is a shell script such as:
#!/bin/sh set -e resolvectl dns $dev 192.0.2.53 192.0.2.54 resolvectl domain $dev "~foo.example.com" "~bar.example.com" resolvectl dnssec $dev off
#!/bin/sh systemd-resolve -i $dev \ --set-dns=192.0.2.53 --set-dns=192.0.2.54 \ --set-domain=foo.example.com --set-domain=bar.example.com \ --set-dnssec=off # <- Not super nice, but might be needed.
OpenVPN sets the
dev environment variable to the name of the VPN TUN/TAP (i.e. virtual) network device (eg.
tun0) when calling the hooks.
Don't forget to make this script executable:
chmod u+x ./manual-config
For OpenVPN, an alternative is to use the
openvpn \ --config client.ovpn \ --up /etc/openvpn/update-systemd-resolved \ --down /etc/openvpn/update-systemd-resolved \ --down-pre \
On Debian and Ubuntu, this script is in the
This automatically pulls the configuration advertised by the VPN server:
- resolver to use;
- domain names;
- whether to disable DNSSEC.
If this fails, the previous section might help you pinpoint to the problem:
- check the configuration (
- test internal/external names resolution (
resolvectl query some-service.foo.example.com)
- disable DNSSEC if needed (
resolvectl dnssec tun0 off).