1 127.0.0.53:stub resolver の役割
systemd-resolved は、LAN や VPN から配られる DNS サーバー情報を集約し、libc が参照する「システムの名前解決窓口」を一本化するためのデーモンです。多くの環境では実アップストリーム(ルータや ISP、社内 DNS)ではなく、まず 127.0.0.53 上の短いスタブへ問い合わせ、その背後で resolved が実際の転送を担います。ファイルでは /etc/resolv.conf がシンボリックリンクで ../run/systemd/resolve/stub-resolv.conf を指している例が典型で、そこに nameserver 127.0.0.53 と書かれます。
この設計はデスクトップ向けには理にかなっていますが、Mihomo の TUN スタックは「アプリが発行した UDP/TCP の 53 番問い合わせ」をトンネル内へ吸い上げ、コア内の dns ブロックで処理しようとします。アプリと OS が依然として 127.0.0.53 だけを見ていると、トラフィックがループして遅い・タイムアウトする・意図しない経路に抜ける、という三種類の症状に分かれやすいです。前段の Linux に Mihomo を載せ、systemd で常駐させる手順まで終わっている読者は、本稿をそのまま「TUN へ進むための DNS レイヤの仕上げ」として使えます。
2 まず現状を数字で見る
推測で設定をいじる前に、次の二つで「誰が 53 を聴き、誰に名前を投げているか」を確定させます。
resolvectl status
ls -l /etc/resolv.conf
ss -lunp | head
resolvectl status では各インターフェースの DNS Servers と Current Scopes を確認します。127.0.0.53 が 「Link XX」 の上流として並んでいないか、逆にグローバルな upstream が別にあるかをメモします。ss -lunp では 127.0.0.53:53 を systemd-resolve が握っている事実を押さえます。ここまでで「stub が生きている」ことが分かれば、以降のパターン A / B のどちらを選ぶか判断しやすくなります。
resolv.conf をそのままマウントしている場合、コンテナ内から見える名前解決とホストの resolved は一致しません。症状がコンテナだけで起きるときは、まずマウント元を疑ってください。
3 ありがちな症状の読み方
- 遅延や間欠的な失敗: 問い合わせが
127.0.0.53→コア→再びローカルループに入り、TTL 待ちやタイムアウトが積み上がる。 - 「プロキシに乗っているはずなのに漏れている」: アプリが OS の getaddrinfo だけを使い、解決結果の IP がコアのルール評価とズレる。特に redir-host と fake-ip を切り替えた直後に起きやすいです。DoH/FakeIP の整理と合わせて読むと原因が見えやすくなります。
- TUN オンだが
dig @127.0.0.53だけ妙に生きている: stub が残存し、アプリケーションのライブラリはそこに固定されている。これは設定ミスというより「スタックが二重化している」サインです。
これらをログで追うときは、短時間だけ log-level: debug に上げ、同じホスト名への重複クエリがループしていないかを connection ログの読み方に沿って確認すると安全です。常時 debug はノイズと秘密情報の両面で避けた方がよいです。
4 パターン A:stub リスナーを止め、Mihomo が公式の窓口になる
シンプルに割り切るなら、systemd-resolved の stub を止め、 libc が直接コアのリスナーを見る形に寄せます。サーバー用途や「TUN を常時オンにしたいデスクトップ」で採用されやすい構成です。
/etc/systemd/resolved.confでDNSStubListener=noを設定(コメントアウトを外す)。sudo systemctl restart systemd-resolvedのあと、ss -lunpで127.0.0.53が消えたことを確認。/etc/resolv.confを/run/systemd/resolve/resolv.confへ向けるか、固定で Mihomo の DNS リスナー(例:127.0.0.1の UDP/TCP ポート)を指す運用を NetworkManager や netplan から宣言します。
ポイントは 「stub を止めたのに resolv.conf がまだ stub を指している」中途半端を作らないことです。リンク先を変えたら、cat /etc/resolv.conf で実際の nameserver 行が期待どおりか必ず再確認してください。
resolv.conf と競合することがあります。企業端末ではインフラポリシーを優先し、必要なら IT へ相談してください。
5 パターン B:resolved を残し、上流だけ Mihomo に送る
ノート PC で複数プロファイルを切り替えたい場合、stub を完全消失させず、upstream をループしない場所に固定するほうが運用が楽なことがあります。起きることはシンプルで、systemd-resolved に「このマシン上の Mihomo が権威的なフォワーダだ」と教えることです。
手順のイメージは次のとおりです。resolvectl dns で対象インターフェース(多くは wlan0 や eth0)に Mihomo のローカル DNS アドレスをセットし、resolvectl domain で必要なら検索ドメインを併記します。Mihomo 側の dns.listen が 127.0.0.1:1053 など高位ポートの場合、resolved の文法では 127.0.0.1:1053 をポート付きで渡せる版とそうでない版があります。受け入れ側がポート指定を解釈しない場合は、コアで 53 を占有しないようにしつつ、iptables/nft のリダイレクトや TUN の dns-hijack で吸い上げる組み合わせが現実的です。
# Replace IFACE with your link, e.g. wlan0
sudo resolvectl dns IFACE 127.0.0.1
sudo resolvectl flush-caches
実コマンドを流し込む前に、Mihomo が本当にそのアドレスで待ち受けているかを ss -lunp で担保してください。空振りすると一瞬で全ホストの名前解決が止まります。
6 Mihomo 側:TUN・DNS ハイジャック・モードの整合
tun と dns-hijack
TUN セクションでは、スタック(system / gvisor / mixed など)に応じて挙動が変わるため、ディストリビューションごとの推奨値はリリースノートも確認してください。多くの場合、dns-hijack に any:53 に加えて tcp://any:53 や、環境によっては 127.0.0.53:53 明示が有効です。意図は一つで、「アプリがどこへ投げてもコアの DNS パイプラインに着地させる」ことです。
tun:
enable: true
stack: system
auto-route: true
strict-route: false
dns-hijack:
- any:53
- tcp://any:53
dns:
enable: true
listen: 127.0.0.1:1053
enhanced-mode: fake-ip
nameserver:
- https://dns.google/dns-query
# Add fake-ip-filter / fallback per your policy
上記はあくまで雛形です。enhanced-mode を redir-host に切り替える場合は、resolved と libc のキャッシュが背中合わせになるため、切り替え直後に resolvectl flush-caches とアプリ再起動をセットで行うと混乱が減ります。
権限とポート競合
プライベートポート以外の 53 を Mihomo 本体が直接握る構成は、CAP_NET_BIND_SERVICE や root 実行が絡むことがあります。systemd ユニットで CapabilityBoundingSet や AmbientCapabilities を足す場面もあるので、セキュリティ境界を理解したうえで最小権限に寄せてください。
7 反映後の検証チェックリスト
resolvectl status:期待する upstream とインターフェースの対応が崩れていないか。dig example.comとdig @127.0.0.1 -p 1053 example.com(ポートは自環境に合わせる):どちらが実際に使われるべきかに整合するか。- Mihomo ダッシュボードまたは API の DNS ログ:同一クエリの多重ループが止まったか。
- ブラウザと CLI の両方で、意図した出口に乗ったときの挙動差が解消したか。
どこか一歩でも齟齬があると「TUN は動いているのに世界が半分古い」状態が続きます。検証を一気に終えたら、log-level を戻し、変更したユニットファイルや netplan のバックアップを残しておくと次の自分が助かります。
8 まとめ
Linux のモダンなデスクトップは systemd-resolved を前提に組まれており、その入口である 127.0.0.53 は「悪者」ではなく、ユーザー空間とカーネルの間をつなぐ stub です。問題は Mihomo TUN が名前解決の責務まで抱え込もうとするときに、stub とコアの期待が衝突する点にあります。パターン A で stub を止めて一本化するか、パターン B で resolved の上流をコアに向けるか、どちらかに寄せたうえで dns-hijack と listen ポートを矛盾なく揃えれば、検索意図どおりの「ループ・漏れ・遅延」はほぼ解消できます。
GUI で同じテーマを追いたい場合でも、概念は共通です。ルールとログを一体で扱える Clash 互換クライアントは、トラブル時に原因箇所へ戻るのが速く、コマンドラインのみの構成と並べても運用負担のバランスが取りやすいです。オープンソースの参照や議論は GitHub で追えますが、配布パッケージの第一入手先として本站のダウンロードページを使うと、同一プロジェクトでもビルド差分に振り回されにくくなります。