はじめに

平素は大変お世話になっております。
クイックガードのパー子です。

先日、Ubuntu Server 20.04 の DNSサーバをデフォルトのものから変更したところ、あるタイミングで何者かが勝手に /etc/resolv.conf を書き換えて、変更を巻き戻してしまう事案が発生しました。
プラットフォームは IDCFクラウド です。

その調査結果と対策をご紹介します。

いきなり結論

お急ぎの方のために結論を述べます。

“IDCFクラウドの Ubuntu Server 20.04 において DNSサーバを変更する場合は、dhcpcd を停止しておく”

/etc/resolv.conf を書き換えた犯人は、標準でインストールされている DHCPクライアントの dhcpcd でした。
dhcpcd を停止しておけば書き換わりを防げます。

具体的な手順は以下のとおりです。

  1. dhcpcd.service を無効化する。

    $ sudo systemctl disable dhcpcd.service
    
  2. マシンを再起動する。

事案

発生した事案について、順を追って経緯をご説明します。

背景

とある案件にて、IDCFクラウド上に Ubuntu Server 20.04 を構築しました。
仮想マシンのイメージは 標準提供テンプレートの Ubuntu Server 20.04 LTS 64-bit です。

システム内部で利用するドメインの名前解決のために、リゾルバが参照する DNSサーバを変更する必要がありました。

(DNSサーバは仮に 10.3.0.101, 10.3.0.102 の 2つとします。)

DNSサーバの変更

Ubuntu Server 20.04 では systemd-resolved を使っているため、/etc/resolv.conf の直接の書き換えは禁止されています。

正しい変更方法はいくつかありますが、今回は Netplan を用いて変更しました。

既存の設定ファイルはいじらずに、カスタム用のファイル /etc/netplan/99-dns.yaml を作成して、

/etc/netplan/99-dns.yaml
---
network:
  version: 2
  renderer: 'networkd'
  ethernets:
    # このマシンのインタフェイス名は `ens160` だった
    ens160:
      nameservers:
        addresses:
          - '10.3.0.101'
          - '10.3.0.102'
        search:
          - 'cs106idcfcloud.internal'
      dhcp4-overrides:
        use-dns: false
        use-domains: false

これを Apply しました。

$ sudo netplan apply

正常な状態

Apply の結果、期待どおりの状態となりました。

systemd-resolved は Local DNS stub listener として 127.0.0.53 を提供しているため、/etc/resolv.conf は以下のようになります。
(Symlink となっており、実体は /run/systemd/resolve/stub-resolv.conf です。)

/etc/resolv.conf (-> /run/systemd/resolve/stub-resolv.conf)
# This file is managed by man:systemd-resolved(8). Do not edit.
#
# This is a dynamic resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists all
# configured search domains.
#
# Run "resolvectl status" to see details about the uplink DNS servers
# currently in use.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 127.0.0.53
options edns0 trust-ad
search cs106idcfcloud.internal

実際に参照している DNSサーバは resolvectlコマンドで確認できます。

$ sudo resolvectl status ens160
Link 2 (ens160)
      Current Scopes: DNS
DefaultRoute setting: yes
       LLMNR setting: yes
MulticastDNS setting: no
  DNSOverTLS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
  Current DNS Server: 10.3.0.102
         DNS Servers: 10.3.0.101
                      10.3.0.102
          DNS Domain: cs106idcfcloud.internal

事案発生

ある日の AM6時頃、名前解決ができなくなった旨のアラートを監視システムが発報しました。

すぐにインスタンスを確認したところ、IDCFクラウドのデフォルトの DNSサーバを参照するように /etc/resolv.conf が書き換えられていました。

/etc/resolv.conf (-> /run/systemd/resolve/stub-resolv.conf)
# Generated by resolvconf
domain cs106idcfcloud.internal
nameserver 10.41.0.1
nameserver 158.205.229.244
nameserver 158.205.237.131

ん… Generated by resolvconf とな?

調査

どうやら resolvconf が悪さをしているようです。

resolvconf も /etc/resolv.conf を管理するためのツールなのですが、systemd-resolved と競合するので両者の併用は避けるべきです。

resolvconf を発動したのは何者でしょうか?
当該時刻に発生したイベントを調べたところ、APT のアップグレードが走ってネットワーク関連のサービスが再起動されていました。

/var/log/syslog
...
Jan  5 06:00:20 myhost systemd[1]: Starting Daily apt upgrade and clean activities...
...
Jan  5 06:06:19 myhost systemd[1]: Stopping Network Name Resolution...
Jan  5 06:06:19 myhost dhcpcd[1045]: ens160: pid 1 deleted IP address xxx.xxx.xxx.xxx/27
Jan  5 06:06:19 myhost dhcpcd[1045]: ens160: deleting route to xxx.xxx.xxx.xxx/27
Jan  5 06:06:19 myhost dhcpcd[1045]: ens160: deleting default route via xxx.xxx.xxx.xxx
Jan  5 06:06:19 myhost systemd[1]: systemd-resolved.service: Succeeded.
Jan  5 06:06:19 myhost systemd[1]: Stopped Network Name Resolution.
Jan  5 06:06:19 myhost systemd-networkd[120053]: ens160: DHCPv4 address xxx.xxx.xxx.xxx/27 via xxx.xxx.xxx.xxx
Jan  5 06:06:19 myhost dbus-daemon[695]: [system] Activating via systemd: service name='org.freedesktop.hostname1' unit='dbus-org.freedesktop.hostname1.service' requested by ':1.148' (uid=100 pid=120053 comm="/lib/systemd/systemd-networkd ")
Jan  5 06:06:19 myhost systemd[1]: Starting Network Name Resolution...
Jan  5 06:06:19 myhost systemd-networkd-wait-online[120061]: managing: ens160
Jan  5 06:06:19 myhost systemd[1]: Starting Hostname Service...
Jan  5 06:06:19 myhost systemd[1]: Finished Wait for Network to be Configured.
Jan  5 06:06:19 myhost dhcpcd[1045]: ens160: rebinding lease of xxx.xxx.xxx.xxx
Jan  5 06:06:19 myhost systemd-resolved[120078]: Positive Trust Anchors:
Jan  5 06:06:19 myhost systemd-resolved[120078]: . IN DS 20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d
Jan  5 06:06:19 myhost systemd-resolved[120078]: Negative trust anchors: 10.in-addr.arpa 16.172.in-addr.arpa 17.172.in-addr.arpa 18.172.in-addr.arpa 19.172.in-addr.arpa 20.172.in-addr.arpa 21.172.in-addr.arpa 22.172.in-addr.arpa 23.172.in-addr.arpa 24.172.in-addr.arpa 25.172.in-addr.arpa 26.172.in-addr.arpa 27.172.in-addr.arpa 28.172.in-addr.arpa 29.172.in-addr.arpa 30.172.in-addr.arpa 31.172.in-addr.arpa 168.192.in-addr.arpa d.f.ip6.arpa corp home internal intranet lan local private test
Jan  5 06:06:19 myhost systemd-resolved[120078]: Using system hostname 'myhost'.
Jan  5 06:06:19 myhost systemd[1]: Started Network Name Resolution.
...

む… dhcpcd が動いていますね。
こいつが犯人のようです。

$ sudo resolvconf -l
# resolv.conf from ens160.dhcp
# Generated by dhcpcd from ens160.dhcp
domain cs106idcfcloud.internal
search cs106idcfcloud.internal
nameserver 10.41.0.1
nameserver 158.205.229.244
nameserver 158.205.237.131

dhcpcd は フック機能 を持っており、DHCP の状態変化をトリガーに /etc/resolv.conf を書き換えてしまうのです。

nohook script
        Don’t run this hook script. Matches full name, or prefixed with 2 numbers
        optionally ending with .sh.

        So to stop dhcpcd from touching your DNS settings or starting wpa_supplicant you
        would do:-
              nohook resolv.conf, wpa_supplicant

$ cat /lib/dhcpcd/dhcpcd-hooks/20-resolv.conf | head -n 15
# Generate /etc/resolv.conf
# Support resolvconf(8) if available
# We can merge other dhcpcd resolv.conf files into one like resolvconf,
# but resolvconf is preferred as other applications like VPN clients
# can readily hook into it.
# Also, resolvconf can configure local nameservers such as bind
# or dnsmasq. This is important as the libc resolver isn't that powerful.

resolv_conf_dir="$state_dir/resolv.conf"
NL="
"
: ${resolvconf:=resolvconf}

build_resolv_conf()
{

対策

単純に dhcpcd を停止すれば OK です。

dhcpcd が停止していればフックも走らないため、/etc/resolv.conf が書き換えられることはなくなります。

Ubuntu Server 20.04 の標準ネットワーク・マネージャである systemd-networkdDHCPクライアント機能 を持っているため、DHCP はそちらに任せます。
というか、そもそも systemd-networkd の DHCPクライアント機能はデフォルトで ON になっているため、競合しないようにシュッと dhcpcd を停止してしまうことをオススメします。

/etc/netplan/01-netcfg.yaml
# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
  version: 2
  renderer: networkd
  ethernets:
    ens160:
      dhcp4: yes
/run/systemd/network/10-netplan-ens160.network
[Match]
Name=ens160

[Network]
DHCP=ipv4
LinkLocalAddressing=ipv6

[DHCP]
RouteMetric=100
UseMTU=true

停止する際は、いきなり systemctl stop すると通信が遮断されてしまうので、いったん無効化してからマシンを再起動するとよいでしょう。

$ sudo systemctl disable dhcpcd.service

まとめ

IDCFクラウド Ubuntu Server 20.04 の DNSサーバを変更する際の注意点をご紹介しました。

このインスタンスには DHCPクライアントの dhcpcd が標準でインストールされていますが、systemd-resolved や systemd-networkd と競合します。
dhcpcd は停止してしまいましょう。

今後ともよろしくお願い申し上げます。