knot-resolver に乗り換えた

Unbound を使っていた

vnet jail で ipfw を動かして外への gateway にして、cache DNS server として unbound を動かしていた。

Unbound が死ぬ

難儀なのが、この Unbound が時々異常終了することである。 何が効いたか分からないが、最近は jail を道連れにするようになってかなり困っていた。 私一人なら jail を起動しなおせば済むのだが、 「実家ではインターネットの通信が切れることなんてまずないのに、なんでこのうちはしょっちゅう切れるの?」 と言われるとつらい。 つらいだけでなく、趣味で gatewayFreeBSD host にしているのをやめるところまで追い込まれるかもしれない。

Unbound が死ぬ時のログはこんな感じである。

Jun 14 08:51:11 gate unbound[51935]: [51935:0] notice: sendto failed: No buffer space available
Jun 14 08:51:11 gate unbound[51935]: [51935:0] notice: remote address is 2001:0DB8::53 port 53

前は月に1回くらいであったが、ここのところ、ログからすると 5/27, 5/31, 6/2, 6/6, 6/8, 6/14 と切れている。 外部とつながる cache DNS server が死んだら実質使えなくなるし、gateway の jail が落ちた日には通信が遮断されるので、 そりゃユーザーとしては頭にくるであろう。

原因がはっきりしないなりに対策はした

”sendto failed"、"No buffer space available" というのを手がかりに、sysctl の kern.ipc.maxsockbuf の値を増やしてみたり、 kern.maxfiles と kern.maxfilesperproc を増やしてみたり。 でまあ、効き目はあまりなかった。2日で落ちていたのが、6日保つようになったくらい。

検索してもそれらしい話には当たらない。

https://forum.netgate.com/topic/144487/unbound-and-traffic-shaping-cause-sendto-failed-no-buffer-space-available では "traffic shaper" が悪さをしているという話になっているので、同じ jail の中で ipfw を動かしているのがあかん可能性はあるけど、traffic shaping にあたることはしていない。

Pfsense on Esxi--sudden loss in dns resolving : PFSENSE にも似た話はあるが、コメントに "No real solution was found." とかあって泣く。

解決策がみつからないとはいえ、上記2例を見るに、Unbound に問題があると言ってもよさそうである。 ならば cache DNS server を変えてみるという手はあるだろう。

knot-resolver

knot-resolver を試すことにした。

Authoritative DNS server と比べて、cache DNS server には検索して出てくる選択肢が少ない。 私が見つけられたのは、BIND、Unbound、knot-resolver だけである。 というわけで、試すのは自動的に knot-resolver になる。 こいつが駄目なら、古きよき BIND に還ることになる。

sysctl.conf の設定

起動するといきなり、kern.maxfilesperproc を 524288 以上にしろとかいうメッセージが出るので、

kern.maxfiles=1048576
kern.maxfilesperproc=524288

に設定する。host 側で設定すると jail にも効くので、host の /etc/sysctl.conf に書く。

kresd を rc.d で制御する

FreeBSD ports にある knot-resolver は、2020/06/21 現在は rc.d スクリプトをインストールしてくれないので、 自分で作成する。

まず /var/run/kresd を owner kresd で作成し、/usr/local/etc/rc.d/kresd を次の内容で作成する。

#!/bin/sh
#
# PROVIDE: kresd
# REQUIRE: SERVERS cleanvar
# KEYWORD: shutdown
#
# Add the following lines to /etc/rc.conf to enable kresd:
#
# kresd_enable="YES"
# optional:
# kresd_config="/usr/local/etc/knot-resolver/kresd.conf"

. /etc/rc.subr

name=kresd
rcvar=kresd_enable

load_rc_config ${name}

: ${kresd_enable:="NO"}
: ${kresd_config:="/usr/local/etc/knot-resolver/kresd.conf"}
: ${kresd_rundir:="/var/run/kresd"}

command=/usr/sbin/daemon
procname=/usr/local/sbin/${name}
pidfile=/var/run/${name}/${name}.pid

command_args="-fS -T ${name} -p ${pidfile} ${procname} -c ${kresd_config} -n ${kresd_rundir}"
required_files=${kresd_config}

run_rc_command "$1"

これであとは、/etc/rc.confkresd_enable="YES" を入れればよい。

現状

とりあえず、1週間は落ちることなしに動いた。 次は1ヶ月を越えて動き続けてくれるかである。

追記 (2020/06/23)

デフォルトの kresd.conf だとローカルホストからしか引けない。 さらに、現在の FreeBSD ports&packages の設定だと、更新時に kresd.conf を上書きする。 そういうわけで、設定ファイルを /usr/local/etc/knot-resolver/kresd-gate.conf に用意する。 /etc/rc.conf の記述は次のようになる。

kresd_enable="YES"
kresd_config="/usr/local/etc/knot-resolver/kresd-gate.conf"

この kresd-gate.conf の記述については (knot-resolver のドキュメント)https://knot-resolver.readthedocs.io/en/stable/index.html を参考に書けばよいが、私の設定の概略は以下のような感じになる。

-- Network interface configuration
-- 内部ネットワークは IPv4
-- 内部ネットワーク向けには DNS over TLS もサービスする
net.listen('127.0.0.1', 53, { kind = 'dns' })
net.listen('192.0.2.1', 53, { kind = 'dns' })
net.listen('192.0.2.1', 853, { kind = 'tls' })
net.listen('::1', 53, { kind = 'dns', freebind = true })

net.outgoing_v6('2001:db8:a::1')
net.outgoing_v4('192.0.2.1')

home_trees = policy.todnames({
        'home.example.jp.',
        '2.0.192.in-addr.arpa.'
})
policy.add(policy.suffix(
        -- DNSSEC の verification を行わないので、FORWARD ではなく STUB
        policy.STUB('192.0.2.53), home_trees
))
policy.add(policy.all(policy.FORWARD({
        '2001:db8:b:53',
        '198.51.100.53'
})))

-- Load useful modules
modules = {
        'hints > iterate',  -- Load /etc/hosts and allow custom root hints
        'stats',            -- Track internal statistics
        'predict',          -- Prefetch expiring/frequent records
}

-- ユーザ権限を root から一般ユーザーに落とす
user('kresd', 'kresd')

-- Cache size
cache.size = 100 * MB