私的サーバー構築日誌：LAN内DNSサーバー Unbound

# 能書き

[私的サーバー構築日誌：仕切り直し](https://qiita.com/katz_engineer/items/1f21de2bd9072c260ae6)からの自宅サーバー構築の続きです。

## 目標

Unboundで手軽に家庭内DNSサーバーを実現します。

※DHCPサーバーよりも先にDNSサーバーを立てた方が良いです。

## 参考文献

- [自宅サーバー構築譚：LAN内DNSサーバー Unbound・Ubuntu 22.04 - Qiita](https://qiita.com/katz_engineer/items/4e29b954e0290621c1ab)
- [俺様サーバー構築記 - LAN内DNSサーバー Unbound @デスクトップパソコン(BIOS) - Qiita](https://qiita.com/katz_engineer/items/b171b84504091c4d734d)
- [自宅サーバを立てる際の家庭内名前解決の落とし穴 - Qiita](https://qiita.com/katz_engineer/items/26c4718b3c3fc0164130)
- [Set Up Unbound DNS Resolver on Ubuntu 22.04/20.04 Server](https://www.linuxbabe.com/ubuntu/set-up-unbound-dns-resolver-on-ubuntu-20-04-server)
- [第386回　Unboundでお手軽に家庭内DNSサーバーを作ろう - Ubuntu Weekly Recipe](https://gihyo.jp/admin/serial/01/ubuntu-recipe/0386)
- [2.5 内向きDNS - 2 Unboundのインストール - Unboundで内向きDNSを建てる - 純規の暇人趣味ブログ](https://jyn.jp/unbound-internal-dns/#DNS-2)
- [Unbound - ArchWiki](https://wiki.archlinux.jp/index.php/Unbound)
- [Ubuntu 18.04 の systemd-resolved で local DNS stub listener の利用をやめる - Qiita](https://qiita.com/shora_kujira16/items/31d09b373809a5a44ae5)
- [Ubuntuで指定されているDNSサーバ 127.0.0.53](https://pcvogel.sarakura.net/2022/09/06/36606)
- [ubuntu systemd-resolved で ローカルドメインの解決をする](https://takuya-1st.hatenablog.jp/entry/2022/03/10/155657)
- [unbound.conf(5) - 日本Unboundユーザー会](https://unbound.jp/unbound/unbound-conf/)
- [unbound-checkconf(8) - 日本Unboundユーザー会](https://unbound.jp/unbound/unbound-checkconf/)

既定の名前解決systemd-resolvedに関して。

- [Ubuntu 18.04 の systemd-resolved で local DNS stub listener の利用をやめる](https://qiita.com/shora_kujira16/items/31d09b373809a5a44ae5)
- [Dnsmasq で おうち DNS on Ubuntu 20.04 LTS](https://zenn.dev/jeffi7/articles/3c2ff2abd71143)

余談。こんな事もあった模様。

- [Ubuntu 20.04 その64 - Unboundにアンプ攻撃やDoSの脆弱性・アップデートを - kledgeb](https://kledgeb.blogspot.com/2020/05/ubuntu-2004-64-unbounddos.html)

家庭内ドメインとして .local はやめましょう。

- [TLDにおける名前衝突 (Name Collision)問題 - JPNIC](https://www.nic.ad.jp/ja/newsletter/No57/0800.html)

# 準備

## 家庭内ドメイン

自分ちのドメイン名、何にしますか？
一番正しいのはお金を出して買う事です。維持費も掛かります。しかし月に缶ジュース1～2本分も出せば、結構良い奴を維持できます。
最近はレンタルサーバーと同時に契約すれば割安になるサービスもあります。

ただ、家庭内限定なら .home とかでも良さそうです。外部には公開できませんが、家庭内ですし。何より無料ですしね。会社だったら .corp でしょうか。
我が家は .home に決めました。

因みに .local は、空いている保証が無いのだとか。やめた方が良さそうです。

## スナップショット

インストール前にスナップショットを撮りましょう。

```console
sudo zfs snapshot tank/root/ubuntu@$(date +%Y%m%d_%H%M%S)_before_Unbound
```

# 起動

## インストール

下記コマンドを実行。

```console
sudo apt install -y unbound
```

インストールできたら`/etc`をバージョン管理します。

```console
cd /etc
sudo svn st
```

こうなりました。

```console
$ cd /etc
$ sudo svn st
?       apparmor.d/local/usr.sbin.unbound
?       apparmor.d/usr.sbin.unbound
M       group
M       gshadow
?       init.d/unbound
?       insserv.conf.d
M       ld.so.cache
M       passwd
?       rc0.d/K01unbound
?       rc1.d/K01unbound
?       rc2.d/S01unbound
?       rc3.d/S01unbound
?       rc4.d/S01unbound
?       rc5.d/S01unbound
?       rc6.d/K01unbound
?       resolvconf
M       shadow
?       systemd/system/multi-user.target.wants/unbound.service
?       systemd/system/unbound.service.wants
?       unbound
```

相変わらずな感じです。
- `apparmor`はウイルス対策ソフトの一種と思われます。
- `ld.so.cache`はライブラリの場所のキャッシュ。
- アカウントとグループの追加（`group`と`gshadow`と`passwd`と`shadow`）
- init.d関連とsystemd関連。
    - `insserv.conf.d`も、よくわかりませんが、init.d関連のようです。
      中身を調べると`/etc/insserv.conf.d/unbound`というファイルなので、やはりUnboundにまつわる設定のようです。
- `unbound`は設定ファイルのディレクトリと思われます。
- `resolvconf`を調べるとディレクトリであり、`/etc/resolvconf/update.d/unbound`というシェルスクリプトがありました。内容は理解できませんでしたが、Unboundにまつわる何らかのコマンドと思われます。

取り敢えず全部追加して更新。

```console
sudo svn st | grep "^?" | cut -b9- | sudo xargs -I{} find {} -type f -or -type d -or -type l | sudo xargs -rt svn add
```

ちょっと警告が出ます(already under version control)が、こいつは無視して下さい。心配なら`sudo svn st`で全部追加されてる事を確認すればOKです。

それからcommitします。

```console
sudo svn ci -m"installed Unbound"
```

## systemd-resolved の設定を変更

こいつはDNSの名前解決をするsystemdサービスです。 IP アドレス`127.0.0.53`のポート`53`で待ち受けています。言いたい事は山程ありますが、それは置いときましょう。[以前](https://qiita.com/katz_engineer/items/241c3667cae2e742d54f)は放置で問題無かったと思うのですが、今回は放置してはダメでした。対策しないとUnboundが起動しません。

systemd-resolvedを止める方法もあります。ただ、止めなくても何とかなるようですな。

今回は下記のように設定ファイルを修正しときます。

- DNSサーバーとして`172.16.1.2`（現在いじっているサーバーマシン）を指定する
- 家庭内ドメイン .home を自動で補う

```console
IPADDR=172.16.1.2
NETWORK=172.16.0.0/16
```

```console
sudo sed -i -e"/^#DNS=/c DNS=$IPADDR" -e"/^#Domains=/c Domains=home" /etc/systemd/resolved.conf
```

```console
sudo systemctl restart systemd-resolved
```

## Unbound 設定ファイル

参考文献や上述の`/etc`に追加されたディレクトリなどから判断して、主な設定ファイルは`/etc/unbound/unbound.conf`です。この中身は`include: "/etc/unbound/unbound.conf.d/*.conf"`というだけの恐ろしく単純な1行、及び英語のコメント。従ってオレオレ設定は`/etc/unbound/unbound.conf.d`に適当なファイル名で置けば良いのでしょう。

今回は下記のように修正しました。

待ち受けるIPアドレスは、外部からの問い合わせも全部受けるなら`0.0.0.0`を指定するのが一般的です。が、これを指定するとsystemd-resolvedと衝突して起動に失敗します。そこで`172.16.1.2`（このマシンのIPアドレス）を指定する事にします。

もしIPアドレスを複数割り当てている場合には、その分だけ`interface`行を増やせば良いようです。

```console
cd /etc/unbound/unbound.conf.d/
cat | sudo tee home.conf >/dev/null <<___
server:
  username: unbound
  verbosity: 1
  interface: $IPADDR
  access-control: $NETWORK allow
  root-hints: "/etc/unbound/unbound.conf.d/parent.hints"
  include: "/etc/unbound/unbound.conf.d/machines.list"
  hide-identity: yes
  hide-version: yes
remote-control:
  control-enable: no
___
```

```console
sudo touch machines.list
```

ルートヒントは、世界的に使用されているグローバル標準 https://www.internic.net/domain/named.cache がある訳ですが。家庭内LANなのでプロバイダが指定してくる物を上位DNSとすれば十分かなと。

```console
PARENTHINT=プロバイダ指定のDNSのIPアドレス
```

```console
cat | sudo tee parent.hints >/dev/null <<___
.            3600000 NS ROUTER.HOME.
ROUTER.HOME. 3600000 A  $PARENTHINT
___
```

Unbound の設定ファイルを修正したら、文法チェックのツールを実行しておきましょう。

```console
unbound-checkconf
```

## 起動

unbound の設定変更時には、`systemctl reload`では駄目で`systemctl restart`が必要のようです。権限がどうとか。

```console
sudo systemctl enable unbound
```

```console
sudo systemctl restart unbound
```

起動を確認。

```console
systemctl status unbound
```

外の名前解決を確認。

```console
nslookup example.com
```

## Subversion 登録

一応大丈夫そうなので`/etc`をコミットしましょう。

```console
cd /etc
sudo svn st | grep "^?" | cut -b9- | sudo xargs -rt svn add
```

```console
sudo svn ci -m"Unbound initial settings"
```

# 内向きDNS

これです。この為に、家庭内名前解決とか面倒臭い事をするんです。

## 設定

マシン名は`/etc/unbound/unbound.conf.d/machines.list`に追加します。正引きと逆引きの両方が必要です。後は好きに設定して下さい。

```console
cd /etc/unbound/unbound.conf.d/
cat | sudo tee -a machines.list >/dev/null <<___
local-data: "primary.home. 3600000 IN A $IPADDR"
local-data-ptr: "$IPADDR 3600000 primary.home."
___
```

文法チェック。

```console
unbound-checkconf
```

## 確認

設定ファイルを読み込みます。

```console
sudo systemctl restart unbound
```

ちょっと確認。別のマシンから`nslookup`コマンドを実行してみます。

```console:別マシン
nslookup primary.home 172.16.1.2
```

外部のドメイン名（example.com等）も、引き続き名前解決できる事を確認します。

```console:別マシン
nslookup example.com 172.16.1.2
```

## 設定保存

```console
cd /etc
sudo svn ci -m"DNS setting: primary"
```

## 設定：無線 LAN 親機

通常の家庭の場合、無線LANの親機がルータやDHCP等の設定も持っているでしょう。この内DNSを、[我が家で言えば](https://qiita.com/katz_engineer/items/a5cc92d7ca523ee3be14#nic-%E3%81%AE%E5%86%8D%E8%A8%AD%E5%AE%9A)`172.16.1.2`に向けるように設定します。

1. DHCPサーバから通知するDNSサーバアドレスを設定
2. DNSルーティング設定とかいう機能で、家庭内で解決する名前のドメイン毎に、Unboundを立てたサーバのIPアドレスを設定

これは機種によって異なります。それぞれのマニュアルを確認して下さい。

ウチは最近になって無線LANルーターを買い替えました。とある有名なゲーミングルーターが安売りしてたので飛びついたのですが、ちょっと失敗だったかも知れません。良いと思っていた機能が無くなってしまったので。その内の一つが、DHCPの設定としてDNSサーバーアドレスを指定する機能だったのでした。

仕方が無いのでクライアントマシン側で設定します。が、Windowsだとどうも上手くいきませんな。けど、ここに拘ってるといつまでも進みません。諦めました。

# 起動失敗への対応

私のマシンでは、マシン起動した際に、Unboundは起動に失敗します。

```console
$ systemctl status unbound
× unbound.service - Unbound DNS server
     Loaded: loaded (/lib/systemd/system/unbound.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Sat 2023-08-26 19:58:08 JST; 3min 7s ago
       Docs: man:unbound(8)
    Process: 2072 ExecStartPre=/usr/lib/unbound/package-helper chroot_setup (code=exited, status=0/SUCCESS)
    Process: 2075 ExecStartPre=/usr/lib/unbound/package-helper root_trust_anchor_update (code=exited, status=0/SUCCESS)
    Process: 2078 ExecStart=/usr/sbin/unbound -d -p $DAEMON_OPTS (code=exited, status=1/FAILURE)
    Process: 2079 ExecStopPost=/usr/lib/unbound/package-helper chroot_teardown (code=exited, status=0/SUCCESS)
   Main PID: 2078 (code=exited, status=1/FAILURE)
        CPU: 10ms

Aug 26 19:58:08 chihiro systemd[1]: unbound.service: Scheduled restart job, restart counter is at 5.
Aug 26 19:58:08 chihiro systemd[1]: Stopped Unbound DNS server.
Aug 26 19:58:08 chihiro systemd[1]: unbound.service: Start request repeated too quickly.
Aug 26 19:58:08 chihiro systemd[1]: unbound.service: Failed with result 'exit-code'.
Aug 26 19:58:08 chihiro systemd[1]: Failed to start Unbound DNS server.
```

`/var/log/syslog`を良く見ると、待ち受けソケットのバインドに失敗するようです。

```console
unbound[2078]: [1693047487] unbound[2078:0] error: can't bind socket: Cannot assign requested address for 172.16.1.2 port 53
```

問題はこの後。失敗した直後に再起動を試みてしまいます。その為、デフォルトで設定されている規定の回数を超えてしまい、Unboundの起動を諦めてしまうようですな。

そこでsystemdのドロップインファイルとして、再起動まで10秒待つ設定を入れます。待ち時間の「10秒」に根拠はありません。もっと短くても良いでしょう。またマシンによっても異なると思われます。

```console
sudo mkdir /etc/systemd/system/unbound.service.d/
```

```console
sudo cat <<___ | sudo tee /etc/systemd/system/unbound.service.d/unbound.conf
[Service]
RestartSec=10
___
```

```console
sudo systemctl daemon-reload
```

ドロップインファイル`/etc/systemd/system/unbound.service.d/unbound.conf`が利いている事を確認。

```console
systemctl cat unbound
```

結果はこう表示されます。

```console
$ systemctl cat unbound
# /lib/systemd/system/unbound.service
[Unit]
Description=Unbound DNS server
Documentation=man:unbound(8)
After=network.target
Before=nss-lookup.target
Wants=nss-lookup.target

[Service]
Type=notify
Restart=on-failure
EnvironmentFile=-/etc/default/unbound
ExecStartPre=-/usr/lib/unbound/package-helper chroot_setup
ExecStartPre=-/usr/lib/unbound/package-helper root_trust_anchor_update
ExecStart=/usr/sbin/unbound -d -p $DAEMON_OPTS
ExecStopPost=-/usr/lib/unbound/package-helper chroot_teardown
ExecReload=+/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/unbound.service.d/unbound.conf
[Service]
RestartSec=10
```

マシンを再起動して、一発でUnboundの起動に成功している事を確認します。

```console
sudo reboot
```

```console
systemctl status unbound
```

うまくいっていたらSubversionコミットしましょう。まずは状況確認。

```console
sudo svn st /etc
```

今回は、他に色々と操作を試みていたせいか、よくわからないファイルにも差分が出たようです。

```console
$ sudo svn st /etc
?       /etc/systemd/system/multi-user.target.wants/snap-snapd-19993.mount
?       /etc/systemd/system/snap-snapd-19993.mount
?       /etc/systemd/system/snapd.mounts.target.wants/snap-snapd-19993.mount
?       /etc/systemd/system/unbound.service.d
M       /etc/zfs/zfs-list.cache/tank
M       /etc/zfs/zpool.cache
```

まとめてコミットしておきます。

```console
sudo svn st /etc | grep "^?" | cut -b9- | sudo xargs -I{} find {} -type f -or -type d -or -type l | sudo xargs -rt svn add
```

```console
sudo svn ci -m"add a drop-in file of unbound; which has 'RestartSec=10'" /etc
```

# 仕舞い

これで、家庭内の名前解決が出来るようになりました。