「/etc/resolv.conf」を奪い合う2つのLinuxサービスを止める
編集する前に、ファイルの所有者を確認しましょう。不変属性は競合を隠すだけで、再接続やDHCPの更新は手作業のDNS修正を何度も巻き戻します。
DNSが元に戻り続け、VPNの再接続があなたの修正を無効にし、しまいにはチームの誰かがファイルをロックしようと言い出します。その流れは症状であって解決策ではありません。二つのコンポーネントがどちらも /etc/resolv.conf を所有していると考えており、最後に書いたほうが勝つのです。ファイルをロックしても所有権の競合は解決せず、隠すだけで、本来望んでいたコンポーネントを壊してしまいます。以下の四つの確認を順に行い、ファイルではなくそれを所有する層で構成を直してください。
確認 1: シンボリックリンクか、どこを指すか
まずファイル自体から始めます。編集しているパスがそもそもファイルではないかもしれません。リゾルバのランタイムディレクトリへのシンボリックリンクは、クライアントスクリプトが書き換える通常のファイルとはまったく異なる挙動をします。
ls -l /etc/resolv.conf
readlink -f /etc/resolv.conf
cat /etc/resolv.conf
systemd-resolved の stub パスへのリンクは、ファイルの中身が生成されることを意味し、手作業の編集は次のイベントで上書きされます。nameserver 行を持つ通常のファイルは、接続時に動くクライアントや DHCP フックが書くのが普通です。どちらも正当ですが、大事なのは変更する前にどちらなのかを知ることです。
確認 2: どのデーモンが動いているか
インストールされているものではなく、実際に動いているものを調べます。現代のディストリビューションには複数のリゾルバが存在しうるので、ループバックインターフェースで 53 番ポートを握っているものが応答しています。
systemctl status systemd-resolved --no-pager
resolvectl status
ss -lunp | grep ':53'
status の出力にはリンクごとの DNS サーバーとルーティングドメインが示され、これが split DNS に必要な地図になります。内部名は内部サーバーへ、それ以外は公開サーバーへという方針はリンクごとのドメインに住んでおり、それをファイル内の静的な nameserver 一覧に平坦化すると、その区別は静かに失われます。
NetworkManager 自身の設定が、どのバックエンドを使うかを決めます。メイン設定ファイルの dns= 設定は、内部リゾルバ、dnsmasq、systemd-resolved、none のどれかを選び、ディストリビューションがすでに選んでいる場合もあります。このホストで「DNS を直す」とは何かを決める前に、その設定を読みましょう。
確認 3: VPN クライアントに何を書くことが許されているか
競合はたいていここに住んでいます。リゾルバのファイルを直接書き換える VPN クライアントは、同じファイルを保守するあらゆるサービスと争い、先に動いたほうが負けます。最近の OpenVPN はファイルを書き換える代わりに DNS 設定を systemd-resolved に渡せますが、これが望ましい協調的な振る舞いです。古いスクリプトや手書きのフックは、レガシーなヘルパーを呼んだり、単にファイルを上書きしたりしがちです。
WireGuard の状況も形は似ています。インターフェース設定自体は DNS を管理せず、周囲のツールがリゾルバヘルパーや resolved 連携を通じて管理します。そうしたヘルパーが二つインストールされていれば、どちらも接続時に自らの世界観を適用しようとします。
推測するのではなく、クライアントの up と down のフックを読みましょう。/etc/openvpn/up.sh や同等のディレクトリにある数行の shell が、そうでなければランダムに見える挙動を説明してくれますし、クライアントに競争ではなく協調させる場所でもあります。
確認 4: ファイルではなく所有者の側で直す
所有者がわかったら、その所有者がサポートするインターフェース経由で DNS を設定します。systemd-resolved なら静的ファイルではなくリンクごとの設定とルーティングドメイン、NetworkManager なら接続プロファイルの DNS 設定と選択したバックエンド、resolvconf 連携を使うディストリビューションならそのヘルパー経由の書き込みです。
ファイルを不変にしてはいけません。不変属性は正当な DHCP 更新や VPN 更新も、あなたが戦っていたものと一緒に止めてしまい、次のパッケージ更新や再接続のときに設定からは説明できない障害を生む状態を残します。それは所有権の競合を隠すだけで、まさに解決しようとしていたものなのです。
二つのコンポーネントが本当に共存しなければならないなら、仕事を分けましょう。ネットワークマネージャーにインターフェース層の設定を所有させ、VPN クライアントには同じ連携を通じてリンクごとの DNS とドメインを提供させ、ファイルを直接書く三つ目のスクリプトは削除します。systemd-resolved のドキュメントには、サポートするモードと、それぞれが /etc/resolv.conf で何を見せるかが書かれています。残すべきポリシーの量に合うモードを選びましょう。split DNS が重要なら、リンクごとのドメインを持つ stub 構成がそれを保ち、上流サーバーをファイルにコピーするモードはそれを静かに平坦化します。
編集後ではなく再接続後に検証する
変更直後に正しく見えるファイルは何も証明しません。次のイベントがまだ起きていないからです。以前に壊していた流れを再現します。トンネルを下げて上げ、DHCP リースを更新するかインターフェースを切り替え、それから同じ値を読み直します。
resolvectl query build.corp.example
resolvectl query example.com
resolvectl status | head -40
次に重要な区別を確認します。内部名は内部サーバーで解決され、公開名は引き続き機能することです。すべてを内部サーバーへ送る設定は、誰かがトンネル経由でインターネットに出ようとするまで問題なく見えますし、すべてを公開サーバーへ送る設定は内部名を漏らします。
所有者、バックエンド、シンボリックリンクのモードをデプロイメモに記録します。その一つの段落が、将来のパッケージ更新をインシデントからレビュー項目に変え、次の人が chattr コマンドで同じ競合を再発見するのを防ぎます。