こんにちは。日々是発見が楽しみな西山です。
先日、ご自宅のルーター、キャッシュDNSの性能は足りてますか?
というブログを投稿したのですが、
「キャッシュDNSサーバーを自作してみるって書いてるのに作り方書いてない」
という至極もっともなツッコミをいただきました。
なので、今回はUnboundでキャッシュDNSサーバーを作る記事を書こうと思います。
私の自宅環境ではProxmox VEが稼働しているので、例によってLXCコンテナを立てて構築しましたが、自宅用のキャッシュDNS程度ならメモリやCPUリソースは少なくていいので、例えば旧世代のラズパイでも全然かまいません。
また、QNAPやSynologyのNASでDockerを動かせるモデルがあるので、そういう環境があればUnboundのDockerコンテナを作ってもいいかと思います。
インストール
AlmaLinux9のコンテナを立て、Unboundをインストールしました。
dnf install unbound
Linux系OSなら大体はリポジトリからUnboundをインストールできるかと思います。
cd /etc/unbound
でUnboundのコンフィグディレクトリに移動し、主に「unbound.conf」を変更していきます。
私はカスタマイズを見やすくするため、/etc/unbound/unbound.conf はわざと
include: /etc/unbound/conf.d/*.conf
の1行だけに書き換え、/etc/unbound/conf.d/ 配下に設定ファイルを全部まとめています(もちろん、/etc/unbound/unbound.conf をそのまま編集しても構いません)。
また、宅内のPCやスマホからIPv6 DNSとして参照できるよう設定していますが、そこの設定がけっこう複雑になりますので、割り切ってIPv4だけで利用できるようにしてもいいと思います。
基本設定
base.conf というファイルに、ネットワーク設定や参照先DNSサーバーなど、基本的な設定をまとめました。
server:
interface: 0.0.0.0
interface: ::0
interface: 2001:db8::2
unboundデーモンがクエリを受け付けるインターフェースを指定しています。
IPアドレス「2001:db8::1」は、このDNSサーバーの固定IPv6アドレスです。
prefer-ip6: yes
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 192.168.0.0/24 allow
access-control: ::0/0 refuse
access-control: ::1/128 allow
access-control: 2001:db8::/64 allow
access-control: fe80::/64 allow
「access-control:」はローカルIPからのクエリのみ許可し、他は拒否する設定です。
特にIPv6でインターネット接続可能な場合、ルーター等の環境によっては外部ネットワークから到達できてしまう場合がありますので、ファイアウォールと組み合わせてしっかり止めておきましょう。
interface-automatic: yes
module-config: "iterator"
val-permissive-mode: yes
hide-version: yes
hide-identity: yes
root-hints: "/var/lib/unbound/root.hints"
「root-hints:」の行で、最新のルートヒントを利用しています。
これを指定した場合、下記コマンドでルートヒントを取得する必要があります。
「root-hints:」の行を省略すると、Unbound組み込みのルートヒントが利用されます。組み込みルートヒントでも別に問題はありません。
logfile: "/var/log/unbound/unbound.log"
use-syslog: no
log-time-ascii: yes
Unboundのログをsyslogとは別に出力します(しなくても構いません)。
この設定をする場合、起動前に /var/log/unbound ディレクトリの作成+オーナーをunboundユーザーに変更する作業が必要です。
forward-zone:
name: "."
forward-addr: 2001:a7ff:5f01::a
forward-addr: 2001:a7ff:5f01:1::a
クエリ問い合わせ先のキャッシュDNSを指定します。
私は自宅のネットワークから一番近く、レイテンシが小さいNTTフレッツ網用のキャッシュDNSを指定しています。
remote-control:
control-interface: 127.0.0.1
control-interface: ::1
Unboundのコントロールコマンド「unbound-control」や、APIへの接続許可設定です。デフォルト設定(サーバーローカルからのみ実行可)のままです。
パフォーマンス設定
performance.conf というファイルに、キャッシュサイズやスレッド数など、Unboundの性能に関する設定をまとめました。
(私はチューニングオタクなので、カリッカリに性能を出す設定になってます)
server:
msg-cache-slabs: 8
rrset-cache-slabs: 8
infra-cache-slabs: 8
key-cache-slabs: 8
rrset-cache-size: 512m
msg-cache-size: 128m
outgoing-num-tcp: 1000
incoming-num-tcp: 1000
outgoing-range: 8192
num-queries-per-thread: 4096
so-rcvbuf: 4m
so-sndbuf: 4m
minimal-responses: yes
qname-minimisation: yes
infra-cache-numhosts: 1000000
その他の設定
unbound.conf というファイルを作り、デフォルトコンフィグから変更していない設定をまとめています。
server:
verbosity: 1
statistics-interval: 0
statistics-cumulative: no
extended-statistics: yes
num-threads: 4
interface-automatic: no
outgoing-port-permit: 32768-60999
outgoing-port-avoid: 0-32767
so-reuseport: yes
ip-transparent: yes
max-udp-size: 3072
chroot: ""
username: "unbound"
directory: "/etc/unbound"
log-time-ascii: yes
harden-glue: yes
harden-dnssec-stripped: yes
harden-below-nxdomain: yes
harden-referral-path: yes
qname-minimisation: yes
aggressive-nsec: yes
unwanted-reply-threshold: 10000000
prefetch: yes
prefetch-key: yes
rrset-roundrobin: yes
minimal-responses: yes
trust-anchor-signaling: yes
root-key-sentinel: yes
val-clean-additional: yes
serve-expired: yes
val-log-level: 1
ipsecmod-enabled: no
ipsecmod-hook:/usr/libexec/ipsec/_unbound-hook
python:
remote-control:
control-enable: yes
server-key-file: "/etc/unbound/unbound_server.key"
server-cert-file: "/etc/unbound/unbound_server.pem"
control-key-file: "/etc/unbound/unbound_control.key"
control-cert-file: "/etc/unbound/unbound_control.pem"
configを設定し終わったら、起動前に
unbound-checkconf
で文法チェックを行い、OKならば
systemctl start unbound
で起動します。
無事起動したら、DNS問い合わせができるか、PCからnslookupやdigで確認します。
dig www.google.com @192.168.0.2
dig www.google.com @2001:db8::2
ここまで動作確認ができたら、宅内ルーターのDHCP設定を開いて、DNSサーバーのアドレスを今回作ったサーバーに変更しましょう。
メーカーや機種ごとに設定箇所が違うのでお手持ちの製品マニュアルを参照願いたいですが、メジャーどころだとこの辺でしょうか。
BUFFALO:DNS解決が極端に遅くなり...
NEC:DNSサーバを設定する
ASUS:[WiFiルーター] DHCPサーバーの設定方法
さらにスピードを追い求めるチューニング
上記の「performance.conf」でスレッド数・セッション数を相当上げているので、そのままではOSデフォルトのポートオープン数制限(ファイルディスクリプタ上限)に当たってしまいます。
対策として、unboundデーモンの起動時に上限値を変更します。
AlmaLinuxの場合、unboundの起動ユニットファイルは /lib/systemd/system/unbound.service になります。
ファイルをエディタで開き、[Service] セクションに下記の一行を追記します。
ExecStartPre=/bin/ulimit -HSn 65536
「デーモンを起動する前に、unboundプロセスのファイルディスクリプタ上限を65536に変更する」というコマンドです。
[Service] セクション全体はこうなります。
[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/unbound
ExecStartPre=/bin/ulimit -HSn 65536
ExecStartPre=/usr/sbin/unbound-checkconf
ExecStartPre=/bin/bash -c 'if [ ! "$DISABLE_UNBOUND_ANCHOR" == "yes" ]; then /usr/sbin/unbound-anchor -a /var/lib/unbound/root.key -c /etc/unbound/icannbundle.pem -f /etc/resolv.conf -R; else echo "Updates of root keys with unbound-anchor is disabled"; fi'
ExecStart=/usr/sbin/unbound -d $UNBOUND_OPTIONS
ExecReload=/usr/sbin/unbound-control reload
ユニットファイルを変更したので、内容を反映してunboundを再起動します。
systemctl daemon-reload
systemctl restart unbound
自作DNSサーバーの力は……
namebenchというDNSベンチマークソフトで、自作キャッシュDNSサーバーとGoogle DNS、Cloudflare DNSのスピード比較をしてみました。
その結果がこちらの画像です。
性能差がスコアで現れると気持ちがいいですね~。