ローカルネットワーク用にdnsmasqでDNSサーバーを構築する

Posted by 雅楽斎 on Tuesday, January 19, 2021

TOC

今回の作業の概要

作業環境はUbuntu 20.10です。

設定項目設定値
管理ドメイン.internal
ブロードバンドルーターのIPアドレス
兼 インターネット向けのDNSサーバー
192.168.1.1
LAN向けDNSサーバーのIPv4アドレス192.168.1.210
ホストrpi4のドメインrpi4.internal
ホストrpi4のIPv4アドレス192.168.1.210
ホストrpi4のIPv6アドレスfd00:dead:beef::210

使用するLAN内向けドメインについて

管理ドメインについてはTLDとして国を表すccTLDの他にjcbやintel、komatsu、konami、mcdonaldsといった企業が持つことを前提としたgTLDが使われるようになっているため、安易な命名をしてしまうと将来的に重複する可能性があります。無用なトラブルに突っ込むのは避けたいところです。

最近、(Android以外の)ほぼ全てのアクティブなOSで使用できる段階まで普及しているmDNSでは.localが使われるようになっています。同じように.localを使うとうまく動かなかった場合に切り分けが難しいので避けます。

特定用途向けのドメインとして定義されているのは以下のものになります。現時点ではlocalやlocalhost、test、onion等が定義されています。

Special-Use Domain Names

localはmDNSで使われるので避けるとして、今現在もある程度LAN内向けとして使われているドメインの中でインターネットドラフトとして公開されているinternalを今回は使います。1

draft-wkumari-dnsop-internal-00 - The .internal TLD.

Ubuntuでのdnsmasq運用時の注意

従来の一般的なLinuxディストロでのDNSサーバーの構築と異なり、以下2つの要素について注意が必要です。

  • systemd-resolved
  • Netplan

systemd-resolved

systemd-resolvedはローカルDNSスタブリスナによるネットワーク名前解決をローカルアプリケーションに提供するsystemdサービスだそうです。字で書いてもよくわかりませんが、早い話UDPのポート53をbindするので、DNSサーバーを構築しようとするとポートをbindできないので注意が必要です。

Netplan

NetplanはCanonicalが開発したネットワーク設定の仕組みで、固定IPアドレス、DHCP使用等を記述して設定する、現在のUbuntuではデフォルトで採用されています。

Netplanは設定をするとsystemd用のファイルを生成するので、生成ファイルを元にしてsystemd-resolvedが動作することから最終的にsystemd-resolvedの設定に関連します。

dnsmasqのインストール

# apt-get install dnsmasq
(snip)
.../dnsmasq_2.82-1ubuntu1.1_all.deb を展開する準備をしています ...
dnsmasq (2.82-1ubuntu1.1) を展開しています...
dnsmasq (2.82-1ubuntu1.1) を設定しています ...
Created symlink /etc/systemd/system/multi-user.target.wants/dnsmasq.service → /lib/systemd/system/dnsmasq.service.
Job for dnsmasq.service failed because the control process exited with error code.
See "systemctl status dnsmasq.service" and "journalctl -xe" for details.
invoke-rc.d: initscript dnsmasq, action "start" failed.
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Wed 2021-01-19 20:54:50 JST; 20ms ago
    Process: 209143 ExecStartPre=/etc/init.d/dnsmasq checkconfig (code=exited, status=0/SUCCESS)
    Process: 209151 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=2)

 1月 19 20:54:50 rpi4 systemd[1]: Starting dnsmasq - A lightweight DHCP and caching DNS server...
 1月 19 20:54:50 rpi4 dnsmasq[209151]: dnsmasq: failed to create listening socket for port 53: アドレスは既に使用中です
 1月 19 20:54:50 rpi4 dnsmasq[209151]: failed to create listening socket for port 53: アドレスは既に使用中です
 1月 19 20:54:50 rpi4 dnsmasq[209151]: FAILED to start up
 1月 19 20:54:50 rpi4 systemd[1]: dnsmasq.service: Control process exited, code=exited, status=2/INVALIDARGUMENT
 1月 19 20:54:50 rpi4 systemd[1]: dnsmasq.service: Failed with result 'exit-code'.
 1月 19 20:54:50 rpi4 systemd[1]: Failed to start dnsmasq - A lightweight DHCP and caching DNS server.
systemd (246.6-1ubuntu1) のトリガを処理しています ...

という訳で、インストール後に自動的に起動しようとして失敗します。根本原因はポート53をsystemd-resolvedが掴んでいるからとのこと。

# netstat -anp | grep ":53"
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      224983/systemd-reso 
udp        0      0 127.0.0.53:53           0.0.0.0:*                           224983/systemd-reso 
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           1750/avahi-daemon:  
udp6       0      0 :::5353                 :::*                                1750/avahi-daemon:

dnsmasqの設定

systemd-resolvedを弄らずにdnsmasqを動かすための設定として、こちらを参考にしました。

dnsmasq と systemd-resolved(Ubuntu 12.04 LTS) [ほうほう爺の独り言]

また、恐らくsystemd-resolvedとの設定の兼ね合いが日本語で一番書かれているのはこちらのサイトだと思います。

Ubuntu : systemd-resolved、及び Resolvconf 設定 - eTuts+ Server Tutorial

/etc/hosts

従前の/etc/hostsはDNSによる名前解決をさせないホストを記載するファイルですが、dnsmasqではデフォルトで2このファイルをそのままDNSサーバーの名前解決の定義として使用するため、結果としては逆に名前解決したいIPアドレスを記載します。

--- hosts.org	2020-10-20 23:07:18.000000000 +0900
+++ hosts	2021-01-19 21:17:41.279844060 +0900
@@ -7,3 +7,6 @@
 ff02::1 ip6-allnodes
 ff02::2 ip6-allrouters
 ff02::3 ip6-allhosts
+
+192.168.1.210 rpi4 rpi4.internal
+fd00:dead:beef::210 rpi4 rpi4.internal

下の2行について、FQDNを後ろに書いているのはこのサーバーはDHCPクライアントを動かしておらず、DNSサーバーとして指定しているのがルーターのIPアドレスなので、この端末上で名前解決をさせる場合はdnsmasqを経由せずに名前解決させる為です。

/etc/dnsmasq.conf

設定項目は参考サイトと一緒にしています(cache-sizeは適切な値が検討つかなかったのでアンコメントのみしています)→下記では150としていますが、単位が件数らしいのでとりあえず3000にしました。

--- dnsmasq.conf.org	2021-01-08 23:50:21.000000000 +0900
+++ dnsmasq.conf	2021-01-19 21:29:49.236199726 +0900
@@ -16,9 +16,9 @@
 # these requests from bringing up the link unnecessarily.
 
 # Never forward plain names (without a dot or domain part)
-#domain-needed
+domain-needed
 # Never forward addresses in the non-routed address spaces.
-#bogus-priv
+bogus-priv
 
 # Uncomment these to enable DNSSEC validation and caching:
 # (Requires dnsmasq to be built with DNSSEC option.)
@@ -50,7 +50,7 @@
 # to  be  up.  Uncommenting this forces dnsmasq to try each query
 # with  each  server  strictly  in  the  order  they   appear   in
 # /etc/resolv.conf
-#strict-order
+strict-order
 
 # If you don't want dnsmasq to read /etc/resolv.conf or any other
 # file, getting its servers from this file instead (see below), then
@@ -121,7 +121,7 @@
 # want dnsmasq to really bind only the interfaces it is listening on,
 # uncomment this option. About the only time you may need this is when
 # running another nameserver on the same machine.
-#bind-interfaces
+bind-interfaces
 
 # If you don't want dnsmasq to read /etc/hosts, uncomment the
 # following line.
@@ -562,7 +562,7 @@
 #dhcp-script=/bin/echo
 
 # Set the cachesize here.
-#cache-size=150
+cache-size=150
 
 # If you want to disable negative caching, uncomment this.
 #no-negcache

/etc/resolv.conf(確認)

従前の一般的なLinuxとは異なり、/run/systemd/resolve/stub-resolv.confのシンボリックリンクになっています。

# ls -l /etc/resolv.conf 
lrwxrwxrwx 1 root root 39 10月 22 23:01 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

dnsmasqの設定反映(再起動)

# systemctl restart dnsmasq.service

状態を確認します。

# systemctl status dnsmasq.service 
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor prese>
     Active: active (running) since Wed 2021-01-19 21:39:21 JST; 1min 11s ago
    Process: 240172 ExecStartPre=/etc/init.d/dnsmasq checkconfig (code=exited, >
    Process: 240180 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, st>
    Process: 240189 ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf >
   Main PID: 240188 (dnsmasq)
      Tasks: 1 (limit: 9255)
     CGroup: /system.slice/dnsmasq.service
             └─240188 /usr/sbin/dnsmasq -x /run/dnsmasq/dnsmasq.pid -u dnsmasq >

 1月 19 21:39:21 rpi4 systemd[1]: Starting dnsmasq - A lightweight DHCP and cac>
 1月 19 21:39:21 rpi4 dnsmasq[240188]: started, version 2.82 cachesize 150
 1月 19 21:39:21 rpi4 dnsmasq[240188]: DNS service limited to local subnets
 1月 19 21:39:21 rpi4 dnsmasq[240188]: compile time options: IPv6 GNU-getopt DB>
 1月 19 21:39:21 rpi4 dnsmasq[240188]: reading /etc/resolv.conf
 1月 19 21:39:21 rpi4 dnsmasq[240188]: using nameserver 127.0.0.53#53
 1月 19 21:39:21 rpi4 dnsmasq[240188]: read /etc/hosts - 9 addresses
 1月 19 21:39:21 rpi4 systemd[1]: Started dnsmasq - A lightweight DHCP and cach>

dnsmasq自体は問題なく起動しています。netstatで確認するとsystemd-resolvedでbindするIPアドレスが127.0.0.53に変更されています。

# !netstat
netstat -anp | grep ":53"
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      240188/dnsmasq      
tcp        0      0 192.168.1.210:53        0.0.0.0:*               LISTEN      240188/dnsmasq      
tcp        0      0 192.168.1.220:53        0.0.0.0:*               LISTEN      240188/dnsmasq      
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      224983/systemd-reso 
tcp6       0      0 ::1:53                  :::*                    LISTEN      240188/dnsmasq      
tcp6       0      0 fd00:dead:beef::210:53  :::*                    LISTEN      240188/dnsmasq      
tcp6       0      0 fe80::dea6:32ff:fedc:53 :::*                    LISTEN      240188/dnsmasq      
tcp6       0      0 fd00:dead:beef::220:53  :::*                    LISTEN      240188/dnsmasq      
tcp6       0      0 fe80::dea6:32ff:fedc:53 :::*                    LISTEN      240188/dnsmasq      
udp        0      0 127.0.0.1:53            0.0.0.0:*                           240188/dnsmasq      
udp        0      0 192.168.1.210:53        0.0.0.0:*                           240188/dnsmasq      
udp        0      0 192.168.1.220:53        0.0.0.0:*                           240188/dnsmasq      
udp        0      0 127.0.0.53:53           0.0.0.0:*                           224983/systemd-reso 
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           1750/avahi-daemon:  
udp6       0      0 ::1:53                  :::*                                240188/dnsmasq      
udp6       0      0 fd00:dead:beef::210:53  :::*                                240188/dnsmasq      
udp6       0      0 fe80::dea6:32ff:fedc:53 :::*                                240188/dnsmasq      
udp6       0      0 fd00:dead:beef::220:53  :::*                                240188/dnsmasq      
udp6       0      0 fe80::dea6:32ff:fedc:53 :::*                                240188/dnsmasq      
udp6       0      0 :::5353                 :::*                                1750/avahi-daemon:  

/etc/dnsmasq.confにドメイン設定追加

/etc/dnsmasq.confの設定時に書き忘れたので、internalドメインの設定を追加します。

--- dnsmasq.conf.org	2021-01-19 21:50:29.937538982 +0900
+++ dnsmasq.conf	2021-01-19 21:59:19.904954640 +0900
@@ -71,7 +71,7 @@
 
 # Add local-only domains here, queries in these domains are answered
 # from /etc/hosts or DHCP only.
-#local=/localnet/
+local=/internal/
 
 # Add domains which you want to force to an IP address here.
 # The example below send any host in double-click.net to a local
@@ -132,7 +132,7 @@
 
 # Set this (and domain: see below) if you want to have a domain
 # automatically added to simple names in a hosts-file.
-#expand-hosts
+expand-hosts
 
 # Set the domain for dnsmasq. this is optional, but if it is set, it
 # does the following things.
@@ -141,7 +141,7 @@
 # 2) Sets the "domain" DHCP option thereby potentially setting the
 #    domain of all systems configured by DHCP
 # 3) Provides the domain part for "expand-hosts"
-#domain=thekelleys.org.uk
+domain=internal
 
 # Set a different domain for a particular subnet
 #domain=wireless.thekelleys.org.uk,192.168.2.0/24

変更を反映します。

# systemctl restart dnsmasq.service

dnsmasqの動作確認

ブロードバンドルーターのDHCP設定で配布するDNSサーバーの設定値を変更する前に、動作確認をします。

digコマンドの@でDNSサーバーを指定すればネットワーク設定と関係なくクエリーを投げてくれるのでLAN内の端末から投げてみます。3

$ dig @192.168.1.210 rpi4.internal

; <<>> DiG 9.16.6-Ubuntu <<>> @192.168.1.210 rpi4.internal
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27068
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;rpi4.internal.			IN	A

;; ANSWER SECTION:
rpi4.internal.		0	IN	A	192.168.1.210

;; Query time: 0 msec
;; SERVER: 192.168.1.210#53(192.168.1.210)
;; WHEN: 火  1月 19 21:02:33 JST 2021
;; MSG SIZE  rcvd: 58

問題ないですね。

ブロードバンドルーターのDNS設定を変更

dnsmasq自体の設定は終わったので、ローカルネットワークで使用しているブロードバンドルーターのDHCPサーバーが配布するDNSサーバーにdnsmasqを設定した端末のIPアドレスを設定します。

プライマリDNSサーバーはdnsmasqをインストールした端末のIPアドレスを設定、セカンダリDNSサーバーにルーターのLAN側アドレスを設定して反映したら、DHCPサーバーの情報を反映するためにPCのLAN接続を一旦切って接続し直します。

接続PCがsystemd-resolvedを使っている場合、DHCPサーバーから受け取った情報を反映せずに参照するDNSサーバーが更新されないので、systemd-resolvedを再起動します。

# systemd-resolve --status
Global
(snip)
  Current DNS Server: 192.168.1.210       
         DNS Servers: 192.168.1.210       
                      192.168.1.1         
(snip)
Link 3 (wlp3s0)
(snip)
  Current DNS Server: 192.168.1.1  
         DNS Servers: 192.168.1.210
                      192.168.1.1  
(snip)
# systemctl restart systemd-resolved
# systemd-resolve --status
Global
(snip)
         DNS Servers: 192.168.1.210       
                      192.168.1.1         
Link 3 (wlp3s0)
(snip)
         DNS Servers: 192.168.1.210
                      192.168.1.1  

改めてLAN内の端末から動作確認します。DHCPで配布されたDNSサーバーを参照させるため、@でのIPアドレス指定はしません。

$ dig rpi4.internal

; <<>> DiG 9.16.1-Ubuntu <<>> rpi4.internal
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3614
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;rpi4.internal.			IN	A

;; ANSWER SECTION:
rpi4.internal.		0	IN	A	192.168.1.210

;; Query time: 3 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: 火  1月 19 23:01:42 JST 2021
;; MSG SIZE  rcvd: 58

AAAAレコード(IPv6)でも引けるか確認します。

$ dig rpi4.internal aaaa

; <<>> DiG 9.16.1-Ubuntu <<>> rpi4.internal aaaa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11367
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;rpi4.internal.			IN	AAAA

;; ANSWER SECTION:
rpi4.internal.		0	IN	AAAA	fd00:dead:beef::210

;; Query time: 4 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: 火  1月 19 23:02:33 JST 2021
;; MSG SIZE  rcvd: 70

逆引きも試してみます。

$ dig -x 192.168.1.210

; <<>> DiG 9.16.1-Ubuntu <<>> -x 192.168.1.210
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1689
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;210.1.168.192.in-addr.arpa.	IN	PTR

;; ANSWER SECTION:
210.1.168.192.in-addr.arpa. 0	IN	PTR	rpi4.internal.

;; Query time: 3 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: 火  1月 19 23:05:36 JST 2021
;; MSG SIZE  rcvd: 82

同じようにIPv6アドレスでの逆引きも設定されているか確認します。

$ dig -x fd00:dead:beef::210

; <<>> DiG 9.16.1-Ubuntu <<>> -x fd00:dead:beef::210
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35059
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;0.1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.e.e.b.d.a.e.d.0.0.d.f.ip6.arpa. IN PTR

;; ANSWER SECTION:
0.1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.e.e.b.d.a.e.d.0.0.d.f.ip6.arpa. 0 IN PTR rpi4.internal.

;; Query time: 3 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: 火  1月 19 23:05:44 JST 2021
;; MSG SIZE  rcvd: 128

dnsmasqを落としたらセカンダリに指定しているDNSサーバーで動くか念の為確認します。dnsmasqを動かしている端末でdnsmasqを停止して、

# systemctl stop dnsmasq.service

LAN内の端末から引けなくなっていることを確認、

$ dig -x fd00:dead:beef::210

; <<>> DiG 9.16.1-Ubuntu <<>> -x fd00:dead:beef::210
;; global options: +cmd
;; connection timed out; no servers could be reached

セカンダリDNSを参照してインターネットには繋がることを確認。

$ dig google.co.jp

; <<>> DiG 9.16.1-Ubuntu <<>> google.co.jp
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30120
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;google.co.jp.			IN	A

;; ANSWER SECTION:
google.co.jp.		40	IN	A	172.217.31.131

;; Query time: 72 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: 火  1月 19 23:10:53 JST 2021
;; MSG SIZE  rcvd: 57

ところどころsystemd-resolvedの動作に引っかかったところはありましたが、想定通りの動作をすることを確認できました。

スポンサーリンク


  1. RFCになってくれればより安心してLAN向けに使えるんですが今は仕方ないですね [return]
  2. dnsmasqの設定としては/etc/dnsmasq.confにno-hostsとaddn-hostsという設定項目があるので、これを有効にすれば/etc/hosts以外のファイルを参照するようになるはずです(未検証) [return]
  3. mDNSが使えないAndroidでもtermuxをインストールしてdnsutilsパッケージをインストールすれば同じように確認が可能です [return]

comments powered by Disqus