Unbound DNSリゾルバ設定ガイド:DNSSECバリデーション付きセキュアな再帰DNSサーバーの構築

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

クイックスタート:5分でUnboundを動かす

内部DNSクエリがCloudflareやGoogleを経由してから解決される理由が気になったことはありませんか?あるいは、上流サーバーが1台落ちるだけでネットワーク全体が止まる理由は?Unboundはその両方を解決します。自分のハードウェア上で再帰リゾルバとして動作し、すべてのクエリに対してルートサーバーから権威ネームサーバーまでDNSツリーを辿ります。サードパーティのリゾルバ不要。ログが外部に送られることもありません。

Ubuntu/Debianで最速の導入手順:

# Unboundをインストール
sudo apt update && sudo apt install -y unbound

# サービスを有効化して起動
sudo systemctl enable --now unbound

# 動作確認
dig @127.0.0.1 google.com

digがAレコードを返せば、Unboundは起動して名前解決できています。これが基本です。以降の手順で本番環境に対応させていきます。

詳細解説:Unboundの仕組み

再帰・フォワーディング・キャッシュの違い

多くの家庭用ルーターはフォワーディングリゾルバを使っており、クエリを8.8.8.8に転送してその結果をキャッシュします。Unboundもそれは可能ですが、真の強みは完全な再帰解決にあります。ルートDNSサーバーから始まり、階層を辿って権威ネームサーバーに直接到達します。これによって3つの具体的なメリットが得られます:

  • 特定の上流サーバーにクエリのログを取られたり改ざんされたりしない
  • DNSSECバリデーションがローカルで行われ、第三者に委ねない
  • キャッシュTTLやネガティブキャッシュの動作を自分で制御できる

主要な設定ファイル

Unboundのメイン設定は/etc/unbound/unbound.confにあります。デフォルトは最小限の内容です。ホームラボや小規模オフィス向けの実用的な出発点として、アクセス制御・DNSSEC・パフォーマンスチューニング・基本的なプライバシー設定をカバーした例を示します:

sudo nano /etc/unbound/unbound.conf
server:
    # リッスンするネットワークインターフェース
    interface: 0.0.0.0
    port: 53

    # ローカルネットワークからのクエリを許可
    access-control: 127.0.0.1/32 allow
    access-control: 192.168.0.0/16 allow
    access-control: 10.0.0.0/8 allow
    access-control: 0.0.0.0/0 refuse

    # DNSSECバリデーション
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    # ルートヒントファイル(定期的に更新)
    root-hints: "/var/lib/unbound/root.hints"

    # パフォーマンスチューニング
    num-threads: 2
    cache-max-ttl: 86400
    cache-min-ttl: 300
    neg-cache-size: 4m
    prefetch: yes
    prefetch-key: yes

    # プライバシー設定
    hide-identity: yes
    hide-version: yes
    qname-minimisation: yes

    # ログ設定(verbosity: 0=最小限, 2=デバッグ)
    verbosity: 1
    log-queries: no
    logfile: "/var/log/unbound/unbound.log"

ルートヒントファイルの取得

ルートヒントファイルは、13のルートネームサーバークラスターの場所をUnboundに教えるものです。変更はまれですが、古いヒントファイルはインフラ移行時に微妙な名前解決の失敗を引き起こすことがあります:

sudo mkdir -p /var/lib/unbound
sudo curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
sudo chown unbound:unbound /var/lib/unbound/root.hints

DNSSECバリデーションの有効化

DNSSECは、自前のリゾルバを運用する最大の理由と言っても過言ではありません。有効化すると、UnboundはすべてのDNSレスポンスの暗号署名を検証します。侵害された上流サーバーが偽のAレコードを注入することができなくなります。これはDNSキャッシュポイズニング攻撃の典型的な手口です。DNSSECなしでは、それが起きても気づくことすらできません。

トラストアンカー(ルートゾーンの公開鍵)を初期化します:

sudo unbound-anchor -a /var/lib/unbound/root.key
sudo chown unbound:unbound /var/lib/unbound/root.key

2つのテストで動作を確認します:

# AD(認証済みデータ)フラグが返るはず
dig @127.0.0.1 dnssec-failed.org +dnssec

# このドメインは意図的にDNSSECを壊してある — SERVFAILが返るはず
dig @127.0.0.1 www.dnssec-failed.org

www.dnssec-failed.orgSERVFAILが返るのが正しい結果です。Unboundが不正なレスポンスを返すことを拒否したということです。これがまさに期待する動作です。

再起動と確認

sudo systemctl restart unbound
sudo systemctl status unbound

# 本番環境で再起動する前に設定の構文チェック
sudo unbound-checkconf

応用的な使い方

内部ドメイン向けスプリットホライズンDNS

nas.home.labvpn.internalといった内部ホスト名を使っていますか?パブリックインターネットに触れることなく、それらをローカルで解決したいはずです。設定にlocal-zonelocal-dataディレクティブを追加するだけです:

server:
    # 内部ゾーンを定義
    local-zone: "home.lab." static

    # 内部ホストのAレコードを追加
    local-data: "nas.home.lab. IN A 192.168.1.10"
    local-data: "router.home.lab. IN A 192.168.1.1"
    local-data: "vpn.home.lab. IN A 192.168.1.20"

    # 逆引き(PTRレコード)
    local-data-ptr: "192.168.1.10 nas.home.lab"

特定ゾーンを別のリゾルバにフォワード

Active Directory環境やスプリットDNSのVPN構成では、特定のゾーンを内部ネームサーバーで処理しつつ、それ以外は再帰解決する設定が必要になることがよくあります。forward-zoneブロックを1つ追加するだけで対応できます:

forward-zone:
    name: "corp.internal."
    forward-addr: 10.0.0.53   # 内部のAD DNSサーバー

不正利用を防ぐレート制限

LAN外、たとえばVPS上にリゾルバを公開する場合、レート制限は必須です。設定しなければ、オープンDNSアンプリファイアとして悪用されます:

server:
    ratelimit: 1000             # IPごとの1秒あたり最大クエリ数
    ratelimit-size: 4m
    ip-ratelimit: 2000          # IPごとの制限

フォワーディングモードでのDNS-over-TLS(DoT)上流

上流にフォワードしつつ通信を暗号化したい場合、UnboundはクライアントサイドのDNS-over-TLSに対応しています。これはこのブログのDoH/DoTサーバーガイドとは異なるアプローチです。ここではUnboundが暗号化クエリを上流に送るクライアントとして動作しており、エンドポイントとして提供する側ではありません:

forward-zone:
    name: "."
    forward-tls-upstream: yes
    forward-addr: 1.1.1.1@853#cloudflare-dns.com
    forward-addr: 9.9.9.9@853#dns.quad9.net

unbound-controlによるモニタリング

Unboundには、名前解決の遅延やキャッシュヒット率の低下を診断するのに実際に役立つ統計インターフェースが付属しています。まず有効化します:

# 設定でリモートコントロールを有効化
# remote-control:
#     control-enable: yes

# unbound-control用のTLSキーをセットアップ
sudo unbound-control-setup

# リアルタイム統計を確認
sudo unbound-control stats_noreset | grep -E 'total|cache|num'

# 特定のキャッシュレコードをフラッシュ(デバッグ時に便利)
sudo unbound-control flush www.example.com

# キャッシュ全体をダンプ
sudo unbound-control dump_cache

現場で役立つ実践的なヒント

ルートヒントの更新を自動化する

ルートヒントが変わることはほとんどありませんが、最新の状態を保つのは良い習慣です。月次のcronジョブで手作業なしに対応できます:

sudo crontab -e

# 以下の行を追加:
0 3 1 * * curl -s -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.root && systemctl reload unbound

クライアントをUnboundに向ける

サーバー自身の設定:

# /etc/resolv.conf を編集するか、systemd-resolvedのスタブを使用
nameserver 127.0.0.1

ネットワーク全体に適用するには、DHCPサーバー(ルーターまたはdnsmasq)でUnboundホストのIPをDNSサーバーとして配布するよう設定します。ネットワーク上のすべてのデバイスが自動的に適用されます。クライアントごとの設定は不要です。

任意のドメインのDNSSECステータスを確認する

# フラグに「ad」(認証済みデータ)があるか確認
dig @127.0.0.1 github.com +dnssec | grep flags

# DNSSECチェーンの詳細トレース
dig @127.0.0.1 github.com +sigchase +trusted-key=/var/lib/unbound/root.key

デバッグ用のログ設定

名前解決ができない場合は、静的なログを掘り返すより、一時的にverbosityを上げる方が効果的です:

sudo unbound-control verbosity 2

# リアルタイムで確認
sudo tail -f /var/log/unbound/unbound.log

# 完了したら元に戻す
sudo unbound-control verbosity 1

本番環境での運用経験

15台のデバイスを抱えるホームラボで1年以上Unboundを運用しています。1日あたり約8,000〜12,000件のDNSクエリを処理し、キャッシュヒット率は常に70%以上を維持しています。解決レイテンシが実態を語っています。上流フォワード時の約30msが、キャッシュヒット時は5ms未満に、ルートサーバーへのコールド再帰ルックアップでも150ms未満に収まっています。よく通るパスで6倍の改善です。

さらに印象的だったのは、設定を誤った上流で検証テストをしていた期間中、DNSSECバリデーションが実際に2件のスプーフィングされたレスポンスを検出したことです。単純なフォワーディングでは両方とも無言で受け入れていたでしょう。気づくことすらできなかったはずです。

最近のUbuntuでの注意点として、systemd-resolvedがすでにポート53をバインドしているため、Unboundが起動しないことがあります。回避策は2つあります:

# 方法1: systemd-resolvedのスタブリスナーを無効化
sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved

# 方法2: Unboundをポート5335で動かし、systemd-resolvedをそこに向ける
# unbound.conf の設定: port: 5335
# /etc/systemd/resolved.conf の設定: DNS=127.0.0.1:5335

セキュリティ強化チェックリスト

  • access-control: 0.0.0.0/0 refuseをデフォルト拒否として設定し、必要なサブネットのみをホワイトリストに追加する
  • hide-identity: yeshide-version: yesを有効にしてバージョンフィンガープリンティングをブロックする
  • qname-minimisation: yesを使用する — Unboundが各ネームサーバーに送るラベルを必要最小限にすることで、データの漏洩を大幅に削減できる
  • パブリックVPSではufwまたはiptablesでインターネットからポート53を完全にブロックする
  • Unboundを最新の状態に保つ — sudo apt update && sudo apt upgrade unbound

見た目ほど複雑な設定ではありません。20分の設定作業で、完全な再帰解決・DNSSECバリデーション・サードパーティへのログなし・体感的に瞬時のキャッシュルックアップが手に入ります。ホームラボや小規模な本番環境にとって、このトレードオフに反論できる理由はほとんどないでしょう。

Share: