同じ VPN 上に二台のマシンがあり、ホスト名も同じなのに、解決できるのは片方だけです。違いは通常トンネルではなく、どのリゾルバーが答えたかにあります。Linux デスクトップでは、その名前は名前サービススイッチを通り、systemd-resolved の stub を通り、あるいはアプリケーションが持ち込んだリゾルバーを通ることがあり、それぞれが同じ接尾辞に独自の規則を適用します。規則が分かれるのはまさに接尾辞の部分です。.local は中立なラベルではないからです。何かの実行順序を変える前に、失敗しているプログラムが実際にどの経路を通るのかを突き止めてください。

二つのリゾルバー、二つの異なる答え

従来の glibc ルックアップは nsswitch.conf の hosts: 行をたどり、どれかが答えるまで各モジュールに順番に問い合わせます。stub リスナー経由で systemd-resolved と話すクライアントは代わりに単一のプロセスに問い合わせ、そのプロセスはリンクごとのポリシーと独自の特殊ケースを適用します。独自のリゾルバーを同梱する、またはそうするライブラリをリンクするアプリケーションは両方を迂回できます。したがって一つのホスト名が一台のマシン上に三人の所有者を持ち、そのうち VPN の意図を知っているのは一人だけです。

何かを変える前に所有者を特定してください。修正は三人それぞれで異なるからです。同じデスクトップ上のブラウザーとコマンドラインツールが正当に食い違うことがあり、その食い違いこそが手がかりであって不具合ではありません。ブラウザーが名前を解決しツールが解決しないなら、デバッグしているのはリゾルバーの経路であって VPN のルートではありません。ルートは別に確認し、ルーティングの障害を名前サービスの障害と取り違えないようにしてください。両者が一致するなら、障害はホストより上流にあり、ここのリゾルバー設定にこれ以上手を入れる価値はありません。

どのコマンドが実際にリゾルバーに問い合わせたのか

読み取り専用の二つのコマンドは関連する問いに答え、それらを比較すれば設定を読むより速く問題を切り分けられます。実際の内部名を使い、出力は非公開にしてください。内部のレコードは内部のままです。どちらも、失敗しているプログラムが動いているのと同じセッションで実行します。

getent ahosts files.corp.local
resolvectl query files.corp.local
resolvectl status

最初のものは設定されたモジュールを通して問い合わせるので、典型的なプログラムが経験するのはこちらです。二つ目は systemd-resolved に直接問い合わせ、応答したリンク、プロトコル、サーバーを報告します。アドレスで社内サーバーに問い合わせても、サーバーがそのレコードを持っていることしか証明できません。アプリケーションがそもそも尋ねたかどうかは何も語らないからです。VPN を運ぶリンクとそれが宣言するドメインを読み、その一覧を失敗している名前の接尾辞と比較してください。

なぜ .local はユニキャストゾーンを飛ばすのか

systemd-resolved のリファレンス によれば、.local で終わる複数ラベルの名前は、mDNS が有効なときは通常のユニキャスト DNS ではなくマルチキャスト DNS に送られます。RFC 6762 はその名前空間をリンクローカルマルチキャスト用に確保しているため、社内ゾーンが正しいレコードを持っていても、その接尾辞では決して参照されません。トンネルがリゾルバーを完璧にルーティングしても、競争に負けることがあります。キャッシュからの ping の成功は、たった今起きた検索について何も語りません。

文書化された例外があり、ほとんどの場合はこれで片づきます。リンクがその接尾辞を検索ドメインまたはルーティングドメインとして宣言すると、デーモンはそれをユニキャスト経路に留め、マルチキャストの特殊ケースを飛ばします。これはリンクごとのポリシーであり、グローバルな優先設定ではありません。同じ VPN サーバーを使う二台のマシンでも、一方のネットワークプロファイルがドメインを宣言し他方が宣言しなければ挙動が変わります。だから覚えている方ではなく、実際に有効なプロファイルを読んでください。

狭い例外はグローバルな並べ替えに勝る

名前を変えられない既存ゾーンでは、リゾルバーと VPN の担当者に、それを運ぶ接続上でドメインを宣言させてください。その一つの接尾辞に対するルーティングドメインの項目と、意図したサーバーアドレスがあれば、名前付きの担当者による小さな変更で済み、手で編集したファイルではなくリンクとともに生き残ります。

切断後も維持されます。リンクが戻るとリンクポリシーが再適用されるからです。共有の nsswitch.conf を編集するのは魅力的で高くつく代替案です。マシン上のすべての名前、ローカル探索も含めて検索の挙動が変わるからです。マルチキャストデーモンを無効にするのも同じではありません。ローカルの挙動を消すだけで、どのリゾルバーにも社内ゾーンを教えないからです。サービスが引き継ぎではなく新規なら、組織が管理するサブドメインの下に置き、競争を完全に避けてください。名前がファームウェアに固定されていたりラベルに印刷されていたりするなら狭い例外を選び、クライアントをまだ管理できるなら新しいサブドメインを選んでください。

変更後に何を証明するか

修正は、テスト用に導入したツールではなくアプリケーション自身のルックアップで証明し、代わりに答えうるキャッシュをすべて消してください。次にリゾルバーの経路と、答えを返したリンクを確認します。最後に、ローカル探索が変更前と同じように振る舞うことを確かめてください。現れなくなったプリンターこそ、人が最後に気づく部分です。次の三つのコマンドは、名前、経路、TLS の同一性をこの順で確認します。

getent ahosts files.corp.local
resolvectl query files.corp.local
curl -sv --max-time 5 https://files.corp.local/ 2>&1 | head -n 20

証明書が違えば正しいアドレスでも厳格なクライアントは失敗するので、解決が通ったらアプリケーションが期待する名前を確認してください。サービスがオフィスの範囲だけでなく VPN のアドレスからの接続も受け入れることを確かめます。ドメインの範囲、サーバー、担当者を接続のドキュメントに書き留めてください。そうしなければ、次にこの障害に出会った人が、あなたが今避けたのと同じグローバル編集に手を伸ばします。

関連記事