1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

auth-zone を使った権威・キャッシュ同居の Unbound ~ 同居しない例も添えて

Posted at

・LAN 内のドメインを権威サーバーとして名前解決する
・それ以外のドメインへのクエリは反復問い合わせする
構成の例です。

現代では、権威サーバーとキャッシュサーバーは分離することが推奨されています。
参考: JPRS トピックス&コラム No.020
https://jprs.jp/related-info/guide/topics-column/no20.html

前提

サーバー IP アドレス: 192.168.182.12
AlmaLinux 9.5
Unbound 1.23.0
SELinux 有効

OSの設定

通信ソケットバッファの拡大のため、以下を実行してカーネルパラメーターを変更します。
詳細については公式のドキュメントを参照してください。

# sysctl -w net.core.rmem_max=4194304 >> /etc/sysctl.conf
# sysctl -w net.core.wmem_max=4194304 >> /etc/sysctl.conf

Unbound の実行ユーザー、グループを追加します。

# groupadd unbound
# useradd -g unbound -s /sbin/nologin unbound

Unboundのインストール

標準のリポジトリにあるバージョンが古いため、前提となるパッケージをインストールし、ソースを入手してビルド、インストールします。

# dnf install gcc openssl-devel libevent-devel expat-devel systemd-devel bison flex
# cd /usr/src
# curl -O https://nlnetlabs.nl/downloads/unbound/unbound-latest.tar.gz
# tar xf unbound-latest.tar.gz
# cd unbound-1.23.0
# ./configure --enable-systemd --with-libevent
# make
# make install

Unbound の初期設定

systemd への登録、Unbound で使用するディレクトリの作成と、所有者の変更を行います。
また、unbound-control 用の証明書と秘密鍵の生成を行います。
ルートゾーンのトラストアンカーも取得します。

Unbound 1.23.0 は RFC5011 に対応しているため、自動的にトラストアンカーを更新します。

/usr/lib/systemd/system/unbound.service
[Unit]
Description=Validating, recursive, and caching DNS resolver
Before=nss-lookup.target
Requires=network-online.target
After=network-online.target

[Install]
WantedBy=multi-user.target

[Service]
Type=notify
ExecReload=/usr/sbin/unbound-control reload
ExecStartPre=/usr/local/sbin/unbound-checkconf
ExecStart=/usr/local/sbin/unbound -d -p
NotifyAccess=main
MemoryDenyWriteExecute=true
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
ProtectControlGroups=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=false
ProtectProc=invisible
ProtectSystem=strict
RuntimeDirectory=unbound
ConfigurationDirectory=unbound
StateDirectory=unbound
RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX
RestrictRealtime=true
SystemCallArchitectures=native
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module mount @obsolete @resources
RestrictNamespaces=yes
LockPersonality=yes
RestrictSUIDSGID=yes
ReadWritePaths=/usr/local/etc/unbound /var/log/unbound
# mkdir /usr/local/etc/unbound/files
# chown unbound:unbound -R /usr/local/etc/unbound/files
# unbound-control-setup
# sudo -u unbound /usr/local/sbin/unbound-anchor -a /usr/local/etc/unbound/files/root.key

ログの設定

空のログファイルを作成し、所有者を変更します。

# mkdir /var/log/unbound
# touch /var/log/unbound/unbound.log
# chown -R unbound:unbound /var/log/unbound/

ログローテーションの設定を追加します。

/etc/logrotate.d/unbound
/var/log/unbound/unbound.log
{
        daily
        rotate 7
        missingok
        create
        dateyesterday
        postrotate
                unbound-control log_reopen 
        endscript
}

Unboundの設定

以下の定義ファイルを作成します。

/usr/local/etc/unbound/unbound.conf
server:
    chroot: ""
    
    interface: 192.168.182.12
    interface: 127.0.0.1
    port: 53

    logfile: /var/log/unbound/unbound.log
    log-time-ascii: yes

    # サーバーの CPU コア数を設定する
    num-threads: 4 
    msg-cache-slabs: 4
    rrset-cache-slabs: 4
    infra-cache-slabs: 4
    key-cache-slabs: 4 
    # サーバーの CPU コア数を設定する項目終了
    
    so-reuseport: yes
    outgoing-num-tcp: 1000
    incoming-num-tcp: 1000
    msg-cache-size: 128m
    rrset-cache-size: 256m
    num-queries-per-thread: 2048

    do-ip4: yes
    do-ip6: no
    do-daemonize: no

    access-control: 127.0.0.0/8 allow # ACL を設定 
    access-control: 192.168.182.0/24 allow
    access-control: 192.168.207.0/24 allow
    
    private-address: 192.168.0.0/16 # 左記を含む回答は破棄される DNS rebinding 対策
    private-address: 169.254.0.0/16
    private-address: fd00::/8
    private-address: fe80::/10
    private-domain: "example.com" # private-address を回答に含めるドメインを列挙
    
    local-zone: "182.168.192.in-addr.arpa" transparent # 逆引きゾーンの数だけ列挙
    
    auto-trust-anchor-file: "/usr/local/etc/unbound/files/root.key"

    hide-identity: yes
    hide-version: yes

remote-control:
    control-enable: yes
    control-interface: 127.0.0.1

    auth-zone:
        name: "example.com"
        fallback-enabled: yes
        zonefile: /usr/local/etc/unbound/example.com.zone

    auth-zone:
        name: "182.168.192.in-addr.arpa"
        fallback-enabled: yes
        zonefile: /usr/local/etc/unbound/182.168.192.in-addr.arpa.zone

組み込み以外のルートヒントファイルを使用する場合は、定義ファイルに以下を追加します。

server:
(中略)
+    root-hints: "<ルートヒントファイルの絶対パス>"
(後略)

ゾーンファイルの作成

以下のようなゾーンファイルを作成します。

/usr/local/etc/unbound/example.com.zone
$ORIGIN example.com.
$TTL 3600
@       IN      SOA     dns.example.com. root.example.com. (
                  1          ; Serial
                  900        ; refresh
                  600        ; retry
                  86400      ; expire
                  900        ; minimum
)

@                 IN NS        dns.example.com.
dns               IN A         192.168.182.12
dhcp              IN A         192.168.182.15
/usr/local/etc/unbound/182.168.192.in-addr.arpa.zone
$ORIGIN 182.168.192.in-addr.arpa.
$TTL 3600
@       IN      SOA     dns.example.com. root.example.com. (
                  1          ; Serial
                  900        ; refresh
                  600        ; retry
                  86400      ; expire
                  900        ; minimum
)

@                 IN NS        dns.example.com.
12                IN PTR       dns.example.com.
15                IN PTR       dhcp.example.com.

以下のコマンドにて設定ファイルのチェックを行います。

# unbound-checkconf

Unbound の起動

DNS の着信接続を許可し、Unboundを起動します。

# firewall-cmd --add-service=dns --permanent
# firewall-cmd --reload
# systemctl enable unbound
# systemctl start unbound

確認

以下の通り解決できます。

権威ゾーンのレコード aa flag が立っている
# dig @192.168.182.12 example.com.

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 example.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45700
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com.                   IN      A

;; AUTHORITY SECTION:
example.com.            900     IN      SOA     dns.example.com. root.example.com. 1 900 600 86400 900

;; Query time: 10 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Wed May 07 12:04:27 JST 2025
;; MSG SIZE  rcvd: 85

# dig @192.168.182.12 example.com. NS

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 example.com. NS
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54709
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com.                   IN      NS

;; ANSWER SECTION:
example.com.            3600    IN      NS      dns.example.com.

;; ADDITIONAL SECTION:
dns.example.com.        3600    IN      A       192.168.182.12

;; Query time: 0 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Wed May 07 12:04:41 JST 2025
;; MSG SIZE  rcvd: 74

# dig @192.168.182.12 dns.example.com.

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 dns.example.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19297
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;dns.example.com.               IN      A

;; ANSWER SECTION:
dns.example.com.        3600    IN      A       192.168.182.12

;; Query time: 0 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Wed May 07 12:05:01 JST 2025
;; MSG SIZE  rcvd: 60

# dig @192.168.182.12 -x 192.168.182.15

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 -x 192.168.182.15
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 468
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

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

;; ANSWER SECTION:
15.182.168.192.in-addr.arpa. 3600 IN    PTR     dhcp.example.com.

;; Query time: 0 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Wed May 07 12:05:23 JST 2025
;; MSG SIZE  rcvd: 86
反復問い合わせしたレコード aa flag がない
# dig @192.168.182.12 google.com.

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 google.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52179
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;google.com.                    IN      A

;; ANSWER SECTION:
google.com.             300     IN      A       172.217.175.14

;; Query time: 180 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Wed May 07 12:05:49 JST 2025
;; MSG SIZE  rcvd: 55

DNSSEC 検証も正しく成功・失敗しています。

DNSSEC が正しく設定されているドメイン
# delv @192.168.182.12 jprs.co.jp. +vtrace
;; fetch: jprs.co.jp/A
;; validating jprs.co.jp/A: starting
;; validating jprs.co.jp/A: attempting positive response validation
;; fetch: jprs.co.jp/DNSKEY
;; validating jprs.co.jp/DNSKEY: starting
;; validating jprs.co.jp/DNSKEY: attempting positive response validation
;; fetch: jprs.co.jp/DS
;; validating jprs.co.jp/DS: starting
;; validating jprs.co.jp/DS: attempting positive response validation
;; fetch: jp/DNSKEY
;; validating jp/DNSKEY: starting
;; validating jp/DNSKEY: attempting positive response validation
;; fetch: jp/DS
;; validating jp/DS: starting
;; validating jp/DS: attempting positive response validation
;; fetch: ./DNSKEY
;; validating ./DNSKEY: starting
;; validating ./DNSKEY: attempting positive response validation
;; validating ./DNSKEY: verify rdataset (keyid=20326): success
;; validating ./DNSKEY: marking as secure (DS)
;; validating jp/DS: in fetch_callback_dnskey
;; validating jp/DS: keyset with trust secure
;; validating jp/DS: resuming validate
;; validating jp/DS: verify rdataset (keyid=53148): success
;; validating jp/DS: marking as secure, noqname proof not needed
;; validating jp/DNSKEY: in fetch_callback_ds
;; validating jp/DNSKEY: dsset with trust secure
;; validating jp/DNSKEY: verify rdataset (keyid=35821): success
;; validating jp/DNSKEY: marking as secure (DS)
;; validating jprs.co.jp/DS: in fetch_callback_dnskey
;; validating jprs.co.jp/DS: keyset with trust secure
;; validating jprs.co.jp/DS: resuming validate
;; validating jprs.co.jp/DS: verify rdataset (keyid=13611): success
;; validating jprs.co.jp/DS: marking as secure, noqname proof not needed
;; validating jprs.co.jp/DNSKEY: in fetch_callback_ds
;; validating jprs.co.jp/DNSKEY: dsset with trust secure
;; validating jprs.co.jp/DNSKEY: verify rdataset (keyid=63574): success
;; validating jprs.co.jp/DNSKEY: marking as secure (DS)
;; validating jprs.co.jp/A: in fetch_callback_dnskey
;; validating jprs.co.jp/A: keyset with trust secure
;; validating jprs.co.jp/A: resuming validate
;; validating jprs.co.jp/A: verify rdataset (keyid=43611): success
;; validating jprs.co.jp/A: marking as secure, noqname proof not needed
; fully validated
jprs.co.jp.             300     IN      A       117.104.133.165
jprs.co.jp.             300     IN      RRSIG   A 8 3 300 20250604023001 20250505023001 43611 jprs.co.jp. GNkmT72CLP6GLbPBoVCUNYDKJKdNQqEoT7mBgXr+L4sPVjh1K9oCttS6 eQrBrfBpwFQwSjOmJKk1VcN3aclxF2xd2s1UnOH8tmhKkj0tm16J1GY5 +oLQ+yOwI7MRDLZkevEnbdSjiYDI/EWOavyzEt707VizoD0JsaUTbdfF 2QI=
意図的に不正な設定がされているドメイン
# delv @192.168.182.12 dnssec-failed.mufj.jp. +vtrace
;; fetch: dnssec-failed.mufj.jp/A
;; resolution failed: SERVFAIL

補足(フォワーダーとして使用する場合)

フォワーダーとして使用する場合は以下のようにします。(Google Public DNS に転送する例)

/usr/local/etc/unbound/unbound.conf
(前略)
remote-control:
    control-enable: yes
    control-interface: 127.0.0.1

+   forward-zone:
+       name: "."
+       forward-addr: 8.8.8.8

    auth-zone:
        name: "example.com"
        fallback-enabled: yes
        zonefile: /usr/local/etc/unbound/example.com.zone

    auth-zone:
        name: "182.168.192.in-addr.arpa"
        fallback-enabled: yes
        zonefile: /usr/local/etc/unbound/182.168.192.in-addr.arpa.zone

補足(権威サーバーへ転送する場合)

権威サーバーにクエリを転送したい場合は、stub-zone を使用します。
定義ファイルは以下のようになります。
192.168.182.10 上の権威サーバーは以下記事同等のものとします。

/usr/local/etc/unbound/unbound.conf
server:
    chroot: ""
    
    interface: 192.168.182.12
    interface: 127.0.0.1
    port: 53

    logfile: /var/log/unbound/unbound.log
    log-time-ascii: yes

    # サーバーの CPU コア数を設定する
    num-threads: 4 
    msg-cache-slabs: 4
    rrset-cache-slabs: 4
    infra-cache-slabs: 4
    key-cache-slabs: 4 
    # サーバーの CPU コア数を設定する項目終了
    
    so-reuseport: yes
    outgoing-num-tcp: 1000
    incoming-num-tcp: 1000
    msg-cache-size: 128m
    rrset-cache-size: 256m
    num-queries-per-thread: 2048

    do-ip4: yes
    do-ip6: no
    do-daemonize: no

    access-control: 127.0.0.0/8 allow # ACL を設定 
    access-control: 192.168.182.0/24 allow
    access-control: 192.168.207.0/24 allow
    
    private-address: 192.168.0.0/16 # 左記を含む回答は破棄される DNS rebinding 対策
    private-address: 169.254.0.0/16
    private-address: fd00::/8
    private-address: fe80::/10
    private-domain: "example.com" # private-address を回答に含めるドメインを列挙

+    local-zone: "example.com" transparent
    local-zone: "182.168.192.in-addr.arpa" transparent # 逆引きゾーンの数だけ列挙
    
    auto-trust-anchor-file: "/usr/local/etc/unbound/files/root.key"

    hide-identity: yes
    hide-version: yes

+    domain-insecure: "example.com" # 対象ゾーンでの DNSSEC 検証を無効にする
+    domain-insecure: "182.168.192.in-addr.arpa"

remote-control:
    control-enable: yes
    control-interface: 127.0.0.1
    
+    stub-zone: # クエリを権威サーバーに転送
+        name: "example.com"
+        stub-addr: 192.168.182.10

+    stub-zone:
+        name: "182.168.192.in-addr.arpa"
+        stub-addr: 192.168.182.10

この設定では、以下のように解決されます。

権威を持たないため aa flag が立っていない
# dig @192.168.182.12 example.com.

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 example.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65394
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com.                   IN      A

;; AUTHORITY SECTION:
example.com.            701     IN      SOA     dns1.example.com. root.example.com. 0 900 600 86400 900

;; Query time: 0 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Mon May 19 09:49:46 JST 2025
;; MSG SIZE  rcvd: 86

# dig @192.168.182.12 dns1.example.com.

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 dns1.example.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4964
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;dns1.example.com.              IN      A

;; ANSWER SECTION:
dns1.example.com.       3517    IN      A       192.168.182.10

;; Query time: 0 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Mon May 19 09:49:49 JST 2025
;; MSG SIZE  rcvd: 61

# dig @192.168.182.12 -x 192.168.182.10

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.182.12 -x 192.168.182.10
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31862
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

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

;; ANSWER SECTION:
10.182.168.192.in-addr.arpa. 3542 IN    PTR     dns1.example.com.

;; Query time: 0 msec
;; SERVER: 192.168.182.12#53(192.168.182.12) (UDP)
;; WHEN: Mon May 19 09:49:51 JST 2025
;; MSG SIZE  rcvd: 86
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?