Что это и зачем
Раньше /etc/resolv.conf - обычный текстовый файл, в нём руками или через
DHCP-клиент прописывались nameserver-строки. Проблемы старого подхода:
- Только 3 nameserver-а максимум (по RFC).
- Все интерфейсы делят один список - нет per-link DNS (важно для VPN).
- Нет кеширования - каждый запрос в сеть.
- Нет DNSSEC, DoT (DNS-over-TLS) из коробки.
systemd-resolved - демон-стаб который решает это. Он слушает на
адресе 127.0.0.53:53 (последний октет = 53, порт DNS) и:
- Принимает запросы локальных приложений через
/etc/resolv.conf. - Хранит отдельные конфиги DNS-серверов на каждый сетевой интерфейс
(
enp0s3идёт через ISP,wg0через VPN - резолв туда куда надо). - Кеширует ответы.
- Поддерживает DNSSEC, DoT, LLMNR, mDNS.
- Получает upstream-DNS от NetworkManager / systemd-networkd / DHCP.
Базовый поток:
curl example.com
↓ (libc gethostbyname)
/etc/resolv.conf → nameserver 127.0.0.53
↓ (UDP к 127.0.0.53:53)
systemd-resolved
↓ (выбирает интерфейс по routing'у, шлёт upstream)
Внешний DNS (ISP / 8.8.8.8 / 1.1.1.1)
/etc/resolv.conf - это symlink
На современных Ubuntu 22+/Fedora/RHEL 9+ файл - symlink:
ls -l /etc/resolv.conf
▸../run/systemd/resolve/stub-resolv.conf
Содержит только nameserver 127.0.0.53 плюс search-домены. Реальные
upstream-DNS видны через resolvectl, не в этом файле.
Есть два варианта target'а symlink'а:
| Цель symlink'а | Поведение |
|---|---|
/run/systemd/resolve/stub-resolv.conf | через 127.0.0.53 (default) |
/run/systemd/resolve/resolv.conf | прямо в upstream, без stub'а |
свой статичный /etc/resolv.conf | bypass systemd-resolved совсем |
Если нужно вырубить resolved и вернуть "по-старому":
sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo 'nameserver 1.1.1.1' | sudo tee /etc/resolv.conf
# Но NetworkManager может перезаписать - отключить ему управление
resolvectl - главный инструмент
resolvectl status # все link'и + upstream-DNS на каждом
resolvectl query example.com # резолв через resolved (как dig, но через stub)
resolvectl statistics # cache hits/misses
resolvectl flush-caches # сбросить кеш
resolvectl dns enp0s3 1.1.1.1 9.9.9.9 # задать DNS на конкретный интерфейс
resolvectl domain wg0 '~corp.local' # split-DNS: только запросы corp.local через wg0
Префикс ~ в домене = routing-only (использовать этот link только для
таких доменов). Без ~ - обычный search-домен.
Конфигурация - /etc/systemd/resolved.conf
Глобальные настройки. Не редактировать сам файл - использовать drop-in:
# /etc/systemd/resolved.conf.d/dns.conf
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 9.9.9.9
FallbackDNS=8.8.8.8
Domains=~. # все запросы через DNS= (а не per-link)
DNSSEC=allow-downgrade
DNSOverTLS=opportunistic
Cache=yes
После правки:
sudo systemctl restart systemd-resolved
resolvectl status # проверить что подхватилось
Drop-in (см. systemd-drop-ins) важен потому что пакетные обновления
могут перезаписать /etc/systemd/resolved.conf.
/etc/nsswitch.conf - порядок резолверов
Резолв имени проходит через цепочку из nsswitch.conf:
hosts: files myhostname mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns
Слева направо:
files→/etc/hosts(статические записи)myhostname→ имя самого хоста + localhostmdns4_minimal→ Avahi/mDNS для локальной сетиresolve→ systemd-resolved через D-Bus (быстро, поддерживает DNSSEC)dns→ fallback на классический glibc-resolver через/etc/resolv.conf
Если cross-проверка не нужна и хочется только resolved - оставить
files resolve [!UNAVAIL=return] dns. Менять nsswitch обычно не надо,
но это первое что смотришь при «host пингается, а dig не находит» (или наоборот).
DNSSEC, DoT, LLMNR - что включать
- DNSSEC - валидация подписей DNS-зон.
DNSSEC=allow-downgrade- разумный дефолт: пытается, но не ломает резолв если upstream не умеет. - DNSOverTLS - шифрование DNS-трафика до upstream-сервера.
opportunistic- попробовать TLS, fallback на plain. Для гарантий нужен upstream который реально умеет (1.1.1.1, 9.9.9.9).
- LLMNR - link-local multicast, замена WINS. Для домашних сетей.
Шумный, можно выключить (
LLMNR=no). - MulticastDNS -
.localимена в локалке. Если стоит Avahi - отключить в resolved (MulticastDNS=no) чтобы не дублировать.
Дебаг - почему не резолвится
resolvectl status # какие upstream'ы на каком link'е
resolvectl query --cache=no example.com # обход кеша
journalctl -u systemd-resolved -f # стрим логов резолвера
sudo resolvectl log-level debug # подробный лог
ss -ulnp 'sport = :53' # кто реально слушает 53
dig @127.0.0.53 example.com # обращение прямо в stub
Типичные проблемы:
- Пустой DNS Servers в
resolvectl status- NetworkManager не отдал; проверитьnmcli dev show | grep DNS. - Резолвится через
digно не через приложение - приложение не использует libc, читает/etc/resolv.confсам и видит127.0.0.53, но glibc-кеш пуст. Перезапустить приложение. - VPN сломал DNS - VPN-клиент перезаписал
/etc/resolv.confповерх symlink'а. Использоватьresolvectl domain wg0 '~.'вместо подмены файла.