
gopassivedns
基于网络抓包的DNS日志记录工具(Go语言实现)
摘要
一款基于网络抓包的DNS日志记录器,灵感来源于 https://github.com/gamelinux/passivedns。它使用 gopacket 处理 libpcap 和数据包解析,输出 JSON 格式的日志。旨在应对高流量查询捕获场景,适用于从一台到数百台 DNS 解析器的环境。
为什么不直接用 gamelinux 的 PassiveDNS?
它是个不错的选择。我开发这个工具是因为,我认为处理大量不可信数据且包含大量文档不完善的边界情况的任务,应该由托管运行时来管理,以防止内存损坏类攻击。我曾在多个组织中部署过 PassiveDNS,而构建 gopassivedns 是为了解决我观察到的几个具体痛点:我需要监控大量位置,需要扩展存储层以处理海量查询,并且希望有一个测试套件能全面覆盖各种 DNS 边界情况。
为什么不直接用 Bro(或其它 DNS 日志记录 IDS)?
同样是不错的选择。像 Bro 这样的系统通常部署在网络出口,这会导致查询的真实来源被递归解析器掩盖。这意味着你通常需要在 Bro 上部署,并启用解析器的查询日志(假设可行),然后将两者的日志集成到中央日志系统中,以便将查询追溯到客户端。而 gopassivedns 的设计目标是部署在你的解析器上(无需修改解析器配置)和/或网络出口上,通过可靠协议集中记录日志,并能简单地解析到任何日志系统中。
为什么不直接用解析器自身的查询日志?
解析器对查询日志(包括问题与答案)的支持参差不齐。最常用的 DNS 服务器之一 BIND 根本不支持这一功能。其他如 Windows DNS 的日志格式则非常糟糕。此外,基于网络的日志记录能够捕获客户端直接发送到远程服务器(例如 Google DNS)的查询。
用法
配置选项可以通过环境变量、.env 文件或命令行指定。优先级为:命令行参数 > .env 文件 > 已定义的环境变量。配置选项如下:
- -dev [设备] 用于捕获的网络设备(环境变量:PDNS_DEV)
- -fluentd_socket [套接字] Fluentd Unix 套接字路径,用于以 messagepack 格式记录日志(环境变量:PDNS_FLUENTD_SOCKET)
- -bpf [BPF 过滤器] BPF 捕获过滤器(默认:port 53)(环境变量:PDNS_BPF)
- -pcap [文件] 要处理的 pcap 文件(环境变量:PDNS_PCAP_FILE)
- -logfile [文件] DNS 查询日志文件(建议仅用于小型部署或调试)(环境变量:PDNS_LOG_FILE)
- -logMaxAge 日志文件在轮转前的最大保留天数(默认:28)(环境变量:PDNS_LOG_AGE)
- -logMaxBackups 轮转后保留的最大文件数(默认:3)(环境变量:PDNS_LOG_BACKUP)
- -logMaxSize 日志文件在轮转前的最大大小,单位 MB(默认:100)(环境变量:PDNS_LOG_SIZE)
- -quiet 不将 DNS 查询记录到 STDOUT(环境变量:PDNS_QUIET)
- -debug 启用调试日志输出到 STDOUT(环境变量:PDNS_DEBUG)
- -gc_age [数值] 不完整连接被垃圾回收的年龄(默认:-1m)(环境变量:PDNS_GC_AGE)
- -gc_interval [数值] 连接表垃圾回收的运行间隔(默认:3m)(环境变量:PDNS_GC_INTERVAL)
- -kafka_brokers [代理] Kafka 代理列表,用逗号分隔(环境变量:PDNS_KAFKA_PEERS)
- -kafka_topic [主题] 用于日志记录的 Kafka 主题(环境变量:PDNS_KAFKA_TOPIC)
- -cpuprofile [文件] 启用 CPU 性能分析(环境变量:PDNS_PROFILE_FILE)
- -numprocs [数值] 用于解析数据包的 goroutine 数量(默认:8)(环境变量:PDNS_THREADS)
- -pfring 使用 PF_RING 进行数据包捕获(环境变量:PDNS_PFRING)
- -statsd_host statsd 服务器的主机和端口(例如 localhost:8125)(环境变量:PDNS_STATSD_HOST)
- -statsd_interval 发送到 statsd 的间隔(秒)(环境变量:PDNS_STATSD_INTERVAL)
- -statsd_prefix 度量名称前缀(默认为 gopassivedns)(环境变量:PDNS_STATSD_PREFIX)
- -snaplen [整数] pcap 缓冲区使用的 snaplen
- -name 该传感器的名称,用于统计和日志消息(默认为主机名)(环境变量:PDNS_NAME)
- -syslog_facility syslog 设施(环境变量:PDNS_SYSLOG_FACILITY)
- -syslog_priority syslog 优先级(环境变量:PDNS_SYSLOG_PRIORITY)
必须提供 -dev 或 -pcap 参数。
关于 goroutine 与标准守护进程方式存在已知问题(https://github.com/golang/go/issues/227),因此强烈建议使用 http://stackoverflow.com/questions/10067295/how-to-start-a-go-program-as-a-daemon-in-ubuntu 中详述的某种方法,利用系统工具将此进程作为守护进程运行。
如果选择使用 syslog 日志记录,我们使用 Go 的 "log/syslog" 包,它需要一个 Unix 套接字与 syslog 通信,该套接字必须位于 /dev/log、/var/run/log 或 /var/run/syslog 之一。
部署指南
应将其部署在何处?
你有三种选择:部署在你的解析器上、部署在你的网关上,或者两者都部署。部署在解析器上的好处是,你可以获取发送原始请求的客户端的 IP 地址。你还可以看到请求的上游段(从解析器到链中的下一个解析器),除非你调整 BPF 过滤器忽略该段。部署在网关上则意味着你看不到客户端到内部解析器的段,因此难以将请求与特定客户端关联。但另一方面,你会看到绕过内部解析器的请求。当然,你也会看到从解析器到其上游解析器的查询。在理想情况下,我会将此工具部署在每个内部解析器上,并在网关的流量监听点上部署。内部解析器使用 BPF 过滤器忽略查询的上游段,而网关则不忽略任何内容。
应该如何处理结果?
目前,建议使用 logstash 将日志发送到 elasticsearch 集群。所有日志均为 JSON 格式,因此这非常容易。我还建议使用 HDFS 进行长期存储和批量分析。DNS 查询是极好的内部数据来源!
构建与安装
- 克隆此仓库
- 安装 libpcap、libpcap-dev
- 执行 'go get'
- 执行 'go build -o gopassivedns'(-o 参数主要是为了谨慎,假设你已克隆仓库,通常不需要)
- 执行 'cp gopassivedns /some/path/to/gopassivedns'