2026年6月8日
2026年6月8日
Unbound のACL設定方法|access-control で問い合わせ元を絞り、オープンリゾルバを防ぐ
Unbound のアクセス制限(ACL)は、server: 節の access-control: に「ネットワーク範囲」と「動作」を並べて書きます。何も書かなければ自分自身(localhost)からの問い合わせだけを受け付けるので、LAN や VPN のクライアントに使わせるには、待ち受けの interface: と access-control: の両方に許可する範囲を書き足します。この記事では、動作の種類と選び方、典型的な設定例、反映と確認のしかた、REFUSED が返るときの原因の探し方を説明します。
外部の誰からでも再帰問い合わせを受け付ける「オープンリゾルバ」になると、DNS を使った増幅攻撃の踏み台にされるおそれがあります。ACL は利便性のためだけでなく、こうした悪用を防ぐための設定でもあります。
Unbound の既定の動作
Unbound は既定で次のように振る舞います。
- 待ち受け:
interface:を書かなければ localhost(127.0.0.1と::1)だけで待ち受けます。 - アクセス制御:localhost からは許可、それ以外からの問い合わせには REFUSED を返します。
LAN から使えないときは、どちらが原因かを分けて考えます。待ち受けていないアドレスには問い合わせが届かず(タイムアウト)、待ち受けていても許可されていない範囲からは REFUSED が返ります。
access-control: は問い合わせの送信元 IP アドレスで判定します。複数の行が当てはまる場合は、もっとも範囲の狭い(プレフィックスの長い)行が使われ、どれにも当てはまらなければ refuse になります。書く順番ではなく範囲の広さで決まるので、「全体を拒否してから一部を許可する」といった書き方を意識する必要はありません。
動作の種類と選び方
access-control: に書ける主な動作は次のとおりです。
allow:再帰問い合わせを許可します。通常のクライアントにはこれを使います。再帰を求めない問い合わせ(+norec)は拒否されます。refuse:REFUSED を返します。既定の動作で、DNS の仕様に沿った断り方です。deny:何も返さずに捨てます。クライアントは応答を待ってタイムアウトし、再送を繰り返すことがあります。allow_snoop:再帰を求めない問い合わせも許可します。キャッシュに何が入っているかを調べられるため、管理用の端末だけに使います。allow_setrd:再帰の要求フラグがない問い合わせも、再帰として扱います。deny_non_local・refuse_non_local:Unbound に書いたローカルのデータ(local-data:など)への問い合わせだけを許可し、外部の名前の解決は捨てる(または拒否する)動作です。
新しい版には DNS Cookie に関する動作も加わっています。使える動作は版によって違うので、man unbound.conf で手元の版の一覧を確認してください(2026年10月現在)。
迷ったら、許可する範囲は allow、それ以外は既定の refuse のままにします。deny は、大量の不正な問い合わせを受けていて応答を返すこと自体を減らしたい場合に使います。
設定例
社内 LAN(192.168.10.0/24)と VPN(10.8.0.0/24)から使わせ、LAN の中の1台だけを除外し、管理用の端末にはキャッシュの調査も許可する例です。
# /etc/unbound/unbound.conf.d/acl.conf
server:
# 待ち受け(自分自身と LAN 側のアドレス)
interface: 127.0.0.1
interface: ::1
interface: 192.168.10.53
# 既定の localhost の許可は残る。必要な範囲だけを足す
access-control: 192.168.10.0/24 allow
access-control: 10.8.0.0/24 allow
access-control: fd00:10::/64 allow
# LAN の中の特定の1台は拒否(より狭い範囲が優先される)
access-control: 192.168.10.99/32 refuse
# 管理用端末はキャッシュの調査も許可
access-control: 192.168.10.5/32 allow_snoop
Debian・Ubuntu 系のパッケージでは、本体の /etc/unbound/unbound.conf が unbound.conf.d/ 配下のファイルを読み込む形になっていることが多く、追加の設定は別ファイルに分けると管理しやすくなります。読み込み方は版によって違うので、本体の include の行を確認してください。分けたファイルにも server: の行が要ります。
IPv6 を使っている LAN では、クライアントが IPv6 のアドレスから問い合わせてくることがあります。グローバルの IPv6 プレフィックスを ISP から動的に割り当てられている場合、プレフィックスが変わると ACL から外れるので、ULA(fd00::/8 の範囲)で待ち受けるか、IPv6 側では待ち受けない構成も検討します。
反映と確認
設定を変更する前に、今の設定ファイルの控えを取っておきます。書いたら、まず構文を確認します。
sudo cp -a /etc/unbound /etc/unbound.bak-$(date +%Y%m%d)
sudo unbound-checkconf
unbound-checkconf がエラーを出さなければ反映します。access-control: の変更は unbound-control reload で反映できますが、キャッシュも消えます。interface: を変えた場合は、起動時に開く待ち受けが変わるので再起動します。
sudo unbound-control reload # access-control だけの変更
sudo systemctl restart unbound # interface を変えたとき
新しい版には、キャッシュを残したまま設定を読み直す reload_keep_cache もあります。使えるかどうかは unbound-control のヘルプで確認してください。
確認は、許可した端末・拒否した端末・外部のネットワークの3か所から行います。
# 許可した端末から:status: NOERROR になる
dig @192.168.10.53 example.com A
# 拒否した端末(192.168.10.99)から:status: REFUSED になる
dig @192.168.10.53 example.com A
# 管理用端末から:キャッシュにあれば再帰なしでも答えが返る
dig @192.168.10.53 example.com A +norec
deny にした範囲からは応答がなく、connection timed out になります。外部に公開されたアドレスを持つサーバーでは、外部のネットワーク(スマートフォンのテザリングなど)からも問い合わせて、再帰の答えが返らないことを確認します。
REFUSED が返る・届かないときの確認
よくある原因から順に確認します。
- 送信元アドレスが想定と違う:NAT や VPN を通ると、送信元が変わります。Docker のコンテナからホストの Unbound を使う場合、送信元はブリッジのネットワーク(
172.17.0.0/16など)になります。Unbound のログの詳しさ(verbosity:)を一時的に上げるか、tcpdump -n port 53で実際の送信元を確かめ、その範囲を許可します。 - 待ち受けていない:
sudo ss -lunp | grep ':53 'で、問い合わせ先のアドレスで待ち受けているか確認します。localhost だけならinterface:が足りません。 - 設定ファイルが読み込まれていない:追加したファイルが include の対象外の場所にある、拡張子が違う、
server:の行がない、などです。unbound-checkconfが通っても読み込まれていないことがあるので、include の行を確認します。 - 反映していない:
unbound-control reloadか再起動をしたかを確認します。unbound-controlがつながらない場合は、remote-control:の設定が有効か確認します。 - ファイアウォール:タイムアウトになるときは、OS とクラウド側の両方で UDP と TCP の 53番が許可されているか確認します。
ACL とあわせて設定しておくこと
access-control: は Unbound の中での制御です。ファイアウォールでも 53番への接続元を同じ範囲に絞っておくと、設定の誤りがあっても外部からは届きません。
許可した範囲の中の1台が大量に問い合わせる事態に備えるなら、ip-ratelimit: で送信元 IP アドレスごとの1秒あたりの問い合わせ数に上限を設けられます。値は通常の利用で上限に達しない程度にし、ログで制限がかかっていないか様子を見ながら調整します。
unbound-control の操作を受け付ける remote-control: は、access-control: とは別の仕組みです。control-interface: は既定の localhost のままにしておき、外部から操作できる状態にしないようにします。
ネットワークごとに違う答えを返したい(社内からだけ内部の名前を引けるようにする)場合は、view: 節でビューを作り、access-control-view: で範囲とビューを結び付けます。ビューは答えの内容を変える仕組みなので、問い合わせの許可は access-control: で別に書いておきます。
まとめ
- Unbound は既定で localhost だけを許可します。LAN から使わせるには
interface:とaccess-control:の両方を設定します。 access-control:は送信元 IP アドレスで判定し、もっとも狭い範囲の行が使われます。通常はallowと既定のrefuseで組み立て、allow_snoopは管理用の端末だけに使います。- 反映前に
unbound-checkconfで構文を確認し、ACL の変更はunbound-control reload、待ち受けの変更は再起動で反映します。 - 許可・拒否・外部の3か所から
digで確認し、オープンリゾルバになっていないことを確かめます。 - REFUSED の原因は、送信元アドレスの違い(NAT・Docker)、待ち受け、設定の読み込み、反映忘れ、ファイアウォールの順に確認します。