Hi there, today I would like to talk about an recurrent issue that I've been
facing for many years. I
wanted to configure my machine to use an specific DNS server, so I including it in
/etc/resolv.conf. However, after a while my new DNS server was removed and
/etc/resolv.conf
restored to a previous version.
In this article I'm going to explore what is happening and how discover who is
modifying /etc/resolv.conf
so we can solve it.
This article was made thinking of server environments were only a CLI is used. In case of having a GUI, usually we can just modify the network options in the system settings menu and it should work nicely.
Let's go with the topic.
The DNS file: /etc/resolv.conf
As I said previously, to change the DNS server seems easy, since the
/etc/resolv.conf
file can be modified.
We can check it to retrieve our DNS servers:
$ cat /etc/resolv.conf
nameserver 192.168.122.1
Then, we can add a nameserver
entry to include our DNS server:
$ echo "nameserver 192.168.122.135" | sudo tee /etc/resolv.conf
nameserver 192.168.122.135
$ cat /etc/resolv.conf
nameserver 192.168.122.135
Everything seems right so far. Now, what happens after a few minutes or if we
reboot our machine? /etc/resolv.conf
is restored and our changes are lost. If
that is not your case, congrats, you have already modified the DNS servers and
there is no need for you to continue reading.
Why is restored /etc/resolv.conf
to a previous state? Because some daemon (a
background process) is modifying the file. Which daemon? It depends.
There are several programs in GNU/Linux that can be on charge of DNS configuration, so we have to find out which one is taking care of it in our machine.
The hack: Making /etc/resolv.conf immutable
However, before diving into the daemon process discovery, I would like to discuss a nice hack that can be used against any daemon, even if some side effects may apply.
In order to avoid any daemon can modify /etc/resolv.conf
, we can just
make it immutable after modifying it. Thus, no process, nor even root ones, can
modify it (until immutability flag is removed).
We can use the chattr command for this purpose:
$ echo "nameserver 192.168.122.135" | sudo tee /etc/resolv.conf
nameserver 192.168.122.135
$ sudo chattr +i /etc/resolv.conf
After executing the commands, we can restart our machine to verify that our
configuration was kept. In case we want to modify /etc/resolv.conf
again, we
will need to remove the immutability with sudo chattr -i /etc/resolv.conf
.
This hack can be a good option in case we only want to use an static server. I have used it in order to configure dnscrypt.
However, we can find cons if we also want to use the DNS server provided by our DHCP client. In that case, if we change our network, the network daemon won't be able to update the DNS address and we may lost our network connection. I'm not sure it worked with a VPN either.
Therefore, the most proper option is to change the configuration of the network daemon on charge of modifying the DNS server.
Network daemons: Figuring out who is modifying /etc/resolv.conf
The major problem with network daemons is that anyone is different from each other. Besides, many times we don't really know who is managing the DNS servers. I going to explore some techniques to discover the one in our machine, but let's check the usual suspects:
-
NetworkManager: GNOME network daemon.
-
systemd-resolved: systemd DNS daemon.
-
etc: There are more, but we will focus on the previous three, since are the most common.
Once we know what are we looking for, let's see what we can do to discover who
is messing with /etc/resolv.conf
. We have several courses of action.
Hint nº1: /etc/resolv.conf content
The first thing we need to do is read /etc/resolv.conf
, since many programs
that modify it include an explanatory message, that usually says we shouldn't
modify it cause is going to be overwritten.
For example, this is one of my machines /etc/resolv.conf
, which is modified by
systemd-resolved
:
And this is the /etc/resolv.conf
content in case NetworkManager is managing
it:
But we can also found /etc/resolv.conf
without comments:
$ cat /etc/resolv.conf
nameserver 192.168.122.1
In this last case, I discovered that was being managed by dhclient
.
However, in case we are in doubt, we can apply other techniques.
Hint nº2: Monitoring /etc/resolv.conf
Alternatively, we can monitor /etc/resolv.conf
file for write operations. We
can do this with opensnoop, an utility that uses eBPF for tracing file opening
operations. In order to use it we must install the bpfcc-tools
package and
Linux headers (in the case of Debian can be done with
sudo apt install linux-headers-$(uname -r)
).
Once the tool is installed, we can execute it and await a few minutes to check
if any process is modifying some resolv.conf
file:
With the previous command, we trace with opensnoop
the files open for
writing and filter them with grep
to just show those whose name includes
resolv.conf
, not just /etc/resolv.conf
.
If you want to learn more about eBPF, check Learn eBPF Tracing: Tutorial and Examples by Brendan Gregg, who shows a lot of tools for tracing based on eBPF, like tcpconnect, one of my favorites, that allows to see the processes network connections in real time.
It is advisable to monitor not just /etc/resolv.conf
, but any file that
contains resolv.conf
since many programs use cache files with similar names to
store temporal results. For instance, dhclient
uses files like
/etc/resolv.conf.dhclient-new.590
to write changes and then checks if those
are different from /etc/resolv.conf
, if that is not the case,
/etc/resolv.conf
is not written.
On the other hand, it is possible for programs to just rename the cache file to
/etc/resolv.conf
instead of writing it (something like
mv /etc/resolv.conf.HOIHS2 /etc/resolv.conf
, which is done by
NetworkManager), so /etc/resolv.conf
won't appear in as an open file (since
rename syscall is used instead of open and opensnoop
won't be able to detect
it).
Anyway, after examining /etc/resolv.conf
content and monitoring it, we should
already have an idea about the daemon that is managing the DNS server, but even
if we are not sure, I'm going to explain many of them and see how we can check
if they are running.
Network daemons
dhclient
Let's how we can add a DNS server when dhclient is in charge. First, we need to make sure it is running:
Once we know for sure that dhclient
is running, in order to add a new DNS
server we need to modify /etc/dhcp/dhclient.conf, its configuration file, where
we can found several DNS options related. Specifically, to add a new
DNS server we can add some of these lines:
This way we can add our DNS with higher or lower priority, respectively, to the
one added by DHCP configuration. Additionally, we can avoid using the DHCP
configured by DHCP by removing the domain-name-servers
item from the request
statement that we can found in /etc/dhcp/dhclient.conf
.
dhclient
also allows to create specific configurations for each network
interface where we can also specify a DNS server for such interface, but I'm not
going to discuss that here. In case you want more information you can check
dhclient.conf(5).
Therefore, if we add the previously discussed lines to /etc/dhcp/dhclient.conf
and we restart the ifup
service reexecute dhclient
(or wait for a while) we should see the changes
applied to /etc/resolv.conf
:
$ sudo systemctl restart ifup@enp1s0.service
$ cat /etc/resolv.conf
nameserver 127.0.0.1
nameserver 192.168.122.1
nameserver 192.168.122.13;
Be aware that the ifup
service, which spawns dhclient
is given the network
interface as a parameter, which in my case is enp1s0
, but yours may differ.
Besides, if we reboot the machine, changes should remain.
NetworkManager
As we state before, NetworkManager usually is on charge of managing DNS servers
when we found a /etc/resolver.conf
file similar to the following:
Besides, we can confirm that NetworkManager is running by checking the service:
NetworkManager is more complex that other network managers, since it offers several options to manage the DNS server such as do it itself or delegate the task in third-parties like systemd-resolved or dhclient. You can get more information on the dns section of NetworkManager.conf(5). In this section we are going to assume that the DNS server is going to be managed by NetworkManager itself, since other options are explored in their respective sections.
We can use nmcli to manage NetworkManager. With this tool we can specify a new
DNS server for the network connection we want (I didn't found how to specify for
all connections). We can list the connections (network interfaces) with
nmcli connection show
:
And afterwards add a DNS server with nmcli connection modify
:
As we can appreciate, our DNS servers were updated after executing the command, and if we reboot the machine, changes should remain. Be aware that your network connection name could be different from mine so you may need to adapt the command.
Additionally, if you don't want to use the DHCP specified DNS you can use the following command:
sudo nmcli con mod "$network_connection" ipv4.ignore-auto-dns yes
systemd-resolved
Last but not least, we have systemd-resolved. We can verify that is been used by
checking that /etc/resolv.conf
has a similar content to the following:
If we put attention to the content, we can see that systemd-resolved
is
pointing to its own local DNS server at 127.0.0.53
.
On the other hand, /etc/resolv.conf
will be a link to
/run/systemd/resolve/stub-resolv.conf
:
$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 ago 19 2022 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
And we cannot forget to verify that systemd-resolved
is running:
So, now we know systemd-resolved
is on charge of DNS resolutions we can add a
DNS server. To do this we need to add a new DNS
entry in
/etc/systemd/resolved.conf
, like the following:
Then we restart the systemd-resolved
service:
sudo systemctl restart systemd-resolved.service
And we can confirm our DNS server is set:
We can verify it by reading /run/systemd/resolve/resolv.conf
as well:
We must note that this time we didn't check /etc/resolv.conf
to verify the
changes, but resolvectl
and /run/systemd/resolve/resolv.conf
. This is due
to systemd-resolved
don't really modify /etc/resolv.conf
, but adds its own
local DNS server in 127.0.0.53
and then it redirects the DNS requests to the
servers we indicate.
Anyway, changes should remain after we reboot the machine.
Conclusion
In this article we have seen how to modify the DNS servers on different tools after discovering which one of them is on charge. This kind of the GNU/Linux beauty, the existence of several solutions to managed different parts of the operating system, even if some times them give us headaches.
I hope this allowed you to solve a problem and learning a little about GNU/Linux ecosystem.
See you and long live to GNU/Linux!!