Vagrant で Ubuntu の VM を立ち上げたとき、 apt 等を行おうとすると、以下のようなエラーに遭遇した。
client: Err:1 http://security.ubuntu.com/ubuntu focal-updates/main amd64 libjpeg-turbo8 amd64 2.0.3-0ubuntu1.20.04.1
client: Temporary failure resolving 'proxy.local.example'
上記のエラーの内容はプロキシに接続できないというものだが、 問題のポイントはプロキシかどうかはあまり関係が無く、 名前解決に失敗しているという部分だ。
こういうのはだいたい systemd-resolved
のスタブリゾルバ周りの問題だと相場が決まっている。
…ということで、順番に確認しながら問題を解決していこう。
なお、 使った box は generic/ubuntu2004
で、 VirtualBox で VM をホストしている。
スタブリゾルバの確認
まず、 resolv.conf
を確認してみる。
$ cat /etc/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.
nameserver 127.0.0.53
テキストファイルのコメントに書いてあるとおり、コイツを書き換えるのは悪手。
ネームサーバーが 127.0.0.53 を指しているが、 これが systemd-resolved
のスタブリゾルバだ。
ここで言うリゾルバとは、ドメイン名を元にIPアドレスを解決する仕組みのことを指していて、 DNS なども同様の機能を持っている。
その中でも、スタブリゾルバは、(「スタブ」と言うだけあって、)それ自身はドメイン名とIPの対応リストを持たずに、 DNS 等の他のリゾルバを呼び出して解決する仕組みをもっている、ローカルのサービスだ。
ローカルアプリケーションが直接 DNS を参照せず、一旦スタブリゾルバを参照することで、 systemd
で管理された適切な DNS を自動的に参照されるようになったり、 DNS では解決できない hostname.local
といったホスト名ベースの名前解決 (mDNS) を行えるようになっている。
さて、 そのスタブリゾルバがどう動いているか resolvectl status
で確認してみよう。
$ resolvectl status
[...]
Global
Current DNS Server: 4.2.2.1
DNS Servers: 4.2.2.1
4.2.2.2
[...]
Link 2 (eth0)
Current Scopes: DNS
DNSSEC supported: yes
Current DNS Server: 4.2.2.1
DNS Servers: 4.2.2.1
4.2.2.2
208.67.220.220
10.0.2.3
4.2.2.1 や 208.67.220.220 などが DNS に設定されているのがわかる。
これらは、いわゆるパブリックDNSだ。
実は、先ほど名前解決できなかったプロキシは、 LAN 内のサーバーだった。 このため、 パブリックDNSに問い合わせられてしまっては、名前解決ができないのも当然だ。
パブリックDNS はどこで設定されているのか
名前解決に失敗する理由はわかった。 しかし、この DNS はどこで設定されているのか?
ざっと確認してみたところ、 resolved.conf
の設定と、 仮想マシン内の NAT を繋いだ NIC の netplan 設定に、それっぽい設定が書かれていた。
$ cat /etc/systemd/resolved.conf
[Resolve]
DNS=4.2.2.1 4.2.2.2 208.67.220.220
$ cat /etc/netplan/01-netcfg.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: true
dhcp6: false
optional: true
nameservers:
addresses: [4.2.2.1, 4.2.2.2, 208.67.220.220]
これらは通常の Ubuntu のインストールプロセスでは設定されないので、 generic/ubuntu2004
の box の作成者の味付けなのだろう。
これらの DNS のアドレスの設定を、プライベートネットワーク内の DNS のアドレスに書き換えてしまえば、きっと意図した動作に近づくはずだ。
スタブリゾルバを使わずに LAN内 の DNS を指定する
スタブリゾルバは、細かい優先度合いの制御などを外から行えない。 これはネットワークの状況に応じて最適調整してくれる設計だからなのだが、 Vagrant で立ちあげる VM のように、 NAT の DNS や ローカルの DNS が混在していると、うまく調整できずに、こういった問題の解決がやりにくい。
スタブリゾルバを動かしているままでも問題なく動いているのならそれでもかまわないのだが、 上記の DNS の書き換えを行ってもどうも意図通りに名前解決されない場合は、 systemd-resolved
のスタブリゾルバを使わずに、 /etc/resolv.conf
の nameserver を切り替えるようにしたい。
具体的には、 /etc/resolv.conf
が ../run/resolvconf/resolv.conf
へのシンボリックリンクになっているところ、 ../run/systemd/resolve/resolv.conf
へのシンボリックリンクに書き換える。
こうすることで、 resolved.conf
や netplan で設定した DNS が直接 /etc/resolv.conf
の nameserver に書かれるようになる。
詳しくは、 systemd-resolved.service のマニュアル あたりを見て欲しい。
…と、このような書き換えを、 Vagrantfile の定義に書き落とすと、以下のようになる。
なお、以下は 192.168.11.1
がローカルネットワークの DNS のアドレスである場合の例なので、適宜書き換えていただければと。
config.vm.provision :shell, inline: <<-'SHELL'
ln -sf ../run/systemd/resolve/resolv.conf /etc/resolv.conf
sed -i -E '/nameservers:/{n; s/(addresses:).*/\1 [192.168.11.1]/}' /etc/netplan/01-netcfg.yaml
netplan apply
sed -i -E 's/(^\s*DNS\s*=\s*).*$/\1192.168.11.1/' /etc/systemd/resolved.conf
systemctl restart systemd-resolved.service
SHELL
そうすると、 resolv.conf
の中身が以下のように netplan の設定などから求められた DNS に置き換えられ、スタブリゾルバを経由せずに名前解決されるようになる。
$ cat /etc/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 directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
nameserver 192.168.11.1
nameserver 10.0.2.3
このようになれば、ローカルの DNS でキチンと名前解決されるようになるだろう。