article cover placeholder

The Domain Name System (DNS) 2

1. Introduction

After we've studied, what DNS resolving is and how does it work, we'll be looking closer in this article on the way used in Linux distributions for resolving domain names, where the weaknesses lie and how to improve the resolving process and make it more efficient.

2. The GNU C Lib Resolver

Many applications in Linux use the GNU C Library's getaddrinfo-API, for resolving IP-Addresses. This API uses on the other hand the so called Name Service Switch (NSS) at /etc/nsswitch.conf.

The NSS is basically a map of databases and their corresponding services, which can be queried to get the corresponding database entries. NSS goal was to centralize the place, different applications get their data from, and to prevent applications from having their own static databases.

The Database responsible for resolving domain names is the hosts-Database:

01_dns_2_nss.png
Figure 1: Name Service Switch (NSS) in Linux

Here is an example for the hosts entry taken from /etc/nsswitch.conf :

hosts: files dns

There are two services defined here for the hosts database:

files: reads the /etc/hosts file. It is usually used for hosts on the local network.

dns: is the glibc resolver which reads /etc/resolv.conf. Here is an example for the /etc/resolv.conf file generated by the NetworkManager:

# Generated by NetworkManager
search fritz.box
nameserver 192.168.178.1
nameserver fd17:625c:f037:2::3

The name server used for queries here is the router (192.168.178.1), which acts as forwarder.

We can use the dnslookup utility to check which server is being queried for resolving a domain:

dnslookup google.com

output:

dnslookup master
Server: 192.168.178.1:53

dnslookup result (elapsed 17.805328ms):
;; opcode: QUERY, status: NOERROR, id: 11113
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;google.com. IN A

;; ANSWER SECTION:
google.com. 146 IN A 142.250.186.46

Notice that the answer section contains a DNS Resource Record Typ A, with 146 seconds time to live and google's server IPv4 address 142.250.186.46

We can add the Cloudflare DNS Server IP 1.1.1.1 to /etc/resolv.conf

# Generated by NetworkManager
search fritz.box
nameserver 1.1.1.1 # add Cloudflare DNS server's IP
nameserver 192.168.178.1
nameserver fd17:625c:f037:2::3

and test the lookup again:

dnslookup google.com

output:

dnslookup master
Server: 1.1.1.1:53

dnslookup result (elapsed 16.191517ms):
;; opcode: QUERY, status: NOERROR, id: 40977
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;google.com. IN A

;; ANSWER SECTION:
google.com. 124 IN A 142.250.185.110
Prevent unsafe resolving of local hostname over the network

The NSS service myhostname is provided by systemd and enabled by default in /etc/nsswitch.conf and its used for resolving local hostname and localhost locally. However some programs may read /etc/hosts and/or /etc/resolv.conf directly, bypassing the NSS [2]. This behaivor may lead to resolve the local hostname over the network. To prevent such potential security risks, add the following lines to your /etc/hosts file:

# Static table lookup for hostnames.
# See hosts(5) for details.

127.0.0.1 localhost
::1 localhost

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhostsA

The built in glibc-Resolver has some limitations. For example it doesn't cache lookups, meaning more lookup time and it doesn't support secure DNS (DNSEC) to prevent DNS cache poisoning. Therefore in the following chapter we'll setup a DNS cache and make some tests to see the difference. As for secure DNS, since it's a bigger topic, we'll be discussing it in a spearate article.

3. DNS Cache

In last chapter, we've took a look at how domain names are resolved and we've seen that there multiple DNS resolvers involved (stub-, recursive- and forwarding-resolvers). The ones we are interested in are stub-resolvers. These are local DNS resolvers that can take care of the entire resolving process for us, like forwarding queries to forwarder and/or recursive resolvers, DNS caching on the local network, validating DNS records and more.

Setting up a local DNS Cache

I'll be using systemd-resolved as a Stub-Resolver because of its simplicity and since its already included within the systemd package by default. You can find a detailed comparision of different DNS servers on Wikipedia.

sudo pacman -Sy systemd-resolved
sudo systemctl enable systemd-resolved.service
sudo systemctl start --now systemd-resolved.service

To use the local stub resolver which listens on port 53 of 127.0.0.53, create symbolic link from /etc/resolve.conf to /run/systemd/resolve/stub-resolv.conf

ln -sf ../run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

Confirm that the service is enabled and running

sudo systemctl status systemd-resolved

Make sure to enable the systemd-resolved NSS module (nss-resolve) by adding the entry resolve [!UNAVAIL=return] into the hosts NSS database:

hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns

Notice adding the resolve module before files module, since systemd-resolved supports /etc/hosts internally, but with caching.

The [!UNAVAIL=return] after resolve ensures that if systemd-resolved is not running or cannot be reached, the NSS will immediately stops the lookup proecess. Preventing from falling back to further NSS DNS services. This is used for making systemd-resolve the primary authority for resolving domain names.

If you'll be using a client (e.g. DHCP or VPN), its advised to install systemd-resolvconf which provides the symbolic link /usr/bin/resolvconf which is used by these clients.

sudo pacman -S systemd-resolvoconf
The systemd-resolved lookup utility

The system-resolved package provides a built-in lookup utility resolvectl, which can be used for querying DNS servers:

resolvectl query google.com

output:

google.com:142.251.36.174-- link: enp0s3
2a00:1450:4016:80b::200e-- link: enp0s3

-- Information acquired via protocol DNS in 20.5ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

To check which DNS server is used by systemd-resolved stub resolver:

resolvectl status

output:

Global
Protocols: +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Fallback DNS Servers: 9.9.9.9#dns.quad9.net 2620:fe::9#dns.quad9.net 1.1.1.1#cloudflare-dns.com
2606:4700:4700::1111#cloudflare-dns.com 8.8.8.8#dns.google 2001:4860:4860::8888#dns.google

Link 2 (enp0s3)
Current Scopes: DNS LLMNR/IPv4 LLMNR/IPv6 mDNS/IPv4 mDNS/IPv6
Protocols: +DefaultRoute +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
DNS Servers: 192.168.178.1 fd17:625c:f037:2::3
DNS Domain: fritz.box
Default Route: yes

We can see that the stub server is used. This will uses the router on 92.168.178.1 as a forwarder resolver.

Activating systemd-resolved caching

systemd-resolved DNS caching is avtivated by default. However you can confirm it by looking in /etc/systemd/resolved.conf

cat resolved.conf | grep Cache

you should see something like:

#Cache=yes

even though the line is commented, the file lists the default configurations. You can override those by uncommenting the desired configuration and setting its new value. To test the performance without caching we'll deactivate the cache by setting Cache=no in /etc/systemd/resolved.conf

Don't forget to restart the service after changing the configurations

sudo systemctl restart systemd-resolved.service

Lets' clear the cache, make a couple of queries and list the statistics:

# empty the cache
sudo resolvectl flush-caches

resolvectl query google.com
resolvectl query google.com

# show cache information
sudo resolvectl statistics | grep Cache

output:

Cache
Current Cache Size:0
Cache Hits:0
Cache Misses:47

We can see that we're getting only cache misses.

Alternatively you use the --cache=Bool option for quering with or without cache.

Now let's acitvate the cache and try again. But this time let's first send only one query, print the statistics, and afterward send a second query immediately (hence TTL) and print the statics again.

# empty the cache
sudo resolvectl flush-caches

# first query
resolvectl query google.com

# show cache information
sudo resolvectl statistics | grep Cache

# second query
resolvectl query google.com

# show cache information
sudo resolvectl statistics | grep Cache

statistics after the first query:

Cache
Current Cache Size:2
Cache Hits:0
Cache Misses:2

statistics after the second query:

Cache
Current Cache Size:4
Cache Hits:2
Cache Misses:2

in the first statistics output, it's obvious, that no cache hits were achieved. However the query was cached, cache size is 2 (google's IPv4 and IPv6). In the seond output however, we can see that we have 2 cache hits.

We can measure how long a DNS lookup takes time, first empty the cache:

sudo resolvectl flush-caches

Make sure to execute the query multiple times immediately:

resolvectl query mjarkas.de google.com: 2a00:1450:4001:81c::200e -- link: enp0s3
142.250.186.142 -- link: enp0s3

-- Information acquired via protocol DNS in 24.6ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
resolvectl query google.com 0.00s user 0.01s system 32% cpu 0.038 total
resolvectl query mjarkas.de google.com: 2a00:1450:4001:81c::200e -- link: enp0s3
142.250.186.142 -- link: enp0s3

-- Information acquired via protocol DNS in 1.4ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: cache
resolvectl query google.com 0.00s user 0.01s system 71% cpu 0.017 total

It's obvious that the second query took only 1.4ms after caching, whereas the first query needed about 24ms. Also notice that resolvectl actually tells us where it gets the data from, in this the second query, it came from the cache.

4. Conclusion

We've had a deeper Look on how the majority of Linux applications make use of the GNU C Libary's API for resolving Domain Names. We've also introduced the limitation of this API in terms of caching. To overcome this limitations and improve efficienty we've installed a local DNS stub resolver for caching DNS queries.

In the third and last article on DNS topic, we'll have a closer look on DNS Security and Privacy. How DNS cache can be manipulated and how to protect ourselves against DNS Cache poisoning.

5. References

  1. A. S. Tanenbaum and D. Wetherall, Computer Networks, 5th ed. Upper Saddle River, NJ, USA: Pearson, 2011.
  2. Local Hostname is reolved over the network - Arch Wiki
  3. Systemd Resolved - Arch Wiki
  4. How Linux DNS Lookup works - StackExchange
  5. Anatomy of a linux DNS Lookup - Zwischenzugs