OpenWRTでbindとDnsmasqを共存させる

前回([OpenVPNでキャリアNAT超えで自宅LANに接続できるようにする](https://qiita.com/daredeshow/items/b85e029169ec35add387))で、自宅をクラウドの間をVPN回線で接続した。その際の名前解決について少し工夫したので記述する。

# なにをしたいか
![191120-OpenWRT-bind01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52237/9783c4c8-a6e5-1c19-4b83-470332bf6e37.jpeg)
上記のように、自宅、クラウド、普通のインターネットと接続されている状況。ここで3種類の名前解決を取り扱うことになる。

1. ケース(1) 自宅内のホスト名解決
    - 自宅の中のホストには、OpenWRTに標準で入っているDnsmasqのDHCPサーバでIPアドレスが割り振られる。その際にそのホストが持っているホスト名が自動的にIPアドレスと紐づけられ、名前解決できるようになる。例えばもともと `mail` というホスト名を持っているホストがDHCPでIPアドレス `192.168.20.3` を取得すると、同時にDnsmasqの中では設定されているドメイン名例えば `myhome` と組み合わせて、 `mail.myhome` が `192.168.20.3` で正引きすることができるようになる。これはDnsmasq内蔵のDNSの動作になる。
    - つまりDnsmasqにおけるDHCPとDNSの動作は不可分であるので、できるだけこの動作は改変したくない
1. ケース(2) クラウド上のホスト名解決
    - AWSのインスタンスは、同じホスト名でもどのDNSサーバに名前解決リクエストを送ったかによって返却するIPアドレスがかわる。例えばホスト名 `ec2-11-245-123-234.ap-northeast-1.compute.amazonaws.com` をクラウド内部のDNSで名前解決すると `172.30.1.100` という内部IPアドレスを返却するが、クラウド外部のDNSサーバで名前解決すると `11.245.123.234` というようなグローバルIPアドレスを返却する(もしインスタンスがグローバルIPアドレスを持つように設定されている場合)。
    - 今後グローバルIPアドレスを持たないインスタンスをクラウド内部に作った場合でも、VPN経由で名前解決しアクセスしたい
1. ケース(3) ケース(1), (2)以外のホスト名解決
    - これは上位のネームサーバ(OpenDNSやGooglePublicDNSなど)をセットすれば解決できる

つまり名前解決したい対象のドメインによって問い合わせ先のDNSサーバを変更したい。

# なんでbindが必要になるか

もともと上記の課題はDnsmasq単体で解決できるはずだった。設定ファイルに以下のように記述すればいい、はずなのだがなぜか上手く動いてくれない。

```shell:/etc/config/dhcp
config dnsmasq
	option domain 'myhome'
	option localservice '0'
	option local '/myhome/'
	list server '/myhome/192.168.20.254'
	list server '/amazonaws.com/172.30.0.2'
	list server '208.67.222.222'
	list server '208.67.220.220'
(後略)
```

そこでケース(1)の名前解決のみDnsmasqにやってもらうことにして、クライアントからの問い合わせ受け付けと、ドメイン名に応じた振り分けはbindにやってもらい、Dnsmasqには必要に応じてbind側からリクエストをforwardする形にした。

# 設定手順

以下実際の設定手順になる。環境は OpenWRT 18.06.4 。

まずbindパッケージをインストールする

```shell
# opkg install bind-server
# opkg install bind-tools
```

DnsmasqのDNSサーバの待ち受けポート番号をWell Knownの番号以外にする。ここでは15353/udpとした。

```shell:/etc/dnsmasq.conf(設定ファイル中に追加)
port=15353
```

Dnsmasqを再起動

```shell
# /etc/init.d/dnsmasq stop
# /etc/init.d/dnsmasq start
```

/etc/bind/named.confを編集する

```shell:/etc/bind/named.conf
// This is the primary configuration file for the BIND DNS server named.

options {
	// 待ち受けアドレスとポート番号を指定
	listen-on port 53 { 127.0.0.1; 192.168.20.254; };
	listen-on-v6 port 53 { ::1; fd6a:6a60:4c5a::1; };
	directory "/tmp";

	// If your ISP provided one or more IP addresses for stable 
	// nameservers, you probably want to use them as forwarders.  
	// Uncomment the following block, and insert the addresses replacing 
	// the all-0s placeholder.

	// forwarders {
	// 	0.0.0.0;
	// };
	// ケース(3)に対応するforward先のDNSサーバを指定
	forwarders	{ 208.67.222.222; 208.67.220.220; 2620:119:35::35; 2620:119:53::53; };
	// DnsmasqはDnsSecに対応していないので、DnsSecは無効にする
	dnssec-enable no;
	dnssec-validation no;

	auth-nxdomain no;    # conform to RFC1035
};

// ケース(2)に対応するforward先のDNSサーバを指定
zone "amazonaws.com" IN {
	type forward;
	forward only;
	forwarders { 172.30.0.2; };
};

// ケース(2)に対応するforward先のDNSサーバを指定
zone "compute.internal" IN {
	type forward;
	forward only;
	forwarders { 172.30.0.2; };
};

// ケース(1)に対応するforward先のDNSサーバ(Dnsmasq)を指定
zone "myhome" IN {
	type forward;
	forward only;
	forwarders { 192.168.20.254 port 15353; fd6a:6a60:4c5a::1 port 15353; };
};

(以降は初期値のままなので省略)
```

bindを起動

```shell
# /etc/init.d/named stop
# /etc/init.d/named start
```

# 動作確認

クライアントからケースに応じて正しく名前解決できているか確認

```shell
$ nslookup
# サーバがOpenWRT内のDNSをさしていることを確認
> server
Default server: fd6a:6a60:4c5a::1
Address: fd6a:6a60:4c5a::1#53
# ケース(1) 自宅内ホストの名前解決
> gw.myhome
Server:		fd6a:6a60:4c5a::1
Address:	fd6a:6a60:4c5a::1#53

Non-authoritative answer:
Name:	gw.myhome
Address: 192.168.20.254
Name:	gw.myhome
Address: fd6a:6a60:4c5a::1
# ケース(2) クラウド上のホストの名前解決
> ec2-13-113-223-169.ap-northeast-1.compute.amazonaws.com
Server:		fd6a:6a60:4c5a::1
Address:	fd6a:6a60:4c5a::1#53

Non-authoritative answer:
Name:	ec2-13-113-223-169.ap-northeast-1.compute.amazonaws.com
Address: 172.30.1.64
# ケース(3) ケース(1), (2)以外のホスト名解決
> www.microsoft.com
Server:		fd6a:6a60:4c5a::1
Address:	fd6a:6a60:4c5a::1#53

Non-authoritative answer:
www.microsoft.com	canonical name = www.microsoft.com-c-3.edgekey.net.
www.microsoft.com-c-3.edgekey.net	canonical name = www.microsoft.com-c-3.edgekey.net.globalredir.akadns.net.
www.microsoft.com-c-3.edgekey.net.globalredir.akadns.net	canonical name = e13678.dspb.akamaiedge.net.
Name:	e13678.dspb.akamaiedge.net
Address: 23.35.196.245
Name:	e13678.dspb.akamaiedge.net
Address: 2600:140b:8800:18d::356e
Name:	e13678.dspb.akamaiedge.net
Address: 2600:140b:8800:19c::356e
> 
```

# おわりに

bindの素直な動作がいい感じですね！