NScheckerというDNS改竄検知ツール(slack通知も可能)を作っていて、メジャーバージョン v1.0.0をリリースしました。このリリースで使えるツールになりました。
GitHub - ichikaway/nschecker: DNS record changing detection tool with slack notification.
MacとLinuxの実行ファイルはこちらからダウンロードできます。
https://github.com/ichikaway/nschecker/releases
1ヶ月前に、このような記事を書きました。
この時は、対応するドメインが.com .net .jpのみでしたが、今回のメジャーバージョンリリースで全ドメインに対応しました。whoisで出てくるTLD権威サーバに登録されているNS情報はすぐに変更検知できるようになっています。
任天堂の宮本さんの「アイディアというのは複数の問題をいっぺんに解決することだ」というのが印象に残っています。
今回のリリースはそんな感じにできたと思います。詳細は下記をご覧ください。
全ドメイン対応の工夫
TLD権威サーバに登録されているNS情報はTTL指定できず最大48時間キャッシュの影響を受けます。これを回避するためには、TLD権威サーバから直接DNS情報を取得する必要があります。
前回のブログ記事で、.com .net .jpのみ対応としていたのは、このTLD権威サーバのサーバ名をコードにベタ書きしていたからです。次のようなコードで。
servers["com"] = "m.gtld-servers.net" servers["net"] = "m.gtld-servers.net" servers["jp"] = "a.dns.jp"
全ドメインに対応するには、下記の3パターンの候補がありました。
- 全ドメインのTLD権威サーバのリストをコードにベタ書きする
- 毎回DNS RootサーバからTLD権威サーバの情報を取得する
- RootサーバからTLD権威サーバの情報を取得しローカルキャッシュする
RootサーバからTLD権威サーバの情報を取得するのが無駄なパケットを送る気がしたので、全ドメインのTLD権威サーバリストを保持しようと考えていました。頻繁に変わる情報でもないですし、全部で7000サーバほどだったので。
IANAのDNS Rootサーバリストを取得して、NS情報だけ切り出したファイルも用意して実装していましたが、K1LoWさんとのツイッターのやりとりで別の方法に切り替えました。
権威サーバリストをnschecker内に保持してしまうなら、nscheckerのアップデート以外でそれを更新できる仕組みが欲しいですねー。
— k1LoW (@k1LoW) 2020年9月19日
個人的には「必要になった権威サーバだけ通常フローで問い合わせてローカルキャッシュ。キャッシュを消すタイミングは問い合わせ先のTTLに従う」とかにしますかね
たしかに新しいTLDが追加されたり、権威サーバが変更された場合、そのようなケースは頻繁に発生しないとしても一時的に問題が起こるケースがあり、全サーバのリストをメンテナンスしていくのは良いアイディアではありません。
K1LoWさんの要望から、「RootサーバからTLD権威サーバの情報を取得しローカルキャッシュする」方向で実装を検討し始めました。
RootサーバからTLD権威サーバの情報を取得しローカルキャッシュする
この方法は権威サーバリストを自前で更新する必要がなく、Rootサーバへの問い合わせも最小限に抑えられます。ただ、これを実装するとなるとキャッシュ時間のコントロールなど色々と面倒だなと感じていました。
Go言語は標準パッケージでローカルのDNSキャッシュサーバからNSの情報を取得するメソッド net.LookupNS() があります。もしかしてと思い、net.LookupNS("com")としてみたところ、goがローカルのDNSキャッシュサーバにcomの権威サーバを問い合わせて無事TLD権威サーバのリストが返ってきました。
つまり、Rootサーバへの問い合わせと、取得したTLD権威サーバのリストのキャッシュ管理はローカルのDNSキャッシュサーバにお任せでき、全てのドメインがこの仕組みで運用でき、NScheckerはその結果を元にTLD権威サーバ対して、求めたいドメインのNS情報(例えばvaddy.netのNS情報)を取得するDNSパケットを送るだけでよくなりました。
非常にシンプルに課題が解決できました! (複数の問題をいっぺんに解決できた感)
v1.0.0までにしたこと
前回のブログ記事を書いていた頃のv0.04からの差分
- NS情報の即時検知の全ドメイン対応
- 外部パッケージ依存をなくして全てGo標準パッケージのみの実装に
- goreleaserを利用してgithub releasesページからバイナリダウンロード
今後の課題
v1.0.0で一通り自分がやりたいことはやりきった感があります。車輪の再発明であるDNSパケットの実装が1番面白いポイントでした。
今後の予定は、
- 複数のドメインを並列に短時間でチェック
- ライブラリとしても使えるように内部APIの整備
- GitHub Actionsでリリースバイナリの生成
ぐらいかなと。
MX情報は引き続きDNSキャッシュサーバ(フルリゾルバ)の情報を参照しますが、こちらはMXレコードのTTLを自分で短くしておけばすぐに変更検知できると思います。