New feature: set DNS nameservers for specific domain

Pritunl pushes a list DNS nameservers to the client, and the client configures their host with these nameservers. The configuration written by clients sets these nameservers as the global default. It would be useful if Pritunl clients could set these nameservers for specific domains.

Overriding the global default nameservers can lead to issues when another part of the system wants to set the default nameservers as well. For example, IPv6-enabled hosts will regularly override their global DNS nameservers, as detailed here: Client v1.3.3484.2 on macOS Ventura "Lost DNS...Restore DNS" loop on most but not all of my fleet. I am currently facing this issue. Users are reporting frequent (every 30-90 seconds) disconnections from the VPN. Disabling IPv6 on the client host makes the issue disappear, but we would rather not need to disable IPv6 on all user workstations.

On Mac, scutil’s SupplementalMatchDomains field allows VPN clients to configure DNS for only select domains. This would resolve the issue linked above. Could Pritunl set this field?

As a side-note, other VPN clients that connect to the Pritunl server via the OpenVPN protocol do this. They interpret the dhcp-option DOMAIN example.com option to mean they should configure split DNS. The Pritunl client uses it to set the search domain instead. Is there a particular reason to interpret that option the same way as the dhcp-option DOMAIN-SEARCH example.com?

I am more than happy to contribute this feature for Mac OS. This would resolve the issue I mentioned above for all our users, who currently work around the issue by using a different VPN client (either OpenVPN connect or the OpenVPN CLI).

I can’t find any information that the DHCP domain option should be interpreted as a split DNS configuration. The DHCP tags are described below. From what I understand the first domain should be specified as DOMAIN tag 15 and additional domains should be specified as DOMAIN-SEARCH tag 119.

I may add functionality in the future to handle split DNS but not by reusing the existing domain and domain search DHCP options.

DHCP Tag 15 (Domain Name):

  • Purpose: This tag is used to specify the domain name that the DHCP client should use.
  • Usage: When a client receives this tag in a DHCP offer, it typically appends this domain name to unqualified hostnames in DNS queries. For example, if the domain name provided is “example.com” and a user tries to access a server named “server,” the client will automatically search for “server.example.com.”
  • Scope: This is often used in corporate or organizational networks to ensure that machines automatically search within the correct domain for internal resources.

DHCP Tag 119 (Domain Search List):

  • Purpose: This tag specifies a list of domain names to be used by the DHCP client when resolving hostnames via DNS.
  • Usage: The client will use this list to attempt DNS resolution of unqualified hostnames in the order provided in the list. For instance, if the search list includes “example.com” and “example.org”, and a user tries to access a server named “server,” the client will first try “server.example.com”, and if that fails, then “server.example.org”.
  • Scope: This is particularly useful in environments where resources might be spread across multiple domains, and clients need to search through these domains automatically.

Indeed, the DOMAIN option and DOMAIN-SEARCH options both define the search domains on the host. These are passed as-is to the OS on Windows, but on Linux and Mac the client needs to handle them. Pritunl does this and treats DOMAIN and DOMAIN-SEARCH the same way.

However other VPN clients on Mac apply DOMAIN and DOMAIN-SEARCH differently. I agree that they’re wrong in their interpretation of the DHCP options. However what they do has merit and could serve as inspiration for adding split DNS to the Pritunl client on Mac.

All the clients I have tried, including the Pritunl client, when they connect to the Pritunl server and receive dhcp-option DOMAIN mycompany.com, configure DNS on the host using scutil. But the dictionaries they create are different and the result is different.

Pritunl createsmultiple:

$ scutil
> open

> show Setup:/Network/Service/1692E22E-ADBB-4FB7-8349-72BE1809C202/DNS
<dictionary> {
  Pritunl : true
  SearchDomains : <array> {
    0 : mycompany.com
  }
  ServerAddresses : <array> {
    0 : 10.254.0.7
    1 : 10.254.1.9
    2 : 10.254.2.12
    3 : 8.8.8.8
  }
}

> show State:/Network/Pritunl/Connection/a9b405e50abd8715e93a071ef017ce78
<dictionary> {
  Pritunl : true
  SearchDomains : <array> {
    0 : mycompany.com
  }
  ServerAddresses : <array> {
    0 : 10.254.0.7
    1 : 10.254.1.9
    2 : 10.254.2.12
    3 : 8.8.8.8
  }
}

> show State:/Network/Pritunl/Restore/1692E22E-ADBB-4FB7-8349-72BE1809C202
<dictionary> {
  DomainName : home
  ServerAddresses : <array> {
    0 : 192.168.1.1
  }
}

It also updates one existing object:

$ scutil
> open

> show State:/Network/Service/1692E22E-ADBB-4FB7-8349-72BE1809C202/DNS
<dictionary> {
  Pritunl : true
  SearchDomains : <array> {
    0 : pigment.app
  }
  ServerAddresses : <array> {
    0 : 10.254.0.7
    1 : 10.254.1.9
    2 : 10.254.2.12
    3 : 8.8.8.8
  }
}

The first three are state the Pritunl client uses to reset the last one when disconnecting. The last one is what actually changes the host’s DNS settings. Its content is first propagated to this object:

> show State:/Network/Global/DNS
<dictionary> {
  SearchDomains : <array> {
    0 : mycompany.com
  }
  ServerAddresses : <array> {
    0 : 10.254.0.7
    1 : 10.254.1.9
    2 : 10.254.2.12
    3 : 8.8.8.8
  }
  __CONFIGURATION_ID__ : Default: 0
  __FLAGS__ : 2
  __ORDER__ : 0
}

This is reflected in /etc/resolv.conf as:

search mycompany.com
nameserver 10.254.0.7
nameserver 10.254.1.9
nameserver 10.254.2.12
nameserver 8.8.8.8

This is different from OpenVPN Connect. When connecting to the same Pritunl server with the same profile, OpenVPN Connect creates a single key and does not update any other keys:

$ scutil
> open

> State:/Network/Service/OpenVPNConnect/DNS
<dictionary> {
  ServerAddresses : <array> {
    0 : 10.254.0.7
    1 : 10.254.1.9
    2 : 10.254.2.12
    3 : 8.8.8.8
  }
  SupplementalMatchDomains : <array> {
    0 : mycompany.com
  }
  SupplementalMatchDomainsNoSearch : 1
}

Unlike Pritunl’s objects, this sets up a split DNS configuration, where only DNS queries for company.com and its subdomains go to the nameservers pushed by the Pritunl server. The SupplementalMatchDomainsNoSearch is supposed to disable domain search but the fact is that it doesn’t. Setting it to a different value or removing it changes nothing to domain resolution in my experience, so we might as well remove it.

This could be a quick and simple way for the Pritunl client to set up split DNS on Mac.

Orthogonal to the split DNS feature, the way OpenVPN Connect configures DNS can help improve Pritunl. Using shared objects like State:/Network/Service/xxx/DNS leads to race conditions when the host changes network, the host shuts down, etc. This is because this object is actively maintained by the OS, not just Pritunl. Having a single object that only Pritunl manages would be a good way to get rid of these race conditions.

To do that, we could replace the objects Pritunl currently creates with one like OpenVPN Connect’s. We can avoid the “split DNS” effect by including the empty string in SupplementalMatchDomains. That will have the same effect that Pritunl’s approach currently does: update State:/Network/Global/DNS and /etc/resolv.conf and route all the host’s DNS queries to the nameservers pushed by the Pritunl server, but with no shared state and no race conditions.

What do you think?

I would be more than happy to help with such improvements. Currently these DNS issues on Mac are preventing many users in my organisation from using the Pritunl client. Instead they use OpenVPN Connect or the OpenVPN CLI for now, but the Pritunl client has exclusive features that we are interested in. If we could fix these DNS issues that would be great!

I will do some testing with that type of DNS configuration.

1 Like

The next release will use the SupplementalMatchDomains option with a blank domain to avoid modifying the primary service. This may be expanded to include an option to set the match domains from the server but the feature would need to also be developed on the Windows client.

1 Like

Amazing!! Thanks a ton! :heart_eyes:

I’ll try it out right away :slight_smile: