TT Lab
Get started
Learn Learning paths Courses

Network Fundamentals — Hands-on in a Linux VM

Follow a Name All the Way Down

Continue in TT Lab

This lab runs on a VM

On Ubuntu 24.04, systemd-resolved handles name resolution. You set up a second DNS server of your own with dnsmasq next to it, and move between the two layers to see where a name gets its answer. dnsmasq is already installed, but its service is in a failed state — finding out why is step 3.

Goal

Confirm with commands what the stub address in /etc/resolv.conf is, where the real upstream server is, how to point your own DNS server at only a particular domain, and why /etc/hosts beats DNS.

Why it matters

The first fork of "it doesn't work" is the name. But on modern Linux, name resolution is not a single layer — the application calls libc's getaddrinfo, which looks at /etc/hosts first in the order given by nsswitch.conf, then asks the stub in resolv.conf (127.0.0.53), and the stub looks at the per-link settings and passes the query upstream. dig taps only the last of these directly. That is why it really happens that dig works but curl does not, and you can fix it only if you know which layer differs.

Steps

  1. Save the actual file path that /etc/resolv.conf points to and its nameserver line to /root/dns/resolv.txt.
  2. Save the address of the upstream DNS server that the stub actually queries to /root/dns/upstream.txt as one line, upstream=<주소> (upstream= followed by the address).
  3. Find out why dnsmasq does not start, and save the ss output showing the process holding port 53 to /root/dns/port53.txt.
  4. Attach 10.53.0.1/24 to a dummy interface dns0, and bring the service up by using /etc/dnsmasq.d/lab.conf to make dnsmasq listen only on 10.53.0.1 and answer app.lab.internal→10.53.0.10 and db.lab.internal→10.53.0.20.
  5. Save the full output of dig @10.53.0.1 app.lab.internal to /root/dns/dig.txt.
  6. Use resolvectl to set the DNS server 10.53.0.1 and the routing domain ~lab.internal on the dns0 link so that getent hosts app.lab.internal works, and save resolvectl status dns0 to /root/dns/link.txt.
  7. Put 10.53.0.99 db.lab.internal in /etc/hosts, and save the answers from getent and dig @10.53.0.1 to /root/dns/hosts.txt as two lines, getent=·dig=.
  8. In /root/dns/report.md, write three lines, stub=·upstream=·local=, one app.lab.internal line from dnsmasq's query log, and the reason /etc/hosts beats DNS.

Notes

What resolv.conf really is

Save the actual file path that /etc/resolv.conf points to and its nameserver line to /root/dns/resolv.txt.

readlink -f /etc/resolv.conf gives the real path at the end of the symlink. Look at the stub address with grep nameserver /etc/resolv.conf. Put both in one file.

Where is the real server

Save the address of the upstream DNS server that the stub actually queries to /root/dns/upstream.txt as one line, upstream=<주소> (upstream= followed by the address).

The link entry in resolvectl status has Current DNS Server. It is the value for the default-route interface (enp1s0). resolvectl dns enp1s0 is shorter.

Why dnsmasq won't start

Find out why dnsmasq does not start, and save the ss output showing the process holding port 53 to /root/dns/port53.txt.

systemctl status dnsmasq and journalctl -u dnsmasq show Address already in use. To see who holds it, run ss -ulpn 'sport = :53' — -p shows the process name (you must be root).

Set up your own DNS server

Attach 10.53.0.1/24 to a dummy interface dns0, and bring the service up by using /etc/dnsmasq.d/lab.conf to make dnsmasq listen only on 10.53.0.1 and answer app.lab.internal→10.53.0.10 and db.lab.internal→10.53.0.20.

After ip link add dns0 type dummy, add the address and bring it UP. In the configuration, make it hold only that address with listen-address=10.53.0.1 and bind-interfaces, and pin the names like address=/app.lab.internal/10.53.0.10. no-resolv means you will not use an upstream. When you are done, run systemctl restart dnsmasq and then systemctl is-active dnsmasq.

Ask directly with dig

Save the full output of dig @10.53.0.1 app.lab.internal to /root/dns/dig.txt.

@서버 (@ followed by the server) ignores resolv.conf and asks that server directly. The ANSWER SECTION of the output must show the A record, and the header must show status: NOERROR.

Attach your server to the stub

Use resolvectl to set the DNS server 10.53.0.1 and the routing domain ~lab.internal on the dns0 link so that getent hosts app.lab.internal works, and save resolvectl status dns0 to /root/dns/link.txt.

resolvectl dns dns0 10.53.0.1 and resolvectl domain dns0 '~lab.internal'. A domain with ~ is a routing rule meaning "send queries for this domain to this link's server". To check, run getent hosts app.lab.internal — 10.53.0.10 must come out.

/etc/hosts wins

Put 10.53.0.99 db.lab.internal in /etc/hosts, and save the answers from getent and dig @10.53.0.1 to /root/dns/hosts.txt as two lines, getent=·dig=.

echo '10.53.0.99 db.lab.internal' >> /etc/hosts. Then getent hosts db.lab.internal | awk '{print $1}' and dig @10.53.0.1 db.lab.internal +short. The two values differ — that is the point of this step.

What you learned

In /root/dns/report.md, write three lines, stub=·upstream=·local=, one app.lab.internal line from dnsmasq's query log, and the reason /etc/hosts beats DNS.

stub is the nameserver from step 1, upstream is the value from step 2, and local is your own dnsmasq address. For the log, use journalctl -u dnsmasq --no-pager | grep 'query\[A\] app.lab.internal'. The reason must include the word nsswitch or files.