2026年6月11日
2026年6月11日
Unbound から dnsmasq へ移行する手順|unbound.conf の設定を dnsmasq に置き換え、失う機能を確かめてから切り替える
Unbound から dnsmasq への移行は、「unbound.conf の各設定が dnsmasq の何に当たるかを表にする → dnsmasq では置き換えられない機能を確認する → 別のポートで並行稼働して答えを比べる → 切り替える」という順に進めるのが安全です。dnsmasq は小規模な LAN 向けの転送・キャッシュと DHCP を1つにまとめたソフトで、Unbound のような自前で再帰問い合わせを行うリゾルバーではありません。この記事では、移行が向いている場面の見極めから、設定の対応づけ、比較、切り替えと切り戻しまでを説明します。
内容は Unbound 1.x 系と dnsmasq 2.8x〜2.9x 系を前提にしています(2026年10月現在)。オプションの有無は版やビルド時の設定で変わるため、手元の man dnsmasq と dnsmasq --version の出力(DNSSEC などの対応状況が表示されます)で確認してください。作業前に unbound.conf と関連ファイルをバックアップし、検証用の環境で試してから本番に移ります。
移行が向いている場面と向いていない場面
dnsmasq に移す理由として多いのは、DHCP で配った端末名をそのまま名前解決したい、ルーターや小さな VPS で設定を1ファイルにまとめたい、といったものです。こうした用途では dnsmasq のほうが扱いやすい場面があります。
一方で、dnsmasq は問い合わせを上流のリゾルバーに転送して結果をキャッシュする仕組みです。次の機能を使っている場合は、移行後に失われるか、別の方法が必要になります。
- 自前での再帰問い合わせ:dnsmasq はルートサーバーから順にたどる処理をしません。上流(プロバイダーのリゾルバーや公開リゾルバー、または残した Unbound)が必要です。
- プリフェッチや期限切れキャッシュの応答(
prefetch、serve-expired):dnsmasq には同等の機能がありません。 - DNS over TLS での転送(
forward-tls-upstream):dnsmasq 単体では扱えません。暗号化が必要なら、上流に Unbound や stubby などを置く構成になります。 - auth-zone や RPZ:dnsmasq の権威モードは機能が限られ、RPZ には対応していません。
これらが必須なら、Unbound を上流として残し、手前に dnsmasq を置いて DHCP と端末名の解決だけを任せる構成も選べます。全部を置き換えるか、役割を分けるかをここで決めておきます。
手順1:unbound.conf の設定を対応づける
まず、現在の設定を一覧にします。コメントや既定値の行を除いた実際の設定は次のように取り出せます。
grep -vE '^\s*(#|$)' /etc/unbound/unbound.conf /etc/unbound/unbound.conf.d/*.conf
よく使われる設定と、dnsmasq での書き方の対応は次のとおりです。
| Unbound | dnsmasq | 注意点 |
|---|---|---|
interface: |
listen-address= と bind-interfaces |
待ち受けを限定する |
access-control: |
直接の対応なし | local-service かファイアウォールで制限 |
local-zone: "lan." static |
local=/lan/ |
上流に転送せず、未登録の名前は NXDOMAIN |
local-data: "nas.lan. A 192.168.1.10" |
host-record=nas.lan,192.168.1.10 |
逆引き(PTR)も作られる |
local-zone: "ads.example." always_nxdomain |
address=/ads.example/ |
サブドメインも含めて NXDOMAIN |
forward-zone: name: "." |
server= と no-resolv |
resolv.conf を読ませない |
forward-zone / stub-zone(特定ドメイン) |
server=/corp.example/10.0.0.53 |
私設アドレスの応答に注意(後述) |
msg-cache-size など |
cache-size= |
単位は件数。既定は150件 |
auto-trust-anchor-file: |
dnssec と trust-anchor |
ビルドが DNSSEC に対応している必要あり |
つまずきやすいのは、1件だけの名前を address=/nas.lan/192.168.1.10 で書くことです。address= はそのドメイン配下のすべての名前に同じ答えを返すため、foo.nas.lan も同じアドレスになります。1件ずつのレコードは host-record= で書くほうが Unbound の local-data に近い動きになります。
手順2:dnsmasq の設定を書く
置き換えた設定の例です。社内の corp.example だけ社内の DNS サーバーへ転送し、それ以外は上流の2台に転送します。
# /etc/dnsmasq.d/migrate.conf
# 待ち受け(並行稼働の間は別ポートにする)
listen-address=192.168.1.53
bind-interfaces
port=5353
# 上流(resolv.conf は読まない)
no-resolv
server=192.0.2.1
server=192.0.2.2
# 社内ドメインの転送と、私設アドレスの応答を許可
server=/corp.example/10.0.0.53
server=/10.in-addr.arpa/10.0.0.53
rebind-domain-ok=/corp.example/
# ローカルの名前
local=/lan/
host-record=nas.lan,192.168.1.10
host-record=printer.lan,192.168.1.20
# 広告などのブロック
address=/ads.example/
# 基本の防御とキャッシュ
domain-needed
bogus-priv
stop-dns-rebind
cache-size=10000
stop-dns-rebind は、上流から私設アドレス(192.168.x.x など)が返ってきた応答を捨てる設定です。DNS リバインディング攻撃を防ぐ目的ですが、社内の DNS サーバーが私設アドレスを返すのは正常なので、そのドメインを rebind-domain-ok で除外します。これを忘れると「社内の名前だけ引けない」という症状になります。同様に bogus-priv は私設アドレスの逆引きを上流に転送しない設定なので、社内の逆引きゾーンは server=/10.in-addr.arpa/... のように個別に転送先を書き、実際に引けるかを手順3で確かめます。
DNSSEC の検証を続けたい場合は、次の行を足します。トラストアンカーのファイルの場所はディストリビューションのパッケージによって異なるので、dpkg -L dnsmasq-base や rpm -ql dnsmasq で探します。
dnssec
conf-file=/usr/share/dnsmasq-base/trust-anchors.conf
man dnsmasq にあるとおり、上流が DNSSEC の情報を返さない場合、検証を有効にすると名前解決ができなくなることがあります。上流が DNSSEC に対応したリゾルバーかどうかを先に確かめてください。
設定を書いたら、起動する前に書式を検査します。
sudo dnsmasq --test
sudo systemctl restart dnsmasq
「syntax check OK」と表示されれば読み込めます。起動しない場合は journalctl -u dnsmasq を見ます。53番ポートに切り替えたときに「Address already in use」が出る場合は、Unbound や systemd-resolved のスタブ(127.0.0.53)が同じポートを使っています。sudo ss -lunp 'sport = :53' でどのプロセスが待ち受けているかを確認します。
手順3:Unbound と dnsmasq の答えを比べる
Unbound(53番)と dnsmasq(5353番)に同じ問い合わせを送り、結果を比べます。普段よく引かれる名前の一覧を作り、ローカルの名前・社内ドメイン・外部のドメイン・逆引きを混ぜておきます。
cat > names.txt <<'EOF'
nas.lan A
printer.lan A
intranet.corp.example A
www.example.com A
10.1.0.10.in-addr.arpa PTR
ads.example A
EOF
while read -r name type; do
a=$(dig @192.168.1.53 -p 53 "$name" "$type" +short | sort)
b=$(dig @192.168.1.53 -p 5353 "$name" "$type" +short | sort)
[ "$a" = "$b" ] || printf 'DIFF: %s %s\n unbound: %s\n dnsmasq: %s\n' "$name" "$type" "$a" "$b"
done < names.txt
外部のドメインは、負荷分散などで答えが毎回変わることがあるため、差が出たら何度か引き直して判断します。答えだけでなく、dig の status:(NOERROR か NXDOMAIN か)も見比べます。Unbound で NXDOMAIN だった名前が dnsmasq で上流に転送されて別の答えになっていれば、local= の範囲が足りていません。キャッシュの状況は sudo pkill -USR1 dnsmasq を送ると統計がログに出力されるので、cache-size の目安に使えます。
手順4:切り替えと切り戻し
比較で差がなくなったら切り替えます。
- Unbound を止め、自動起動を無効にします(
sudo systemctl disable --now unbound)。 - dnsmasq の
port=5353を消して53番で待ち受けるようにし、再起動します。 - 端末から名前解決を確認します。DHCP で配っている DNS サーバーのアドレスを変える場合は、リース期間が切れるまで古いアドレスを参照する端末が残ります。
アドレスを変えずに入れ替えれば、端末側の設定は変えずに済みます。問題が出たら dnsmasq を止めて Unbound を起動し直せば元に戻るので、unbound.conf はしばらく残しておきます。切り替え直後は log-queries を一時的に有効にして、想定外の名前が NXDOMAIN になっていないかをログで確認すると、見落としに気づきやすくなります。問い合わせのログには端末の利用状況が含まれるので、確認が終わったら無効に戻し、ログの保管期間にも気を配ります。
まとめ
- dnsmasq は転送とキャッシュのソフトで、再帰問い合わせ・プリフェッチ・DNS over TLS での転送・RPZ は置き換えられません。必要なら Unbound を上流に残します。
local-dataはhost-record=に、local-zoneはlocal=やaddress=に置き換えます。address=はサブドメインにも効く点に注意します。- 社内ドメインは
server=/ドメイン/で転送し、stop-dns-rebindを使うならrebind-domain-okで除外します。 dnsmasq --testで検査し、別ポートで答えを比べてから53番に切り替えます。