Mac が VPN で使う DNS リゾルバを確認する方法
scutil --dns は macOS が実際に構築したリゾルバ状態を出力します。各ブロックをインターフェースに対応づけ、実在の名前を問い合わせ、どのサーバーが応答したかを証明します。
scutil --dns は、設定パネルに入力した一台の DNS サーバーではなく、macOS が実際に参照しているリゾルバ設定を出力します。出力が多くの人の予想より長いのは、システムが既定の経路用、スコープ付きインターフェース用、ドメイン固有の転送用に別々のリゾルバブロックを保持しているからです。出力の先頭にあるブロックが、その問い合わせに答えるブロックとは限りません。社内で解決していたプライベート名が VPN を張った瞬間に失敗するなら、食い違いは照合ルールと経路にあり、サーバーアドレスにはありません。まず順序を読み、そのうえで実際の照会で答えを証明してください。
アドレスより先にブロックの順序を読む
各エントリは resolver #N で始まり、nameserver[0]、時には if_index、flags 行、reach を並べ、ブロックが単純な既定値でない場合は domain と search domain の一覧も示します。ドメインを持たない 1 番目のリゾルバが、公開名を扱う既定のリゾルバです。フラグに Scoped を持つものは if_index が示すインターフェースに結び付き、Supplemental と記されたものはその下に並ぶドメインにだけ適用されます。
選択は接尾辞の照合です。intranet.corp.example のような名前では、domain の項目が最も長く一致する接尾辞であるリゾルバが macOS に優先されるため、domain : corp.example を持つブロックは出力のずっと下にあっても既定値に勝ちます。同じ長さのドメイン間の同着は order が解消します。最初の一瞥にはこれ以上はほとんど要らないので、ブロックを最初から最後まで読みたいときは出力全体、ブロックの順序を知るには冒頭の行、フィールド行の横にブロック見出しを並べるには絞り込んだ一巡で足ります。スコープ付きブロックは独自の見出しの下に現れ、上の一覧の続きのように読めるので、出力を引用するときは必ず見出しを視界に入れておいてください。
scutil --dns
scutil --dns | sed -n '1,45p'
scutil --dns | grep -nE '^resolver #|^DNS configuration'
scutil --dns | grep -nE 'resolver #[0-9]+|if_index|domain|flags|nameserver'
if_index をトンネルのインターフェースに対応づける
索引は、その背後のインターフェースを名指しできて初めて役に立ちます。scutil --nwi はシステムが保持するネットワーク情報を一覧し、主インターフェースと到達性を含みますが、ifconfig -l は名前を返し、utun0 から utun9 までがクライアントが通常作るトンネルの候補です。Apple は scutil --dns を自社の検査例として デプロイメントガイド で使い、Network Extension が選択したドメインを選択したリゾルバへ振り分けられる仕組み——スコープ付きブロックが示すのはまさにそれ——を文書化しています。
問題を決める検査は、リゾルバがトンネルの張った経路上にあるかどうかです。
scutil --nwi
ifconfig utun4
route -n get 10.20.0.53
netstat -rn | grep -m5 utun
route -n get がリゾルバの項目と一致しないゲートウェイとインターフェースを返すなら、アドレスは完全に正しくても到達できないことがあります。社内サブネットだけを運ぶ分割トンネルは、その外にある DNS サーバー宛ての問い合わせを落とし、障害はタイムアウトとして現れるので、設定の出力はきれいに見え続けます。
まずシステムのリゾルバに、次にサーバーに問い合わせる
三つの道具は三つの別の問いに答え、それらを混同することがこの分野の誤った結論の大半を生みます。dscacheutil -q host -a name は通常のアプリが使うのと同じシステムリゾルバを通るので、スコープ付きと補助のブロックが効きます。サーバー引数なしの dig は /etc/resolv.conf を読みますが、これは macOS のスコープ付き解決の拠り所ではなく、ドメインごとの規則を完全に飛ばします。dig @address はちょうど一つのサーバーに届くだけで、選択について何も教えてくれません。
dscacheutil -q host -a name intranet.corp.example
dig intranet.corp.example
dig @10.20.0.53 intranet.corp.example
nslookup intranet.corp.example
最初のコマンドが答え、二番目が NXDOMAIN を返すなら、探していた分割が見つかったのであり、壊れているものはありません。最初がタイムアウトし dig @10.20.0.53 が応答するなら、リゾルバは動いているもののトンネルの選んだ経路では届かないので、次の一手は DNS 設定の変更ではなく route -n get です。プライベート名は、チケットや共有メモに貼るものの中に入れないでください。
出力が示せないもの
リゾルバ一覧の上や隣にはいくつもの層があり、どれも出力が決して触れない場所へ問い合わせを送れます。安全な DNS を有効にしたブラウザは独自の暗号化経路で解決することがあり、dscacheutil がタイムアウトする間にページが読み込まれても矛盾ではありません。セキュリティソフトが入れる DNS プロキシは Network Extension プロバイダとして動き、自ら問い合わせを転送するので、scutil --dns が示すのは設定された内容であり、プロキシが最終的に使った上流アドレスではありません。検索ドメインは三つ目のねじれを加えます——修飾のない名前には一覧の接尾辞が順に付くので、答えたブロックは思いもよらなかったものかもしれません。
古い応答は最も安く除外でき、最も見落とされます。
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
scutil --dns | head -20
フラッシュ後に出力が変わるなら、状態はキャッシュされており、以前の出力はもう存在しないトンネルを記述していました。変わらないなら、設定はそのままなので、問いは経路とアプリのポリシーへ移ります。
前後で使う短い手順
ベースラインを取るのはポリシーが許す場所だけにとどめ、ファイルは同期フォルダの外に置いてください。以下の順序は私がクライアント機で実行するものです——ベースラインを取り、接続し、もう一度取り、そのうえで差分を見て照会します。
scutil --dns > ~/dns-before.txt
# connect the VPN, then:
scutil --dns > ~/dns-after.txt
diff -u ~/dns-before.txt ~/dns-after.txt
dscacheutil -q host -a name intranet.corp.example
差分は行ではなくブロック単位で読んでください。トンネルのインターフェース索引とプライベート接尾辞の domain 項目を持つ新しいスコープ付きリゾルバが期待される形です。公開アドレスを指す新しい既定リゾルバは、クライアントプロファイルの所有者に上げる価値のある発見です。そのうえで、実際に失敗したアプリの中で照会を繰り返し、シェルだけで済ませず、結果の横に route -n get のインターフェースを書き留めてください。
変更を戻すには、切断してベースラインの取得をやり直します。スコープ付きブロックが消えていれば、クライアントが後始末をしています。ブロックがもう存在しないインターフェースに対して並んだままなら、状態は古いので、プロファイルに手を入れる前にフラッシュと再接続が最も穏やかな修正です。
四つの検査をこの順で行います——リゾルバのブロックが名前の最も長い接尾辞に一致するか、そのブロックがトンネルの張ったインターフェースに結び付いているか、そのネームサーバーへの経路がトンネルを使うか、そして dig ではなくシステムのリゾルバが答えを返すか、という四つです。最初に落ちた検査が、DNS ポリシー、トンネルの経路、アプリのどれを変えるべきかを教えてくれます。四つすべて通るのに名前が解決しないなら、問題は DNS より上にあり、次に見るべきはその手前で 53 番ポートに応答しているものすべてです。