えんでぃの技術ブログ

えんでぃの技術ブログ

ネットワークエンジニアの視点で、IT系のお役立ち情報を提供する技術ブログです。

systemd-resolvedの特徴と、使い方紹介

systemd-light

前の記事

systemd-resolvedがデフォルトのDNSクライアントとなる前後の/etc/nsswitch.confを比較しました。
また、興味のある方向けに詳細まで踏み込んで紹介しました。
忙しい方はサマリだけでもご覧ください。

endy-tech.hatenablog.jp

お伝えしたいこと

Fedora33以降で (/etc/hostsmulticast DNSを除けば) デフォルトのDNSクライアントとなったnss-resolve (systemd-resolved)について概要を紹介します。
従来のDNSであるnss-dns (/etc/resolv.conf)との違いに着目しつつ、systemd-resolvedの基本構成や便利機能 (per-link DNS)、使い方について書きました。

設定ファイルである/etc/systemd/resolved.conf の書きっぷりについては深く触れていませんが、基本動作にてDNSサーバ選出時にどう関わるかのみ紹介しています。
DNSサーバを使い分けない場合は/etc/systemd/resolved.confのみに記載するのがシンプルですが、per-link DNSを使う場合は寧ろNetworkManagerのみでほぼ全てを制御するのが良いと思います。
いずれにしても設定箇所をあまり分散せず、可能な限り一つにまとめるのがわかりやすくて良いでしょう。

systemd-resolved とは

systemd-resolvedは、Linux においてDNSクライアントとして動作します。
多くのLinuxディストリビューションでinitプロセスを管理しているsystemdの1コンポーネントでもあります。

systemd-resolvedの主要な構成要素は、下表の通りです。

要素 説明
systemd-resolved.service systemd に管理されているサービス。
systemctlによって起動・停止などを制御する
/etc/systemd/resolved.conf systemd-resolvedの設定ファイル
/run/systemd/resolve/stub-resolv.conf Fedora33以降では、/etc/resolv.confがこのファイルへのシンボリックリンクになっている。
digなど/etc/resolv.confを固定的に参照するプログラムとの互換性のために、systemd-resolved.service/run/systemd/resolve/stub-resolv.confを自動的に更新する
resolvectl systemd-resolvedコマンドラインツール。
systemd-resolvedの動作を確認したり、リアルタイムに変更したりできる

systemd-resolved のAPI

man systemd-resolved、またはArch Wiki によると、systemd-resolvedを使う方法 (API: Application Programming Interface) は3種類あります。
いずれのAPIもローカル専用で、ネットワーク越しに外部ホストからアクセスすることはできません。

man systemd-resolvedによると、サポートされる機能の多さとしては(1) > (2) >> (3)という関係になっており、systemdの開発者としては(3)よりも(1), (2) のAPIを使うことをより推奨しているようです。
(3) は、古いプログラムへの互換性を保つために残されています。

次のセクションでこれらのAPIがどのような使われ方をするのか、より具体的に紹介します。

# API 説明
1 D-Bus (Desktop Bus) プロセス間通信機能を提供するAPI
2 NSS (Name System Switch) /etc/nsswitch.confを介してアクセスする方式。
GNU C ライブラリによって実装されている
3 Local DNS Stub Listener TCP/UDP127.0.0.53:53 でリッスンしている
/etc/resolv.confを直接参照するプログラムが利用する

(1) D-Bus (Desktop Bus)

プロセス間で各種設定情報や命令を直接やり取りするための仕組みです (IPC: InterProcess Communication = プロセス間通信)
基本的には同一OS内のローカルのやり取りのみをサポートします。
ネットワーク越しに外部ホストとメッセージをやり取りするような使い方は、サポートされていないとのことです。

標準入力や標準出力、シェルなどのユーザー操作を介さずにプロセス間で直接やり取りすることで、ユーザーに意識させず、プロセス間でイイカンジに情報連携するための仕組みと理解しています。
アプリケーション開発者がこういった内部処理を作り込むことで、複数プロセスが矛盾なく連携し、粗結合なアーキテクチャで高度な機能や使い勝手を実現できるのでしょう。

D-Busの概要は、下記サイトにとてもわかりやすく解説されていました。

www.silex.jp

D-Busを利用している身近な例としては、私の知る限り2つあります。
どちらもNetworkManager関連です。

1つ目は、nmcliです。
nmcliで設定する際、裏ではNetworkManager.serviceD-Bus API を叩いて設定変更を反映しているようです。
nmtuiなど、その他のコマンドも恐らく同様です

以下の出力からも何となく察せると思います。

nmcli conn up eno1
# Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/5)

2つ目は、NetworkManagerからsystemd-resolvedへの設定情報連携です。
Fedora33以降の構成で、/etc/nsswitch.conf (resolveが選出され、DNSクライアントとしてsystemd-resolvedが選択される) や、/etc/resolv.conf (システムのDNSサーバとして127.0.0.53を参照する。結果としてsystemd-resolvedがスタブリゾルバとして問い合わせを代行する) だけを見ていると、NetworkManager のDNS設定が挙動に影響しない...と考えたくなるのですが、実は影響を与えます。

それは、NetworkManagerD-Bus APIによってsystemd-resolvedに設定情報を連携しているためです。
man NetworkManager.confに書いてありますが、/etc/NetworkManager/NetworkManager.confsystemd-resolvedという設定項目のデフォルト値がtrueになっています。
これにより、D-Bus API越しの情報連携が有効になります。

この挙動に馴染みのない方は、ご注意を...。
NetworkManagersystemd-resolvedの連携については、記事の後半で具体的な設定手順と共に紹介します。

(2) NSS (Name Service Switch)

DNSクライアントが名前解決をする際、/etc/nsswitch.confhosts:行を参照してどの方式で名前解決するかを決定します。

Fedora33以降の/etc/nsswitch.confは、以下のようになっています。

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

# 参考情報として、Fedora32 <span style="color: #0000cc">(恐らくRHEL8と同じ)</span> の/etc/nsswitch.confも載せておきます
#hosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname

詳細は前回の記事に譲りますが、多くの場合は/etc/hostsに記載がなければsystemd-resolvedによって外部DNSサーバに問い合わせするのが基本の動きです。

(3) Local DNS Stub Listener

Local DNS Stub Listener とは、TCP/UDPでリッスンしている 127.0.0.53:53のことです。

Fedora33以降では/etc/resolv.conf/run/systemd/resolve/stub-resolv.confへのシンボリックリンクとなっており、/run/systemd/resolve/stub-resolv.confsystemd-resolvedが自動更新するという構造になっています。
結果として、/etc/resolv.confを参照すると、システムのDNSサーバが127.0.0.53、つまりsystemd-resolvedに設定されていることになります。

digコマンドやhostコマンドのように、/etc/nsswitch.confではなく/etc/resolv.confを直接参照するプログラムにも互換性を持たせるため、こういったアクセスの仕方も用意されています。

systemd-resolved の基本機能

前回の記事で紹介しましたが、名前解決に使われるNSS (/etc/nsswitch.confの右側に列挙されているもののこと) には様々なものがあります。
systemd-resolved(=nss-resolve) は、既存のNSSの多くを置き換える機能性を持っています。
systemd-resolvedの場合はキャッシュが効くため、systemdのマニュアルでは従来のfilesmyhostnameよりも推奨されています (→ 前回の記事)。

既存のNSS名 既存NSSの機能 systemd-resolvedでの対応
files /etc/hostsを読み取る デフォルトで有効
mdns4_minimal mDNS機能 (あまり使わない) デフォルト無効。
/etc/systemd/resolved.confを書き換えて有効化できる
dns 外部DNSサーバにクエリを投げる) デフォルト有効。
従来のdns/etc/resolv.confによるシンプルな制御だった。
resolveでは、/etc/systemd/resolved.confでより高度な制御ができる
myhostname 自身のホスト名、*.localhostlocalhostなどの名前解決
(直接は使わないが、この機能を利用するプログラムもある)
デフォルト有効

Fedora33のデフォルトの/etc/nsswitch.conffilesが最初に検索されるため、/etc/hostsの検索にsystemd-resolvedが利用されることはありません。
/etc/nsswitch.confの順序を入れ替えてresolvefilesよりも左に配置することで、systemd-resolved/etc/hostsを読み取るよう設定することも可能です。

このようにsystemd-resolvedは従来のNSSの大半を置き換える機能性を持ちます。
後続のセクションでは、これらの機能の中でも従来のdnsに相当する、外部DNSサーバへの問い合わせの挙動について紹介します。

systemd-resolved と従来型DNSの違い

systemd-resolved (nss-resolve)と従来型DNS (nss-dns)の違いを示します。

設定ファイルの違い

従来のDNS/etc/resolv.confを参照していました。

一方で、systemd-resolved/etc/systemd/resolved.confを参照します。
それぞれの設定ファイルの仕様を見比べると、両者の違いがより詳細に見えてきます。

search (ドメイン自動補完)

まずは、search機能についておさらいします。
例えばsearch に xxx.com を指定していたとします。
ping Aを実行すると、Aが名前解決できなくてもxxx.comを自動補完してA.xxx.compingを実行してくれます。

従来のDNSでは、/etc/resolv.confndotsというオプションによって「ピリオドをいくつまで含んでいるときに自動補完の対象とするか」を制御できました (man resolv.conf)。

systemd-resolvedの場合はndotsを敢えて実装しておらず、単一ラベル (つまりピリオドを含まない) の場合のみドメインを補完します(man systemd-resolved)。
systemd-resolvedndotsを実装していない理由については、man systemd-resolvedで言及されています。

DNSサーバの複数指定

従来のDNSでは、最大3台までDNSサーバを指定できます。
基本的には1台目のみにクエリを投げます。
1台目のDNSサーバへのクエリがタイムアウトしたら、次のDNSサーバに問い合わせます。

systemd-resolvedでは、Routing という仕組みによってDNSサーバが選択されます。
ネットワークのIP Routing とは若干関係ありますが、完全に別物です。
これがper-link DNSに繋がります。
詳細は次のセクションで紹介します。

基本動作

systemd-resolvedは、ネットワークインターフェースごとにDNSサーバを指定できます。
その上で、Routing という仕組みによって、クエリごとにDNSサーバを使い分けます。

具体的には以下の動きになります (man systemd-resolvedPROTOCOLS AND ROUTINGより)
※グレーにした部分はあまり重要ではないので、無視してください
※5. についてはコマンド実行ログは掲載していませんが、私の手元で検証済みです

# 詳細
1 /etc/hostsや自身のホスト名、localhostlocalhost.localdomainXXX.localhostなどは、ネットワークにクエリを出すことなくローカルのみで名前解決する
2 単一ラベル (ピリオドを含まない) 場合は、Unicast DNS (通常のUDP#53のクエリ) は使われず、LLMNR (mDNSのように同一ネットワーク内の名前解決をするサービス) で名前解決する。
ただし、AレコードやAAAAレコードの場合は search domains によってドメイン名が補完され、Unicast DNS でクエリされる
3 *.localという名前はmDNSとして扱われるため、Unicast DNSとしては動作しない
4 複数ラベルA, AAAA, PTRレコードの場合 (ピリオドを含む場合) は、以下のロジックで最適なDNSサーバが選出される (man resolved.confDomains=も併せて参照) (※1)(※2)
  • アクセス先のFQDNが search domains やroute-only domainsと重なる場合は、そのインターフェースと紐づくDNSサーバが優先される
  • 複数の search domains やroute-only domainsに該当する場合は、ラベル数 (※3) の多い search domains と紐づくDNSサーバが優先される (best matching)
  • best matchingDNSサーバが複数あった場合、それらのDNSサーバ全てに対してクエリされる。クエリ結果が複数返ってきた場合は先に受信した方が優先される
  • ~.を含めてsearch domains に全く一致しなかった場合、5 に進む
5 4 のper-link DNSのsearch domains に一致しなかった場合、以下の挙動となる。
  • グローバルなDNSサーバ (/etc/systemd/resolved.confDNS) が設定されていれば、そこに問い合わせる
  • グローバルなDNSサーバが設定されていなかった場合、IPルーティングのデフォルトゲートウェイと紐づくper-link DNSサーバ (resolvectlコマンド出力で+DefaultRouteフラグがついているDNS) に問い合わせる
  • グローバルなDNSサーバ、IPデフォルトゲートウェイのいずれも設定がない場合、全てのper-link DNSとして設定されたDNSサーバに問い合わせ、最初に返ってきた応答を採用する

(※1) search domains に ~. を指定することで、どのFQDNにもマッチする指定が可能です (DNS Default Route)。best matching の観点では、最も優先順位が低いです。~をつけるとRouting Domain の扱いとなり、「DNS Routingの評価対象になるが単一ラベルに対するドメイン補完には利用されない」挙動になります。.はルートドメインを意味し、全てのドメインにマッチします

(※2) グローバルなDNS設定 (/etc/systemd/resolved.confDNSDomains) もper-link DNSと同様に評価されます。グローバルなDNSサーバがbest matching となれば、グローバルなDNSサーバのみにクエリされます。グローバルなDNSサーバとper-link DNSサーバの両方がbest matching となった場合は、どちらにもクエリされます

(※3) 例えば、www.google.comというFQDNにはwwwgooglecomという3つのラベルが含まれます

基本動作の要点

DNS Routing の挙動が非常にややこしいですが、上表の4と5をまとめると、A, AAAA, PTR レコードのクエリ先のDNSサーバは、以下の順序で評価されるようです。
あらゆる状況に対処できるように、systemd-resolvedには細かいルールが設定されていますが、基本的には1か2で確定できるように設計するのがわかりやすくてオススメです。

  1. ~.を含め、search domains に一致したDNS (best matching)。per-link DNS もグローバルなDNSも特に差はなく、同列に評価される
  2. グローバルなDNSが設定されていれば、そこにクエリする
  3. IP Routing の世界でデフォルトルートと紐づくNICにper-link DNSサーバが設定されていれば、そこにクエリする
  4. 全てのper-link DNSサーバにクエリする

設定方法

man resolved.confによると、/etc/systemd/resolved.confではシステム全体でクエリ先のDNSサーバを設定できますが、 search domains によって優先順位付けするような指定方法 (per-link DNS) はできません。

per-link DNSの設定はsystemd-networkd、または外部アプリケーション (例えばD-Bus連携しているNetworkManager) からの同期によって読み込まれます。
次のセクションで具体的な設定例を紹介しますが、設定例は使い慣れた NetworkManager (nmcli)にて紹介します。

どんな時に役に立つか?

Linux端末がVPNに接続している場合、VPN越しにアクセスするDNSサーバと、通常の物理ネットワーク越しにアクセスするDNSサーバで使い分けたいケースがあると思います。
社内向けドメインVPN越しのDNSサーバに、その他のgoogle.comなどのドメインは物理ネットワーク越しのDNSサーバに名前解決をしたいときに、per-link DNSが役に立つと思います。

(参考) 具体例

実際にDNS設定がどうなるか、色々な例で試してみました。

systemd-resolvedで/etc/hostsを認識させる

/etc/nsswitch.confを変更して、systemd-resolvedしか使わないように変更します。
これは検証のための設定変更であり、本来はこのようにすべきではないのでご注意ください。

#hosts:      files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] myhostname dns
hosts:      resolve [!UNAVAIL=return]

また、/etc/hostsに試験用のエントリを追加します。

8.8.8.8     google-hosts

この状態でgoogle-hostspingしたところ、名前解決できました。
確かに、systemd-resolved/etc/hostsを読み込むようです。

ping google-hosts
# PING google-hosts (8.8.8.8) 56(84) bytes of data.
# 64 bytes from google-hosts (8.8.8.8): icmp_seq=1 ttl=116 time=2.88 ms
# (以下略)

ちなみに、自身のホスト名へのpingも通りました (nss-myhostname相当)。

ping pc
# PING pc.internal1 (192.168.100.155) 56(84) bytes of data.
# 64 bytes from pc (192.168.100.155): icmp_seq=1 ttl=64 time=0.252 ms
# (以下略)

変更した/etc/nsswitch.confを元に戻しておきます。

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

search domains にヒットする方を優先する

こちらの構成で検証します。

dns1

DNS1, DNS2 という2台のDNSサーバを用意し、PCからそれぞれにクエリを投げて名前解決する構成を組みました。
PCには、DNS1と紐づけてendy1.testを、DNS2と紐づけてendy2.testを search domains (ipv4.dns-search) に設定しています。
systemd-resolvedにおいて、問い合わせ先のDNSサーバは search domains に一致するものが優先される仕様です。
rr.endy1.testはDNS1に、rr.endy2.testはDNS2に問い合わせる構成となります。

PCのNetworkManager設定は、以下のようになっています。

# DNS1向け
nmcli connection add \
con-name dnstest1 \
ifname enp7s0 \
type ethernet \
ipv4.method manual \
ipv4.addresses 192.168.100.100/24 \
ipv4.dns 192.168.100.2 \
ipv4.dns-search endy1.test  # ★search domains

nmcli connection up dnstest1

# DNS2向け
nmcli connection add \
con-name dnstest2 \
ifname enp1s0 \
type ethernet \
ipv4.method manual \
ipv4.addresses 192.168.122.100/24 \
ipv4.dns 192.168.122.3 \
ipv4.dns-search endy2.test  # ★search domains

nmcli connection up dnstest2

DNS1には、 endy1.test.zoneendy2.test.zoneには、それぞれ以下の行を記載しました。
rr.endy1.testrr.endy2.testに、それぞれ192.168.100.2 (DNS1のIPアドレス) を紐づけました。
ゾーンファイルを2つ作ったのは、ドメイン名を区別するためです。

rr    A   192.168.100.2

DNS2には、 endy1.test.zoneendy2.test.zoneには、それぞれ以下の行を記載しました。
rr.endy1.testrr.endy2.testに、それぞれ192.168.100.2 (DNS2のIPアドレス) を紐づけました。
DNS1とは異なるアドレスを返すため、どちらにPCからDNS1とDNS2のどちらに問い合わせたかわかるようになっています。

rr    A   192.168.122.3

ここで、PCから2箇所にdigを実行してみます。
endy1.testはDNS1に問い合わせており、endy2.testはDNS2に問い合わせていることがわかります。
この挙動から、 search domains と紐づくDNSサーバに優先して問い合わせることがわかります。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       6779    IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       6758    IN  A   192.168.122.3

resolvectl domainsystemd-resolvedが選択するリンクと search domains の関係を確認すると、以下のようになります。

resolvectl domain
# Global:
# Link 2 (enp1s0): endy2.test
# Link 3 (enp7s0): endy1.test

best matching が優先される (longest match)

黄色の部分のみ構成を変更しました。

dns2

rr.endy1.testrr.endy2.test共に、.testドメインに属します。
ただ、上図の構成においては、仕様上以下の動作になります。

  • rr.endy1.testはDNS1とDNS2の両方の search domains に該当するが、best matching の考え方に基づいて2階層分のドメイン名に一致するDNS1が優先される
  • rr.endy2.testは、DNS2の search domains にしか一致しないため、DNS2が優先される

黄色部分の変更差分のNetworkManager設定は、以下のようになります。

sudo nmcli connection modify dnstest2 ipv4.dns-search test
sudo nmcli connection up dnstest2

ここで、PCから2箇所にdigを実行してみます。
endy1.testはDNS1に問い合わせており、endy2.testはDNS2に問い合わせていることがわかります。
この挙動から事前の予想通り、より多くの label (ピリオドで区切られた文字列) に一致するDNSサーバが優先されることがわかりました。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       4800    IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.122.3

resolvectl domainsystemd-resolvedが選択するリンクと search domains の関係を確認すると、以下のようになります。

resolvectl domain
# Global:
# Link 2 (enp1s0): test
# Link 3 (enp7s0): endy1.test

best matching なDNSサーバが複数存在する場合、全てのDNSサーバにクエリする

次は以下の黄色部分を変更しました。

dns3

search domains の条件を両方のリンクで揃えました。
search domains のbest matching でも同等な場合は、それらのDNSサーバ全てに問い合わせる仕様です。
そして、最初にDNS応答を受信した経路が採用されます。

NetworkManagerの設定差分は、以下のようになります。

sudo nmcli connection modify dnstest1 ipv4.dns-search endy1.test,endy2.test
sudo nmcli connection up dnstest1

sudo nmcli connection modify dnstest2 ipv4.dns-search endy1.test,endy2.test
sudo nmcli connection up dnstest2

dig で確認します。
DNSキャッシュをクリアしながら実行していると、途中で結果が変わっていることがわかります。
DNS1とDNS2のうち、先に受信した応答が表示されています。
どちらもほぼ同時に応答を受信するため、結果が不定になっています。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       6554    IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.100.2

resolvectl flush-caches

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       86400   IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.122.3

最後に、resolvectlで設定を確認します。
両方の search domains にbest matching となっていることがわかります。

resolvectl domain
# Global:
# Link 2 (enp1s0): endy1.test endy2.test
# Link 3 (enp7s0): endy1.test endy2.test

DNSデフォルトルート

DNSデフォルトルートの挙動を確認します。
DNS1と紐づくリンクに、デフォルトゲートウェイを設定します。
また、 search domains を空欄にすることで一致しないようにします。

dns4

search domains に具体的なキーワードが全く該当しない場合、デフォルトルートが評価されます。
以下の2通りのいずれかの条件に該当するリンクは、デフォルトルートとして扱われます。

(※) .は、ルートドメインなので全てのドメインに一致します。また、ドメイン名の先頭に~を付けると、「ホスト名にドメイン名を補う」効果はなく、「best matchingなDNSサーバの選択」のみに寄与するという意味になります (routing domain)。

即ち、今回の場合は全てのクエリがDNS1を向くようになります。

NetworkManager上は、以下の変更差分となります。

sudo nmcli connection modify dnstest1 ipv4.gateway 192.168.100.1
sudo nmcli connection modify dnstest1 ipv4.dns-search ""
sudo nmcli connection up dnstest1

sudo nmcli connection modify dnstest2 ipv4.dns-search ""
sudo nmcli connection up dnstest2

digを実行してみると、実際に全てのクエリがDNS1を向くようになります。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       86400   IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.100.2

resolvectl domainからは、両リンクで search domains が空っぽであることがわかります。
resolvectl dnsの出力では、実質的にDNS1にしか問い合わせが向いていないことを反映して、DNS1のみが表示されます。
resolvectlからは、IPルーティングのdefault routeが反映されて、DefaultRouteのフラグが片方のリンクで有効 (+) になっていることがわかります。

resolvectl domain
# Global:
# Link 2 (enp1s0):
# Link 3 (enp7s0):

resolvectl dns
# Global:
# Link 2 (enp1s0):
# Link 3 (enp7s0): 192.168.100.2

resolvectl
# Link 2 (enp1s0)
#      Protocols: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported

# Link 3 (enp7s0)
#          Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
# Current DNS Server: 192.168.100.2                                               

長くなるのでコマンド実行結果は省きますが、いくつか追加で補足します。

DNS2の search domains に~.を追加すると、IP Routing のデフォルトゲートウェイよりも優先されてDNS2のみに問い合わせるようになります。
NetworkManagerの設定コマンドとしては、以下のようになります。

sudo nmcli connection modify dnstest2 ipv4.dns-search "~."
sudo nmcli connection up dnstest2

更にDNS1の search domains に~.を追加すると、DNS1とDNS2の両方にクエリを出すようになりました。
search domains に~.が設定されたリンク同士であれば、ipv4.gatewayの設定有無が優先順位に影響することは無いようです。

現在のDNS設定の確認方法

systemd-resolvedは、/etc/systemd/resolved.confやデフォルトルート、他デーモンからのD-Bus連携によって挙動が変わるため、非常に複雑です。
resolvectlコマンドを使えば、systemd-resolvedの設定情報を確認できるため便利です。

resolvectlをそのまま実行すると、最も詳細な情報が出てきます。
resolvectl domainによって、per-link DNSと関係する search domains を確認できます。
resolvectl dnsによって、各リンクと紐づくDNSサーバを確認できます。

具体的な出力例は、(参考) 具体例を参照してください。

(参考) D-Bus関連コマンド

D-Bus周りでそれっぽいコマンドを実行してみました。
D-Busに対応しているプロセスであれば、CLIGUIだけでなくD-Bus APIでもステータスを確認できます。
--xmlオプションをつければ、XMLで出力することも可能です。

オブジェクトパス (/org/freedesktop/NetworkManager/DnsManager) はインターネットで「NetworkManager D-Bus」で調べても良いですが、bash-completion の働きでタブ補完することでも簡単に確認できました。

D-Bus オブジェクトにはプロパティとメソッドが紐付いて定義されており、コマンドラインで参照できます。
中々使う機会はないのですが、こういったものも存在することを知っておくとたまに便利かもしれません。

# オブジェクトを指定して、メソッドやプロパティの名前や型情報、持っている値を参照できる
gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/DnsManager

# node /org/freedesktop/NetworkManager/DnsManager {
# (中略)
#   interface org.freedesktop.NetworkManager.DnsManager {
#     methods:
#     signals:
#     properties:
#       readonly s Mode = 'systemd-resolved';
#       readonly s RcManager = 'symlink';
#       readonly aa{sv} Configuration = [{'nameservers': <['192.168.1.1']>, 'interface': <'eno1'>, 'priority': <100>, 'vpn': <false>}];
#   };
# };

まとめ

具体例を入れたら非常に長くなってしまいましたが、systemd-resolvedの挙動についてまとめました。

D-Busの働きにより、NetworkManagerの設定変更によりsystemd-resolvedの設定を変更できるので、使い勝手は従来と大きく変わりません。

一方でVPNを利用している場合など、宛先ドメインによってDNSサーバを使い分けたいときにはsystemd-resolvedの per-link DNS 機能が役に立ちそうです。