2026年6月5日

2026年6月5日

dnsmasq のアクセス制限(ACL)設定方法|待ち受けの限定・local-service・ファイアウォールで問い合わせ元を絞る

dnsmasq には、BIND の allow-query のように「このアドレスからの問い合わせだけに答える」という ACL の設定項目はありません。代わりに、待ち受けるインターフェースやアドレスを絞る設定、ローカルのネットワーク以外を無視する local-service、そして OS のファイアウォールを組み合わせて問い合わせ元を制限します。この記事では、それぞれの設定の意味と書き方、確認の方法、ありがちな失敗を説明します。

なぜ制限が要るのか

外部から誰でも問い合わせできるキャッシュDNS(オープンリゾルバー)は、送信元を偽った問い合わせで大きな応答を第三者に送りつける攻撃(DNS アンプ攻撃)の踏み台に使われます。プロバイダーから警告を受けたり、サーバーの回線が埋まったりする原因になります。dnsmasq は家庭や社内の LAN 向けに使われることが多いソフトですが、VPS やクラウドのサーバーで動かすと、初期状態のままではインターネット側のインターフェースにも答えてしまう場合があります。

制限は次の3段階で考えると整理しやすくなります。

  1. どこで待ち受けるか:interface・listen-address・except-interface
  2. どこからの問い合わせに答えるか:local-service
  3. パケットをそもそも届かせるか:ファイアウォール(nftables・ufw など)

送信元のアドレス単位で細かく許可・拒否したい場合は、3のファイアウォールが担当です。

待ち受けるインターフェースを絞る

設定は /etc/dnsmasq.conf か、/etc/dnsmasq.d/ 配下のファイルに書きます。作業前に今の設定ファイルのコピーを取っておきます。

# /etc/dnsmasq.d/10-listen.conf
# LAN 側のインターフェースだけで待ち受ける
interface=eth1
# インターネット側では待ち受けない(interface と併用する場合の明示)
except-interface=eth0
# 指定したインターフェースのアドレスにだけソケットを作る
bind-interfaces

interface= を指定すると、ループバック(lo)は自動的に対象に加わると man ページで説明されています。インターフェース名ではなくアドレスで指定したい場合は listen-address=192.168.10.1 と書けます。複数のアドレスで待ち受けるなら、この行を並べます。

bind-interfaces と bind-dynamic

何も指定しないと、dnsmasq はすべてのアドレス(ワイルドカード)でソケットを開き、届いた問い合わせのうち対象外のインターフェースに来たものを内部で捨てます。この場合、ss で見ると 0.0.0.0:53 で待ち受けているように見え、同じ53番を使う他のソフト(systemd-resolved など)と衝突する原因にもなります。

  • bind-interfaces:指定したインターフェースのアドレスにだけソケットを作ります。起動時に存在しないインターフェースやアドレスには対応できません。
  • bind-dynamic:インターフェースやアドレスが後から増減しても追従します。VPN のように後から立ち上がるインターフェースで待ち受けたいときに向いています。

どちらか一方を使い、両方は書きません。

DHCP を使っている場合

dnsmasq を DHCP サーバーとしても使っているなら、インターネット側のインターフェースで DHCP を配らないようにも気を配ります。no-dhcp-interface=eth0 と書くと、そのインターフェースでは DNS だけを扱い、DHCP は提供しません。dhcp-range は LAN のアドレス範囲だけに書きます。

local-service でローカルのネットワークに限る

local-service を書くと、dnsmasq が動いているサーバーのインターフェースが属するサブネット(直接つながっているネットワーク)以外からの問い合わせを無視します。インターフェースの指定を書かずに動かしている場合の安全策として役立ちます。

# /etc/dnsmasq.d/20-local.conf
local-service

ディストリビューションによっては、起動スクリプトやパッケージの既定の設定で最初から有効になっていることがあります。また、比較的新しい版では local-service に値を付けて範囲をさらに絞る書き方(サーバー自身からの問い合わせだけに限る、など)が加わっています。使える書き方は版によって違うため、man dnsmasq で手元の版の説明を確認してください(2026年10月現在)。

注意したいのは、local-service は「直接つながったサブネット」を基準にする点です。ルーターを1段越えた別のサブネット(社内の別拠点や VPN の先など)からの問い合わせも無視されます。こうしたネットワークにも答えたい場合は、interface や listen-address で待ち受けを指定したうえで、ファイアウォールで許可する送信元を決めます。また man ページでは、interface・except-interface・listen-address などで待ち受けを指定した場合、local-service は働かないと説明されています。待ち受けを明示する構成では、送信元の制限はファイアウォール側で行うと考えておくと混乱しません。

ファイアウォールで送信元を絞る

アドレス単位の許可・拒否は OS のファイアウォールで行います。DNS は UDP と TCP の両方を使うので、両方に同じ制限をかけます。

ufw の例(Ubuntu など)

# 既定で受信を拒否している前提で、LAN からの DNS だけを許可する
sudo ufw allow from 192.168.10.0/24 to any port 53 proto udp
sudo ufw allow from 192.168.10.0/24 to any port 53 proto tcp
sudo ufw status numbered

ufw の既定の受信方針が拒否になっていることを ufw status verbose で確かめます。許可になっていると、上のルールだけでは制限になりません。SSH で作業している場合は、ufw を有効にする前に SSH の許可ルールがあるかを確認しておきます。

nftables の例

# /etc/nftables.d/dns.nft(読み込み方法は環境に合わせる)
table inet dns_filter {
    chain input {
        type filter hook input priority 0; policy accept;
        udp dport 53 ip saddr != { 127.0.0.0/8, 192.168.10.0/24 } drop
        tcp dport 53 ip saddr != { 127.0.0.0/8, 192.168.10.0/24 } drop
        udp dport 53 ip6 saddr != { ::1, fd00:10::/64 } drop
        tcp dport 53 ip6 saddr != { ::1, fd00:10::/64 } drop
    }
}

ip saddr の条件は IPv4 のパケットにだけ当てはまるため、IPv6 用の ip6 saddr の行も書いています。IPv6 のアドレスを持つサーバーで IPv4 の制限だけを書くと、IPv6 からは問い合わせが通ってしまうので注意が必要です。ほかにファイアウォールの設定(firewalld や既存の nftables のルール)がある場合は、それと矛盾しないように組み込みます。

答える内容を絞る設定もあわせて入れる

問い合わせ元の制限ではありませんが、次の設定も入れておくと、不要な問い合わせを上流に送らずに済み、安全性も上がります。

# /etc/dnsmasq.d/30-hardening.conf
# ドットを含まない名前を上流に送らない
domain-needed
# プライベートアドレスの逆引きを上流に送らない
bogus-priv
# 上流からの答えにプライベートアドレスが含まれていたら捨てる(DNS リバインディング対策)
stop-dns-rebind
# 社内向けの名前でプライベートアドレスを返す必要があるドメインは例外にする
rebind-domain-ok=/office.example.com/

stop-dns-rebind を入れると、社内向けにプライベートアドレスを返している上流のドメインまで捨てられることがあります。その場合は rebind-domain-ok で例外にします。

設定を確認する

# 設定ファイルの構文を確認する
dnsmasq --test

# 反映する
sudo systemctl restart dnsmasq

# どのアドレスで待ち受けているか
sudo ss -lnup 'sport = :53'
sudo ss -lntp 'sport = :53'

bind-interfaces を入れていれば、0.0.0.0:53 ではなく LAN 側のアドレスと 127.0.0.1 だけが表示されます。

次に、許可した場所と許可していない場所の両方から問い合わせます。

# LAN 内の端末から:答えが返ること
dig @192.168.10.1 www.example.com

# 外部のサーバーから:タイムアウトするか、答えが返らないこと
dig @203.0.113.10 www.example.com

外部からの確認は、自分の管理している別のサーバーや、モバイル回線の端末から行います。タイムアウトすれば、ファイアウォールか dnsmasq のどちらかで止まっています。どこで止まっているかを知りたい場合は、一時的に log-queries を有効にしてログを見ます。local-service で捨てられた問い合わせは、ローカルのネットワーク以外からの問い合わせを無視した旨のメッセージとして記録されます(文言は版によって異なります)。確認が終わったら log-queries は外し、ログが増え続けないようにします。

まとめ

  • dnsmasq に送信元アドレス単位の ACL はないため、待ち受けの限定・local-service・ファイアウォールを組み合わせます。
  • interface(または listen-address)と bind-interfaces で、インターネット側のインターフェースでは待ち受けないようにします。
  • local-service は直接つながったサブネットだけに答える設定で、別のサブネットや VPN の先は対象外になる点に注意します。
  • アドレス単位の制限は nftables や ufw で、UDP と TCP、IPv4 と IPv6 の両方に書きます。
  • dnsmasq --test・ss・内外からの dig で、意図どおりに制限されているかを確かめます。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲