tcpdump でNSDのクエリログを取る

【環境】
CentOS 7.5
nsd-4.1.20-1.el7.x86_64


NSDDNSサーバ)だと、標準の機能ではクエリログを取得できない。
nsd-4.1.20 の時点では)

権威サーバでクエリログが必要なニーズが無いのかもしれないが、どれだけのクエリがあるのか調べたかったので、ログを取る方法を考えた。

で、思い付いたのが tcpdump でログを取ること。

#!/bin/bash

NIC="ens160"
DST_IP="192.168.1.20"
DST_PORT="53"
LOG_FILE="/var/log/nsd-query/query.log"
ROTATE_SIZE=10485760
REMOVE_RANGE=129600

# check log rotate
if [ ${ROTATE_SIZE} -lt $(ls -l ${LOG_FILE} | awk '{print $5}') ] ; then
  pkill -9 tcpdump
  mv ${LOG_FILE} ${LOG_FILE}"-"$(date "+%Y%m%d-%H%M%S")
fi

# remove old files
find $(dirname ${LOG_FILE}) -type f -name $(basename ${LOG_FILE})"*" -mmin +${REMOVE_RANGE} -delete

# start logger
if [ $(ps aux | grep tcpdump | grep -v 'grep' -c) -lt 1 ] ; then
  /usr/sbin/tcpdump -p -l -nn -tttt -i ${NIC} dst host ${DST_IP} and dst port ${DST_PORT} >> ${LOG_FILE} &
fi

このスクリプトは、以下のような動作を行う。

  • キャプチャするNIC、そのNICのIP、ポート番号を指定
  • クエリログの出力先を指定
  • クエリログをローテーションするファイルサイズ(バイト)の閾値を指定
  • 古いクエリログファイルを削除するための閾値(秒)を指定
  • クエリログのファイルのサイズが設定値を超えた場合、tcpdump プロセスをkill してログファイルをリネームしてmv
  • 指定秒より古いクエリログファイルがあれば削除
  • ps コマンドでtcpdump プロセスが見つからない場合だけ(重複起動をチェック)、ログ取得のためのtcpdump を実行

これを cron などで定期時刻すれば、重複実行を避けつつクエリログを取得し、ログファイルのローテーションを行う。
テキストファイルなので、cat(圧縮したものはzcat)で開き、適当な条件で grep する。


ログファイルのローテーションと言えば、logrotate が手軽だが、tcpdump をローテーションしようとすると、anacron がゾンビのように残ってしまい、うまくローテーションされなかった。

tcpdump にも、出力ファイルをローテーションするオプション(-G)があるが、tcpdump -w で生データを書き出す場合、-r オプションでしか読めず、gzip圧縮して出力場合は直接読み込めない。

圧縮して出力すると、いったん非圧縮ファイルへ書きだす必要があり、圧縮するのを止めようか迷ったが、容量圧迫のプレッシャーに勝てず断念。

また、tcpdump のローテーションでは、古いファイルを削除する仕組みが無い。

これらをうまく解決できず、シェルスクリプト側で無理やりローテーションするようにした。