VPN の DNS がすべての問い合わせを奪う:NetworkManager の dns-priority を修正する
VPN 接続は既定で優先度 0 になり、すべてのドメインを要求します。順序のために負の優先度を設定し、残りはルーティングドメインで返します。
オフィスの VPN に接続すると、突然すべての名前解決が社内リゾルバーへ送られます。公開名は引き続き解決されるため、ローカルのプリンター名が失敗するか、スプリットホライズンのレコードがオフィスのアドレスを返すまで、壊れているようには見えません。Fedora の構成によっては逆のことが起こります。トンネルが上がった後も ISP のリゾルバーが答え続けるのです。どちらも一つの問いです — この問い合わせを所有するのはどのリゾルバーか — そして NetworkManager のシステムでは、答えは接続ごとの DNS プロパティにあります。
二つの接続、一つの勝つリゾルバー
各接続は DNS の優先度を宣言します。通常の接続は既定で 50、VPN 接続は既定で 0 で、値が小さい方が勝ちます。この既定値のせいで、VPN 接続は要求するすべてのドメインについて有線接続を静かに上回ります。誰も選んでいないのに、ゼロが五十に勝つのです。
優先度は候補の順序を決めます。勝った相手に何を尋ねるかを制限するものではありません。検索ドメインのない VPN 接続はすべてを要求するので、それを優先すると公開の問い合わせもトンネルに入り、スプリットトンネルの設計では社内リゾルバーが転送する先へも入ります。
優先度は順序を決め、検索リストは所有を決める
nmcli -f connection.id,ipv4.dns,ipv4.dns-search,ipv4.dns-priority con show "Corp VPN"
resolvectl status
readlink -f /etc/resolv.conf
検索リストは所有の宣言です。corp.example のような裸のドメインは検索サフィックスとルーティングドメインになります。ルーティング専用の形式 ~corp.example は、短い名前にドメインを付加せずに、そのサフィックス配下の問い合わせをこの接続へ送ります。このチルダが「このサーバーに聞く」と「このサーバーに聞いて、入力も補完してもらう」の違いです。
ユーザーが短いホスト名を入力し、レコードが完全修飾名でも指定される場合は、両方の形式を使います。リゾルバーが裸の名前をどう補完するかを変えずに分割だけをしたい場合は、ルーティング形式だけを使います。
次に優先を明示して、接続を再度有効にします:
nmcli con modify "Corp VPN" ipv4.dns-priority -100
nmcli con modify "Corp VPN" ipv6.dns-priority -100
nmcli con modify "Corp VPN" ipv4.dns-search "~corp.example,corp.example"
nmcli con up "Corp VPN"
負の優先度は「この接続を優先せよ」と伝える文書化された方法で、両方のアドレスファミリーを設定すれば IPv6 接続のあるホストでも動作が一貫します。接続を再度有効にせずにプロパティを変更しても、今動いているものは何も変わりません。だからこの手順は明示的な up で終わります。
分割は systemd-resolved に任せる
既定の dnsmasq プラグインは問い合わせを一つのローカルリゾルバーに流すため、リンクごとの分離は粗いままです。systemd-resolved はリンクごとの分割を公開し、resolvectl query は応答したリンクを名指しします。
systemctl enable --now systemd-resolved
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
systemctl restart systemd-resolved NetworkManager
resolvectl query build.corp.example
その後、社内リンクが corp.example を担い、公開リゾルバーが残りを扱います。問い合わせが間違ったリンクに届くと、コマンドはそれに応答したリンクを名指しします。それを知ることは、アドレスだけを知るより有用です。
DNS の設計をトンネルの種類に合わせる
すべてをトンネルに流すプライバシー設計は、分割リストを持つべきではありません。公開名をローカルリゾルバーに返してしまい、意図が台無しになるからです。スプリットトンネルの設計には必要で、それがなければ社内リゾルバーがすべての公開問い合わせを見てしまい、トンネルが落ちた瞬間に構成が壊れます。
さらに、トンネル外の DNS を遮断するキルスイッチがあります。トンネルのゾーンは解決でき、それ以外はすべて失敗しますが、接続自体は完全に健全です。実用的な答えは、LAN のリゾルバーへの明示的な許可か、社内ゾーンだけを転送するローカルキャッシュです。どちらもトンネルの欠陥ではなく、所有者のある例外です。
インターフェースの起動時にリゾルバー構成を書き換えるプロファイルもほぼ同じように振る舞います。ローカルリゾルバーに到達できなくなると、トンネルが知っている名前だけが解決されます。どの仕組みが当てはまるにせよ、各状態でどの名前が動くべきかを書き留め、その一覧を報告を扱う人に渡してください。
リンクごとに確認し、再接続後にもう一度確認する
内部名と公開名を一つずつ尋ね、問い合わせが実際どこへ行くかを見ます。リーク検査ページが教えるのはどのリゾルバーが答えたかであり、システムがどのリンクを選んだかではないので、いま設定した順序については何も語りません。
切断して再接続した後、二つの問い合わせをもう一度実行します。接続が有効になると優先度は再適用され、古いシンボリックリンクは誰も覚えていないアップグレードを生き延びることがあります。最後に書き留めておきたいのは、どのリゾルバーがどのサフィックスを所有するか、そして明日も有効な確認です。