挖掘宝贵DNS数据
域名系统(DNS)是互联网上最重要的协议。DNS名称服务器和解析器的分布式架构形成了一个弹性且高度可扩展的系统,自NSFNET早期以来基本保持不变。这是一个独特的解决方案,允许域名持有者管理自己的区域数据,同时仍连接到全球互联网域名分发层次结构中。
在安全背景下,了解客户端系统在DNS中查询的内容可以获取有关任何环境中正常与异常操作的宝贵信息。几乎所有组织都会在其环境中运行递归DNS服务器,并且内部客户端站点的几乎所有通信都可能先于一个或多个DNS查找请求。记录这些请求和响应必须被视为必要措施,因为它代表了安全和运营分析的宝贵数据来源。
话虽如此,记录数据的地方有些棘手。挑战在于,即使是中等规模的组织,大多数DNS名称服务器也会承受相当大的网络负载。它们不断受到请求的轰炸,并且正确地实现为多线程高性能代码。内部Microsoft域控制器名称服务器具有“启用调试日志记录”功能,但不建议保持启用状态。原因是持续的文件I/O操作写入日志数据会减慢速度。任何磁盘写入操作都会受到内核缓冲区的影响,并且需要等待I/O缓冲区刷新到磁盘。
互联网软件联盟的BIND服务器在配置中已有日志记录功能一段时间,但同样的问题适用。启用此功能会减慢速度,因为服务器将日志信息写入磁盘。对于大约有六个端点的小型网络(如我的家庭网络),您可能并不真正关心甚至不会注意到使用同步文件I/O记录DNS请求的惩罚。在较大的环境中,启用此类日志记录功能会影响整体服务器性能,损害内部用户。此外,在DNS代码线程中,如果需要在服务新DNS请求与将缓冲日志信息写入磁盘之间做出选择,DNS请求可能会优先服务,日志条目可能会丢失。
一个可能的解决方案是部署附加系统,例如Bro传感器或完整数据包捕获解决方案来监听网络数据。这通常涉及使用网络SPAN端口配置或DNS服务器上游的专用光分路器镜像数据。显然,这种解决方案涉及额外的费用和复杂性。为什么不直接从源DNS服务器收集数据?
DNSTAP
由Farsight Security提出的DNSTAP解决了I/O性能瓶颈,直接从DNS服务器本身产生宝贵的日志信息。DNSTAP使用Google的协议缓冲区实现,以灵活的二进制结构化日志格式记录查询和响应信息。
来自http://dnstap.info的架构图给出了DNSTAP逻辑组件的概念。简而言之,DNS数据本身的副本被编码,然后由发送方(DNS服务器)序列化为可靠的字节流。生成的序列化数据可以由接收器软件读取,并在需要时记录到文件中。
通过将DNSTAP字节流I/O与DNS服务器操作本身分离,解决了缓慢的日志记录I/O问题,并提供了很大的粒度,因为生成的数据是实际的DNS请求和响应本身。
DNSTAP可用于Bind、Unbound和Knot服务器实现。在本文的剩余部分,我将重点介绍BIND服务器实现。
在Wild West Hackin’ Fest听了Paul Vixie关于DNSTAP的演讲,并进行了一些额外研究后,我决定在我的家庭办公室网络中启用DNSTAP。在我的情况下,我运行一个基于Ubuntu 18.04的路由器网关和DNS服务器。
我发现的第一个问题是,最新的Ubuntu 18.04修补版BIND发行版没有编译DNSTAP代码。这将需要手动从源代码构建ISC BIND服务器。
在编译之前,需要安装适当源代码构建工具的Ubuntu系统。最好这不是最终安装的目标系统!
|
|
此外,DNSTAP依赖于Google协议缓冲区、协议文件编译器和相关的帧流工具。这些项目实际上可以从Ubuntu存储库中获得,尽管您可能选择编译它们以获取最新/最好的代码。使用Ubuntu存储库,我按以下方式安装了这些:
|
|
尽管帧流工具可以从Ubuntu的存储库中获得,但如果您想要最新的软件,可以考虑访问Farsight的GitHub存储库并从那里克隆源代码进行构建。
|
|
安装依赖项后,您应该下载BIND的最新源代码。我选择了Bind 9.14,因为它包含了DNSTAP代码,并且截至今天被认为是当前/稳定版本。
|
|
此时,您可能明智地移除Ubuntu BIND发行版,以免混淆您用于DNS的“named”二进制文件的版本。
|
|
捕获帧流
为了获得最佳结果,您希望BIND名称服务器将其日志信息写入UNIX套接字,然后使用帧流捕获程序读取该套接字,并以二进制协议缓冲区格式创建日志文件。随后,您可以使用BIND发行版中包含的“dnstap-read”程序读取二进制格式,并根据需要生成可读输出。
重要的是在BIND之前启动帧流捕获过程,以便为您创建UNIX套接字。我选择首先使用命令行选项进行实验,然后最终创建了一个systemd服务,以确保“fstrm_capture”二进制文件总是在BIND之前启动。我的可用日志磁盘空间有限,因此我决定每天轮换输出日志文件,使用86,400秒作为轮换/分割参数。
Systemd配置文件保存为“/lib/systemd/system/framestream.service”
如上所示,当此服务启动时,名为“dnstap.sock”的UNIX套接字在“/var/cache/bind”目录中创建。
配置BIND
BIND配置非常简单,只需要在名称服务器配置文件的“options”部分中添加两行配置。需要注意的是,DNSTAP配置选项包括可以记录的不同消息类型数据。在每个情况下,您可以选择指定“query”或“response”。如果您不指定“query”或“response”,则默认记录两者。
消息类型包括:
- Auth:从解析器接收的查询由权威名称服务器发送响应。这些查询/响应从权威名称服务器的角度。
- Client:从客户端(或存根解析器)发送到名称服务器的查询。名称服务器应执行递归查找并将响应发送回客户端。
- Resolver:从解析器的角度,从另一个解析器发送到权威名称服务器的查询。从解析器的角度,从权威名称服务器接收的响应消息。
- Forwarder:发送/接收并转发到/从下游和上游DNS服务器的DNS流量。
我决定在我的配置中专注于客户端和权威流量。包括解析器消息类型可能会有些嘈杂。您如何配置取决于您的目标。我的BIND配置文件“options”部分如下所示供参考。
读取和分析日志数据
ISC BIND发行版包括一个名为“dnstap-read”的程序,允许用户从二进制结构化协议缓冲区文件读取数据,并将其打印到屏幕。有几个有用的选项可以与“dnstap-read”一起使用。
如果您不指定任何选项,则“dnstap-read”将简单地读取当前日志并打印日志中所有相关的请求/响应,以及一个附加代码,指示记录的消息类型。从协议缓冲区定义文件中提取的消息类型可以是:
- AUTH_QUERY (AQ)
- AUTH_RESPONSE (AR)
- RESOLVER_QUERY (RQ)
- RESOLVER_RESPONSE (RR)
- CLIENT_QUERY (CQ)
- CLIENT_RESPONSE (CR)
- FORWARDER_QUERY (FQ)
- FORWARDER_RESPONSE (FR)
- STUB_QUERY (SQ)
- STUB_RESPONSE (SR)
- TOOL_QUERY (TQ)
- TOOL_RESPONSE (TR)
示例输出
有关额外详细信息,您可以使用“-p”打印完整的DNS消息,产生类似“dig”的输出,包含所有预期的详细信息。数据的YAML(-y)和十六进制转储(-x)形式也可用。在所有情况下,也包括上面显示的原始单行输出。
Python实现
自从阅读和学习所有关于DNSTAP的内容后,我采取了额外的步骤,启动了一个Python3实现,可以解析帧流日志,以与“dnstap-read”类似的方式读取数据,但另外产生一些统计信息。您可以在https://github.com/yoda66/DNSTAP-FrameStream-Python找到我的工作。
所有荣誉归功于Paul Vixie和Farsight Security团队,他们继续传播关于DNSTAP的信息,能够以可扩展的方式收集宝贵的DNS数据以进行进一步分析。