Debian 9 (Stretch)で Dnstap を利用した KnotDNS におけるクエリログの取得

## 1. KnotDNS / Dnstap とは
KnotDNS は　CZ-NIC 社によって開発された権威DNSサーバです.
Dnstap は パケットキャプチャを利用せずに高速にDNSクエリを取得出来る仕組み及びツールです.

KnotDNS はDNSクエリログを取得する機能を標準で持たないため, Dnstap を外部モジュールという形で呼び出す必要があります. Bind や Unbound では参考記事があったのですが, KnotDNS に関する記事は無かったので, 備忘録として残してみます.


## 2. 想定環境
```
- OS: Debian 9.3 (Stretch)
- Utilities (Main):
  - Gcc: 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)
  - G++: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
  - Fstrm: v0.4.0
  - Protobuf: v3.5.2
  - Protobuf-c: v1.3.0
  - Golang: go1.10.3 linux/amd64

- Tools:
  - KnotDNS: v2.6.8
  - Dnstap: ? (Master Branch)
```

上記環境を構築していきます. 今回の作業は全て root ユーザで行います.
<b> ※ 但し, OSに関しては予め展開されている事を想定しています.</b>

## 3. 環境構築
<b> ビルド用ツールの展開 </b>

```
# apt install -y tar wget curl unzip git vim resolvconf
# apt install -y git-core make gcc g++ libtool \
                 autoconf pkg-config liburcu-dev liblmdb-dev \
                 libgnutls28-dev libedit-dev libidn11-dev libevent-dev
```
<b> Fstrm の展開 </b>

```
# git clone -b v0.4.0 https://github.com/farsightsec/fstrm.git /opt/fstrm
# cd /opt/fstrm
# ./autogen.sh
# ./configure 
# make
# make install
# ldconfig
```

<b> Protobuf の展開 </b>
　Protobuf 3.6.x はビルドエラーにより断念, 代替として v3.5.2 を利用した

```
# git clone -b v3.5.2 https://github.com/google/protobuf.git /opt/protobuf
# cd /opt/protobuf
# ./autogen.sh
# ./configure
# make
# make install
# ldconfig
```

<b> Protobuf-c の展開 </b> 

```
# git clone -b v1.3.0 https://github.com/protobuf-c/protobuf-c.git /opt/protobuf-c
# cd /opt/protobuf-c
# ./autogen.sh
# ./configure
# make
# make install
```

<b> KnotDNS の展開 </b>

```
# git clone -b v2.6.8 https://gitlab.labs.nic.cz/knot/knot-dns.git /opt/knot
# cd /opt/knot
# autoreconf -if
# ./configure \
  --sysconfdir=/etc
  --localstatedir=/var/lib
  --bindir=/usr/sbin
  --libexecdir=/usr/sbin
  --sbindir=/usr/sbin
  --with-rundir=/var/run/knot
  --with-storage=/var/lib/knot
  --enable-dnstap
  --with-module-dnstap=yes
  --enable-fastparser
  --enable-recvmmsg=yes
  --disable-silent-rules
  --disable-static
  --disable-documentation
# make
# make install
# ldconfig
```

<b> KnotDNS の Systemd 設定 </b>

```
# cat << EOS > /lib/systemd/system/knot.service
[Unit]
Description=Knot DNS server
Wants=network-online.target
After=network-online.target

[Service]
EnvironmentFile=/etc/default/knot
ExecReload=/usr/sbin/knotc reload
ExecStart=/usr/sbin/knotd $KNOTD_ARGS
Restart=on-abort

[Install]
WantedBy=multi-user.target
EOS

# systemctl daemon-reload
# systemctl status knot.service
```

<b> Golang の展開 </b>

```
# wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz -P /tmp
# cd /tmp
# tar -zxvf go1.10.3.linux-amd64.tar.gz
# mv go /usr/local/bin
```

<b> Golang の有効化 </b>
　Golang におけるルートプロジェクトを `/root/dnstap-golang` に設定していますが,
　本番環境では適切なディレクトリに設定すべきです

```
# mkdir -p /root/dnstap-golang
# echo "export GOROOT=/usr/local/bin/go" >> ~/.bash_proifle
# echo "export GOPATH=/root/dnstap-golang" >> ~/.bash_profile
# echo "export PATH=$GOPATH/bin:$GOROOT/bin:$PATH" >> ~/.bash_profile
# source ~/.bash_profile
```

<b> Dnstap の展開 </b>

```
# go get -u github.com/dnstap/golang-dnstap/dnstap
```

## 4. KnotDNS の初期起動及び設定

```
# knotc conf-init
# systemctl start knot.service
# systemctl status knot.service
```

## 5. KnotDNS への Dnstap の設定
　現在の KnotDNS では静的な設定ファイルからの Dnstap モジュール読込が何故か出来ないようです.
　なので, Dnstap 有効化の設定はCLI上から一つ一つ行う必要があります.

```
# knotc conf-begin
# knotc conf-set mod-dnstap.id capture_all
# knotc conf-set mod-dnstap.sink unix://var/run/knot/dnstap.sock
# knotc conf-set template.id default
# knotc conf-set template.global-module mode-dnstap/capture_all
# knotc conf-diff
# knotc conf-commit
# knotc reload
```

## 6. Dnstap の起動
　dnstap を実行する事で, 以下のログを得る事が出来るはずです.
　また, dnstap は何らかの方法 (Systemd, Supervisorctl) を利用して, BackGround で実行した方が良さそうです.
　ちなみに, オプション `-y` はデバッグモードでの起動を意味しています.

```
# dnstap -u /var/run/knot/dnstap.sock -w /var/log/dnstap.log -y
dnstap: opened input socket /var/run/knot/dnstap.sock
dnstap.FrameStreamSockInput: accepted a socket connection
```

## 7. DNSリクエストの送信
　dnstap を維持した状態で下記コマンドを実行します.
　レコード未登録の場合でも, クエリログは閲覧出来るため, 適当にDNSへリクエストを送信します

```
# kdig @localhost www.hogehoge.com
# kdig @localhost www.example.com
```

## 8. Dnstap でのクエリログの確認

　mod-dnstap の標準オプションでは, リクエスト及びレスポンスの両方が出力されます.

```
# cat /var/log/dnstap.log

type: MESSAGE
identity: "stretch.localdomain"
version: "Knot DNS 2.6.8"
message:
  type: AUTH_QUERY
  query_time: !!timestamp 2018-07-16 08:46:37
  socket_family: INET6
  socket_protocol: UDP
  query_address: ::1
  query_port: 42718
  query_message: |
    ;; opcode: QUERY, status: NOERROR, id: 1116
    ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

    ;; QUESTION SECTION:
    ;www.example.com.   IN       A
---
type: MESSAGE
identity: "stretch.localdomain"
version: "Knot DNS 2.6.8"
message:
  type: AUTH_RESPONSE
  response_time: !!timestamp 2018-07-16 08:46:37
  socket_family: INET6
  socket_protocol: UDP
  query_address: ::1
  query_port: 42718
  response_message: |
    ;; opcode: QUERY, status: REFUSED, id: 1116
    ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

    ;; QUESTION SECTION:
    ;www.example.com.   IN       A
---
type: MESSAGE
identity: "stretch.localdomain"
version: "Knot DNS 2.6.8"
message:
  type: AUTH_QUERY
  query_time: !!timestamp 2018-07-16 08:46:40
  socket_family: INET6
  socket_protocol: UDP
  query_address: ::1
  query_port: 35661
  query_message: |
    ;; opcode: QUERY, status: NOERROR, id: 61958
    ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

    ;; QUESTION SECTION:
    ;www.hogehoge.com.  IN       A
---
type: MESSAGE
identity: "stretch.localdomain"
version: "Knot DNS 2.6.8"
message:
  type: AUTH_RESPONSE
  response_time: !!timestamp 2018-07-16 08:46:40
  socket_family: INET6
  socket_protocol: UDP
  query_address: ::1
  query_port: 35661
  response_message: |
    ;; opcode: QUERY, status: REFUSED, id: 61958
    ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

    ;; QUESTION SECTION:
    ;www.hogehoge.com.  IN       A
---
```

## 9. おまけ
　dnstap のオプションに `-q` を付けると以下のようなクエリログ結果を出力する事が出来ます.

```
# cat /var/log/dnstap.log
08:51:13.000000 AQ ::1 UDP 34b "www.hogehoge.com." IN A
08:51:13.000000 AR ::1 UDP 34b "www.hogehoge.com." IN A
08:51:19.000000 AQ ::1 UDP 33b "www.example.com." IN A
08:51:19.000000 AR ::1 UDP 33b "www.example.com." IN A
```

## 10. 所感
- CLI 上で動的にゾーンを変更出来るのはとても良い (予め定義した設定ファイルを読み込む事も出来る)
- ネットワークデバイスへの設定を彷彿とさせるようなコマンドの使用感が良い
- ドキュメントに一通り設定方法が書かれているが, 内容が割とざっくりなので読み解く必要があるのは辛い
- レートリミットの設定や Any リクエストを無効化する方法など, DNSを介した攻撃への対策も一通りある (気がする)
- RestAPI とかあったら良かったかな... PowerDNS を使えと言われればそれまでだけど...