俺様サーバー構築記 - LAN内DNSサーバー Unbound @デスクトップパソコン(BIOS)

「[俺様サーバー構築記 - 基本方針](https://qiita.com/katz_engineer/items/dbabcff4e14e9db2b10f)」に基づく作業の続き。[LTS版Linuxカーネルの使用を決心](https://qiita.com/katz_engineer/items/70e6a75581c60426c836)してから、私の手持ちで生きているマシンをすべてフォーマットし直しました。

その中で、[家庭内サーバ用の古いデスクトップマシン](https://qiita.com/katz_engineer/items/5f02b4ca2d989bb5e862)に対し、家庭内DNSサーバを構築します。

数年前に、[Bindを使って似たような事をやった](https://qiita.com/katz_engineer/items/26c4718b3c3fc0164130)んですが。私なりにちょっと勉強しまして、今回は軽量で簡単と名高いUnboundを使ってみます。

参考文献1: [自宅サーバを立てる際の家庭内名前解決の落とし穴](https://qiita.com/katz_engineer/items/26c4718b3c3fc0164130)
参考文献2: [Unbound，知ってる？ この先10年を見据えたDNS 第2回 Unboundの導入（新規導入編）](https://gihyo.jp/admin/feature/01/unbound/0002)

# 準備

事前にアップデート。

```console
# zfs snapshot tank/main@$(date +%Y%m%d_%H%M%S)_before_update
# zfs list -t snapshot -S name | head -n2
NAME                                      USED   AVAIL     REFER  MOUNTPOINT
tank/main@20200813_164238_before_update   111K       -     1.78G  -
# pacman -Syu --noprogessbar --noconfirm
```

アップデートが完了したら、インストール前にスナップショットを撮りましょう。

```console
# zfs snapshot tank/main@$(date +%Y%m%d_%H%M%S)_before_Unbound
# zfs list -t snapshot -S name | head -n2
NAME                                       USED   AVAIL     REFER  MOUNTPOINT
tank/main@20200813_165104_before_Unbound     0B       -     2.02G  -
```

# 起動

## インストール

参考文献: [Unbound - ArchWiki](https://wiki.archlinux.jp/index.php/Unbound)

今回は、DNSSECはしません。本当はやっといた方がいいんでしょうけども。

```console
# pacman -S unbound --noprogressbar --noconfirm
```

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

```console
# cd /etc
# svn st
M       group
M       gshadow
M       ld.so.cache
M       passwd
M       shadow
?       trusted-key.key
?       unbound
# svn add trusted-key.key unbound
A         trusted-key.key
A         unbound
A         unbound/trusted-key.key
A         unbound/unbound.conf
# svn ci -m"installed Unbound"
```

## drill コマンド

ちなみに、unboundをインストールすると、```drill```というコマンドも同時にインストールされます。

```console
# drill -v | head -n2
drill version 1.7.1 (ldns version 1.7.1)
Written by NLnet Labs.
```

こいつはDNSの名前解決をするコマンドだそうです。キャッシュDNSサーバのIPアドレスを指定しなければならないのですが、[OSインストール時のネットワーク設定](https://qiita.com/katz_engineer/items/5f02b4ca2d989bb5e862#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)の設定により、systemd-resolvedが127.0.0.53で待ち受けています。127.0.0.1じゃないんですよね。どうしてこんな変な設定になっているのか理解に苦しみます。正確にはlocalhostは127.0.0.0/8なので、RFC的には正しいんですけども… systemdのこういう所が私は嫌（以下略）

参考文献: [2.1 DNS - 2 設定 - systemd-resolved - ArchWiki](https://wiki.archlinux.jp/index.php/Systemd-resolved#DNS)

```console
# grep nameserver /run/systemd/resolve/stub-resolv.conf
nameserver 127.0.0.53
# drill archlinux.jp @127.0.0.53
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 18649
;; flags: qr rd ra ; QYERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; archlinux.jp.        IN      A

;; ANSWER SECTION:
archlinux.jp.   2559    IN      A       160.16.119.98

;; AUTHORITY SECTION:

;; Query time: 0 msec
;; SERVER: 127.0.0.53
;; WHEN: Fri Aug 14 14:51:28 2020
;; MSG SIZE  rcvd: 46
```

## 設定

### systemd-resolved

上述の通り、[OSインストール時のネットワーク設定](https://qiita.com/katz_engineer/items/5f02b4ca2d989bb5e862#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)の設定によって、現在は```systemd-resolved```が名前解決してます。まずはこれを修正します。

参考文献: [systemd-resolved - ArchWiki](https://wiki.archlinux.jp/index.php/Systemd-resolved)

DNSサーバの設定と共に、ついでにDomainsもhomeに設定しときましょう。corpでも何でも構いませんが、一応localは避けておきたいですね。っていうかlocalじゃなくてhomeも望ましくはないですかね？

LAN内だけで解決するドメイン名であって非公開ならhomeでいいんじゃないかと思ってますが、正式には、きちんと自分ドメインを買うのが正しいですな。特に会社その他公的組織の場合は予算を組みましょう。個人だと財布が気になりますが（個人的には月に缶ジュース1～2本分くらい払えよって考えてますが）、会社なら気にしたら負けって位の安値です。

参考文献: [Mac が参加するADのドメイン名で .local を使わないでください。](https://www.picturecode.co.jp/faq/dot-local-domain/)
参考文献: [TLDにおける名前衝突 (Name Collision)問題](https://www.nic.ad.jp/ja/newsletter/No57/0800.html)

```console
# sed -i -e"/^#DNS=/c DNS=127.0.0.1" -e"/^#Domains=/c Domains=home" /etc/systemd/resolved.conf
# sed -i -e"/^DNS=/c DNS=127.0.0.1" /etc/systemd/network/*.network
```

書き換えの確認：

```console
# svn up /etc
Updating '.':
At revision 5.
# svn di --diff-cmd diff -x -U0 /etc
Index: /etc/systemd/network/enp0s25.network
==================================================================
--- /etc/systemd/network/enp0s25.network        (revision 5)
--- /etc/systemd/network/enp0s25.network        (working copy)
@@ -7 +7 @@
-DNS=172.16.0.1
+DNS=127.0.0.1
Index: /etc/systemd/network/enp2s1.network
==================================================================
--- /etc/systemd/network/enp2s1.network (revision 5)
+++ /etc/systemd/network/enp2s1.network (working copy)
@@ -7 +7 @@
-DNS=172.16.0.1
+DNS=127.0.0.1
Index: /etc/systemd/resolved.conf
==================================================================
--- /etc/systemd/resolved.conf  (revision 5)
+++ /etc/systemd/resolved.conf  (working copy)
@@ -15 +15 @@
-#DNS=
+DNS=127.0.0.1
@@ -17 +17 @@
-#Domains=
+Domains=home
```

### Unbound

ArchWikiには下記のように書いてあるんですがね…

> サンプル設定ファイルが /etc/unbound/unbound.conf.example に存在します。

ありませんね～サンプルファイル。

```console
# cd /etc/unbound
# ls
trusted-key.key  unbound.conf
```

という訳で、デフォルト設定もちょっと心配なので、念の為中身を見ておきます。

```console
# grep -vE "#|^$" unbound.conf
server:
        verbosity: 1
        trust-anchor-file: /etc/unbound/trusted-key.key
python:
dynlib:
remote-control:
```

ほとんど何も書かれていません。Unboundはデフォルト設定そのままでも実用に耐えるらしいので、OKと考えましょう。

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

待ち受けるIPアドレスは、外部からの問い合わせも全部受けるなら```0.0.0.0```を指定するのが一般的だと思うのですが。```systemd-resolved```と衝突して色々面倒です… このサーバマシンのIPアドレスは```172.16.0.3```で固定にしたので、それを指定します。

ルートヒントは、世界的に使用されているグローバル標準 https://www.internic.net/domain/named.cache がある訳ですが。家庭内LANなので、プロバイダが指定してくる物を上位DNSとすれば十分かなと。という訳で、ルートヒントは家庭内LANのルータ```172.16.0.1```を指定するだけのファイルを用意しました。定期的な更新なんかも不要ですしね。

```console
# cd /etc/unbound
# vi unbound.cond
〈編集内容は下記を参照〉
# grep -vE "#|^$" unbound.conf
server:
        verbosity: 1
        interface: 172.16.0.3
        interface: 127.0.0.1
        access-control: 172.16.0.0/16 allow
        root-hints: "/etc/unbound/parent.hints"
        trust-anchor-file: /etc/unbound/trusted-key.key
python:
dynlib:
remote-control:
# cat >parent.hints <<___
> .            3600000 NS ROUTER.HOME.
> ROUTER.HOME. 3600000 A  172.16.0.1
> ___
```

## 起動

```console
# systemctl enable unbound
Created symlink /etc/systemd/system/multi-user.target.wants/unbound.service -> /usr/lib/systemd/system/unbound.service.
# systemctl start unbound
# systemctl restart systemd-networkd
# systemctl resatrt systemd-resolved
```

完了後は```drill```コマンドで確認します。最初の、DNSサーバとして```127.0.0.53```を指定した場合の出力と比較します。

```console
# for s in 127.0.0.1 172.16.0.3; do diff -U0 <(drill archlinux.jp @127.0.0.53) <(drill archlinux.jp @$s); done
--- /dev/fd/63  2020-08-14 15:32:29.731482151 +0900
+++ /dev/fd/62  2020-08-14 15:32:29.731482151 +0900
@@ -1 +1 @@
-;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 22290
+;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 48461
@@ -14 +14 @@
-;; SERVER: 127.0.0.53
+;; SERVER: 127.0.0.1
--- /dev/fd/63  2020-08-14 15:32:29.741482151 +0900
+++ /dev/fd/62  2020-08-14 15:32:29.741482151 +0900
@@ -1 +1 @@
-;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 357
+;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 33999
@@ -14 +14 @@
-;; SERVER: 127.0.0.53
+;; SERVER: 172.16.0.3
```

差分が予想の範囲内なのでOKですね。

## 後処理

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

```console
# cd /etc
# svn up
Updating '.':
At revision 5.
# svn st | grep "^?" | cut -b9- | xargs -rt svn add
A         systemd/system/multi-user.target.wants/unbound.service
A         unbound/dev
A         unbound/parent.hints
A         unbound/run
# svn ci -m"Unbound initial settings"
Sending        systemd/network/enp0s25.network
Sending        systemd/network/enp2s1.network
Sending        systemd/resolved.conf
Sending        systemd/system/multi-user.target.wants/unbound.service
Adding         unbound/dev
Adding         unbound/parent.hints
Adding         unbound/run
Sending        unbound/unbound.conf
Transmitting file data ....done
Committing transaction...
Committed revision 6.
```

# 内向きDNS

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

## 設定

参考文献: [2.5 内向きDNS - 2 Unboundのインストール - Unboundで内向きDNSを建てる - 純規の暇人趣味ブログ](https://jyn.jp/unbound-internal-dns/#DNS-2)

```console
# cd /etc/unbound
# cat >server.conf <<___
> local-data: "taro.home.   3600000 IN A 172.16.0.2"
> local-data: "hanako.home. 3600000 IN A 172.16.0.3"
> local-data-ptr: "172.16.0.2 3600000 taro.home."
> local-data-ptr: "172.16.0.3 3600000 hanako.home."
> ___
# vi unbound.conf
〈編集内容は下記差分を参照〉
# svn di --diff-cmd diff -x -U0
Index: unbound.conf
=============================================================
--- unbound.conf        (revision 6)
+++ unbound.conf        (working copy)
@@ -858,0 +859 @@
+       include: "/etc/unbound/server.conf"
```

## 実行

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

```console
# systemctl reload unbound
```

ちょっと確認。

```console
# drill hanako.home @127.0.0.1
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 38567
;; flags: qr aa rd ra ; QYERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; hanako.home. IN      A

;; ANSWER SECTION:
hanako.home.    3600000 IN      A       172.16.0.3

;; AUTHORITY SECTION:

;; Query time: 0 msec
;; SERVER: 127.0.0.1
;; WHEN: Fri Aug 14 16:53:22 2020
;; MSG SIZE  rcvd: 52
```

外部のドメイン名 archlinux.jp も、引き続ききちんと名前解決できる事を確認します。

```console
# drill archlinux.jp @127.0.0.1
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 63596
;; flags: qr rd ra ; QYERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; archlinux.jp.        IN      A

;; ANSWER SECTION:
archlinux.jp.   2559    IN      A       160.16.119.98

;; AUTHORITY SECTION:

;; Query time: 0 msec
;; SERVER: 127.0.0.1
;; WHEN: Fri Aug 14 16:20:36 2020
;; MSG SIZE  rcvd: 46
```

## 家中の設定

DHCPサーバを立てた場合にはそちらを設定します。まずは無線LANの親機ですな。これは機種毎に異なりますが、多くの場合は下記2点かと。

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

また、他にも固定IPアドレスを設定したサーバマシンがあれば、そこのネットワーク設定も指定しておきます。

で、クライアントマシンから```nslookup```や```ping```などのコマンドを実行。うまくいけばOKです。

```console:適当なWindowsマシンなどのクライアントマシン
> ping hanako

hanako.home [172.16.0.3]に ping を送信しています 32 バイトのデータ:
172.16.0.3 からの応答: バイト数 = 32 時間 <1ms TTL=64
172.16.0.3 からの応答: バイト数 = 32 時間 <1ms TTL=64
172.16.0.3 からの応答: バイト数 = 32 時間 <1ms TTL=64
172.16.0.3 からの応答: バイト数 = 32 時間 <1ms TTL=64

172.16.0.3 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 0ms、最大 = 0ms、平均 = 0ms
```

なお私のOS設定手順だと```/etc/resolv.conf```を無視している為に、```drill```コマンド実行時にはDNSサーバを指定しないとうまく動きません。対応方法は下記の3通りになります。

- 上述の通り、```drill```コマンド実行時にDNSサーバを明示的に指定
- ```drill```コマンド実行時に```/run/systemd/resolve/resolv.conf```を指定
- ```/etc/resolv.conf```を、```/run/systemd/resolve/resolv.conf```へのシンボリックリンクとする

本当はこの3番目の方法、```/etc/resolv.conf```をシンボリックリンクにしてしまうのが正しいのでしょう。

2番目の方法の場合は、下記のような指定になります。

```console
# drill hanako.home -c /run/systemd/resolve/resolv.conf
```

実行結果は省略。

いずれにしてもこれで設定完了となります。やったね:thumbsup_tone1: