1. 被动DNS数据库概述
被动DNS(Passive DNS)是一种通过监听网络流量中的DNS响应来构建历史解析记录数据库的技术。与传统的主动DNS查询不同,被动DNS不主动发起查询请求,而是被动记录网络中实际发生的DNS解析行为。
1.1 被动DNS的核心价值
被动DNS数据库在网络安全管理中具有不可替代的价值:
-
威胁情报分析:当发现某个恶意IP地址时,可以通过被动DNS数据库回溯查询该IP历史上绑定过的所有域名,从而揭示攻击者的完整基础设施网络。
-
网络资产测绘:提供真实、动态的互联网资产映射视图,远超主动扫描的覆盖面和时效性,是企业理清外部暴露面的重要工具。
-
异常行为检测:通过分析域名解析模式(如高频变更、短TTL等),可以发现潜在的恶意活动迹象。
1.2 被动DNS与传统DNS的区别
| 特性 | 被动DNS | 传统主动DNS |
|---|---|---|
| 数据采集方式 | 被动监听网络流量 | 主动发起查询 |
| 数据时效性 | 实时记录实际解析行为 | 仅获取查询时的解析结果 |
| 历史记录 | 保留完整历史解析记录 | 不保留历史记录 |
| 覆盖范围 | 仅限于被监听的网络流量 | 可以查询任何公开域名 |
| 隐私考虑 | 可能涉及用户隐私数据 | 仅获取公开DNS信息 |
2. 被动DNS系统架构设计
2.1 核心组件
一个完整的被动DNS系统通常包含以下核心组件:
- 传感器层:负责捕获网络中的DNS流量
- 数据处理层:对原始数据进行解析、标准化和富化
- 存储层:持久化存储处理后的DNS记录
- 查询分析层:提供数据查询和分析接口
2.2 数据采集原理
被动DNS传感器的部署位置至关重要,最佳实践是在递归DNS服务器的出口处部署传感器。这样能够捕获到完整的最终解析结果,包括经过CNAME重定向后的实际IP地址。
数据采集流程:
- 通过网络镜像或端口镜像获取DNS流量
- 过滤出DNS响应报文
- 解析DNS报文,提取关键字段
- 对数据进行标准化处理
- 将处理后的数据发送至存储系统
2.3 数据模型设计
被动DNS数据库中的每条记录通常包含以下核心字段:
- 时间戳:记录观察到该解析的时间
- 查询域名:被查询的完整域名
- 记录类型:DNS记录类型(A、AAAA、CNAME等)
- 应答数据:DNS查询的返回结果(IP地址或规范名)
- TTL:记录的生存时间
- 客户端IP(可选):发起查询的客户端地址(需考虑隐私问题)
3. 被动DNS系统实现
3.1 硬件与网络准备
3.1.1 网络环境配置
-
流量镜像设置:
- 在核心交换机上配置SPAN端口,将DNS服务器的进出流量镜像到传感器节点
- 建议使用专用网络接口卡(建议10Gbps或更高)来处理高流量负载
- 确保镜像流量不包含非DNS流量以减少处理负担
-
网络拓扑考虑:
- 传感器应部署在尽可能靠近递归DNS服务器的位置
- 生产环境建议使用专用网络分段来传输镜像流量
- 考虑网络冗余设计,避免单点故障导致数据丢失
3.1.2 服务器规格建议
根据网络规模的不同,硬件需求会有显著差异。以下是一个中等规模企业(日DNS查询量约1000万次)的配置建议:
-
传感器节点:
- CPU:8核以上(建议Intel Xeon Silver或同级)
- 内存:32GB
- 存储:500GB SSD(用于缓冲数据)
- 网卡:10Gbps专用网卡(建议Intel X710或同级)
-
数据处理与存储节点:
- CPU:16核以上
- 内存:64GB以上
- 存储:根据数据保留策略,建议至少10TB可用空间
- 建议使用RAID配置确保数据可靠性
3.2 软件组件选型
3.2.1 传感器软件选择
-
dnstap:
- 高性能DNS流量捕获工具
- 需要DNS服务器支持dnstap协议
- 适合大规模部署,资源消耗低
-
passivedns:
- 基于libpcap的经典被动DNS采集工具
- 支持多种输出格式(文件、Kafka、Redis等)
- 配置简单,适合中小规模部署
-
自研采集器:
- 基于DPDK或AF_PACKET的高性能实现
- 可以针对特定需求进行优化
- 开发维护成本较高
3.2.2 数据处理与存储方案
-
消息队列:
- Apache Kafka:高吞吐、分布式,适合大规模部署
- RabbitMQ:轻量级,适合中小规模部署
-
数据存储:
- Elasticsearch:强大的全文检索能力,适合实时查询
- ClickHouse:列式存储,分析性能极佳
- TimescaleDB:基于PostgreSQL的时序数据库扩展
-
图数据库(用于高级关联分析):
- Neo4j:成熟的图数据库解决方案
- JanusGraph:可扩展的分布式图数据库
3.3 部署实施步骤
3.3.1 基础环境搭建
-
操作系统准备:
bash复制# 以Ubuntu Server 22.04为例 sudo apt update && sudo apt upgrade -y sudo apt install -y build-essential libpcap-dev libldns-dev cmake git -
Docker环境配置:
bash复制# 安装Docker sudo apt install -y docker.io docker-compose sudo systemctl enable --now docker # 将当前用户加入docker组 sudo usermod -aG docker $USER newgrp docker
3.3.2 传感器部署
以passivedns为例的部署步骤:
-
编译安装passivedns:
bash复制git clone https://github.com/gamelinux/passivedns.git cd passivedns autoreconf --install ./configure make sudo make install -
测试运行:
bash复制# 监听eth0网卡,输出到控制台 sudo passivedns -i eth0 -l - # 生产环境建议输出到Kafka sudo passivedns -i eth0 -l - --kafka-server kafka:9092 --kafka-topic passivedns-raw -
系统服务化:
创建systemd服务文件/etc/systemd/system/passivedns.service:code复制[Unit] Description=PassiveDNS Sensor After=network.target [Service] ExecStart=/usr/local/bin/passivedns -i eth0 -l - --kafka-server kafka:9092 --kafka-topic passivedns-raw Restart=always User=root Group=root [Install] WantedBy=multi-user.target启用服务:
bash复制sudo systemctl daemon-reload sudo systemctl enable --now passivedns
3.3.3 数据处理流水线配置
使用Logstash处理Kafka中的数据并写入Elasticsearch:
-
Logstash配置文件 (
logstash.conf):ruby复制input { kafka { bootstrap_servers => "kafka:9092" topics => ["passivedns-raw"] codec => json } } filter { date { match => [ "timestamp", "ISO8601" ] target => "@timestamp" } mutate { rename => { "query" => "[dns][question][name]" } rename => { "type" => "[dns][question][type]" } rename => { "answer" => "[dns][answers][data]" } add_field => { "[@metadata][index_suffix]" => "passivedns-%{+YYYY.MM}" } } # 添加地理信息 geoip { source => "[dns][answers][data]" target => "geo" } } output { elasticsearch { hosts => ["http://elasticsearch:9200"] index => "passivedns-%{+YYYY.MM.dd}" } } -
Elasticsearch索引模板:
预先定义索引映射可以优化查询性能:bash复制curl -X PUT "http://localhost:9200/_index_template/passivedns" -H 'Content-Type: application/json' -d' { "index_patterns": ["passivedns-*"], "template": { "mappings": { "properties": { "@timestamp": { "type": "date" }, "dns.question.name": { "type": "keyword" }, "dns.question.type": { "type": "keyword" }, "dns.answers.data": { "type": "ip" }, "geo": { "properties": { "country_name": { "type": "keyword" }, "city_name": { "type": "keyword" }, "location": { "type": "geo_point" } } } } } } }'
4. 被动DNS数据分析与应用
4.1 基础查询功能实现
4.1.1 域名历史解析查询
通过Elasticsearch查询指定域名的所有历史解析记录:
python复制from elasticsearch import Elasticsearch
from datetime import datetime, timedelta
es = Elasticsearch(["http://localhost:9200"])
def query_domain_history(domain, days=30, size=1000):
"""
查询域名在指定时间范围内的所有解析记录
:param domain: 要查询的域名
:param days: 查询的时间范围(天)
:param size: 返回的最大记录数
:return: 解析记录列表
"""
time_threshold = datetime.utcnow() - timedelta(days=days)
query = {
"query": {
"bool": {
"must": [
{"term": {"dns.question.name": domain}},
{"range": {"@timestamp": {"gte": time_threshold.isoformat()}}}
]
}
},
"sort": [{"@timestamp": {"order": "desc"}}],
"size": size
}
try:
response = es.search(index="passivedns-*", body=query)
return [hit["_source"] for hit in response["hits"]["hits"]]
except Exception as e:
print(f"查询失败: {e}")
return []
# 使用示例
records = query_domain_history("example.com", days=7)
for record in records:
print(f"{record['@timestamp']} - {record['dns']['question']['type']} -> {record['dns']['answers']['data']}")
4.1.2 IP反向查询
查找特定IP地址曾经绑定过的所有域名:
python复制def query_ip_reverse(ip_address, days=30, size=1000):
"""
反向查询IP地址在指定时间范围内绑定过的所有域名
:param ip_address: 要查询的IP地址
:param days: 查询的时间范围(天)
:param size: 返回的最大记录数
:return: 域名列表
"""
time_threshold = datetime.utcnow() - timedelta(days=days)
query = {
"query": {
"bool": {
"must": [
{"term": {"dns.answers.data": ip_address}},
{"range": {"@timestamp": {"gte": time_threshold.isoformat()}}}
]
}
},
"aggs": {
"unique_domains": {
"terms": {"field": "dns.question.name.keyword", "size": size}
}
},
"size": 0
}
try:
response = es.search(index="passivedns-*", body=query)
return [bucket["key"] for bucket in response["aggregations"]["unique_domains"]["buckets"]]
except Exception as e:
print(f"查询失败: {e}")
return []
# 使用示例
domains = query_ip_reverse("1.2.3.4", days=90)
print(f"IP 1.2.3.4 曾经绑定过的域名: {', '.join(domains)}")
4.2 高级威胁检测
4.2.1 DGA域名检测
域名生成算法(DGA)通常会产生随机性很高的域名。我们可以通过计算域名的熵值来识别潜在的DGA域名:
python复制import math
from tldextract import extract
def calculate_entropy(string):
"""
计算字符串的香农熵
:param string: 输入字符串
:return: 熵值
"""
# 计算每个字符出现的频率
freq = {}
for char in string:
freq[char] = freq.get(char, 0) + 1
# 计算熵值
entropy = 0.0
length = len(string)
for count in freq.values():
probability = float(count) / length
entropy -= probability * math.log(probability, 2)
return entropy
def is_potential_dga(domain, threshold=3.5):
"""
判断域名是否可能是DGA生成的
:param domain: 要检查的域名
:param threshold: 熵值阈值
:return: True/False
"""
# 提取二级域名(去掉TLD部分)
subdomain = extract(domain).subdomain
if not subdomain:
return False
# 计算熵值
entropy = calculate_entropy(subdomain)
# 长度小于6的域名容易误报,可以排除
if len(subdomain) < 6:
return False
return entropy > threshold
# 使用示例
test_domains = ["google.com", "xkjwefgh123.example.com", "amazon.com", "asdfqwerzxc.example.org"]
for domain in test_domains:
print(f"{domain}: {'可能是DGA' if is_potential_dga(domain) else '正常'}")
4.2.2 Fast-Flux检测
Fast-Flux是一种恶意软件常用的技术,通过快速变更域名解析结果来逃避检测。我们可以通过分析域名的解析模式来识别:
python复制def detect_fast_flux(domain, hours=1):
"""
检测域名是否表现出Fast-Flux特征
:param domain: 要检查的域名
:param hours: 分析的时间窗口(小时)
:return: 检测结果和相关信息
"""
time_threshold = datetime.utcnow() - timedelta(hours=hours)
query = {
"query": {
"bool": {
"must": [
{"term": {"dns.question.name": domain}},
{"range": {"@timestamp": {"gte": time_threshold.isoformat()}}}
]
}
},
"aggs": {
"unique_ips": {
"cardinality": {"field": "dns.answers.data"}
},
"ttl_stats": {
"stats": {"field": "dns.answers.ttl"}
}
},
"size": 0
}
try:
response = es.search(index="passivedns-*", body=query)
unique_ips = response["aggregations"]["unique_ips"]["value"]
avg_ttl = response["aggregations"]["ttl_stats"]["avg"]
# Fast-Flux特征:短时间内大量不同IP,且TTL很短
if unique_ips > 10 and avg_ttl < 300: # 阈值可根据实际情况调整
return {
"is_fast_flux": True,
"unique_ips": unique_ips,
"avg_ttl": avg_ttl
}
return {
"is_fast_flux": False,
"unique_ips": unique_ips,
"avg_ttl": avg_ttl
}
except Exception as e:
print(f"检测失败: {e}")
return None
# 使用示例
result = detect_fast_flux("suspicious.example.com")
if result["is_fast_flux"]:
print(f"检测到Fast-Flux行为: 过去1小时内使用了{result['unique_ips']}个不同IP,平均TTL为{result['avg_ttl']}秒")
4.3 关联图谱分析
将被动DNS数据导入图数据库(如Neo4j)可以进行更复杂的关联分析:
4.3.1 数据模型设计
-
节点类型:
- Domain:域名节点
- IP:IP地址节点
- ASN:自治系统节点
- Registrar:注册商节点
-
关系类型:
- RESOLVES_TO:域名解析到IP
- BELONGS_TO_ASN:IP属于某个ASN
- REGISTERED_BY:域名由某个注册商注册
4.3.2 Neo4j查询示例
-
查找与恶意IP相关的所有域名:
cypher复制MATCH (ip:IP {address: '1.2.3.4'})<-[:RESOLVES_TO]-(domain:Domain) RETURN ip, domain -
查找共享IP的域名集群:
cypher复制MATCH (d1:Domain)-[:RESOLVES_TO]->(ip:IP)<-[:RESOLVES_TO]-(d2:Domain) WHERE d1.name = 'known-malicious.com' RETURN d1, ip, d2 -
查找通过CNAME链关联的资产:
cypher复制MATCH path=(start:Domain {name:'initial.com'})-[:RESOLVES_TO|CNAME_TO*]->(end) RETURN path
5. 系统优化与安全考虑
5.1 性能优化
5.1.1 数据采集优化
-
BPF过滤器:使用BPF过滤器减少不必要的数据处理
bash复制# 只捕获DNS响应包(端口53,且是响应包) sudo passivedns -i eth0 -l - -f "udp dst port 53 and udp[10] & 0x80 = 0x80" -
多线程处理:对于高流量环境,使用多线程或worker池处理数据包
python复制# 使用Python的concurrent.futures实现多线程处理 from concurrent.futures import ThreadPoolExecutor def process_packet(packet): # 包处理逻辑 pass with ThreadPoolExecutor(max_workers=4) as executor: executor.map(process_packet, packet_stream)
5.1.2 存储优化
-
数据分区策略:
- 按时间分区(如按月或按周)
- 按记录类型分区(A记录、CNAME记录等)
- 按顶级域名分区
-
压缩与归档:
- 对历史数据启用压缩(如Elasticsearch的index.codec: best_compression)
- 将冷数据归档到成本更低的存储(如S3、Glacier)
-
索引优化:
- 只为常用查询字段创建索引
- 使用复合索引优化多条件查询
- 定期执行索引优化(如Elasticsearch的_forcemerge)
5.2 安全与隐私
5.2.1 数据匿名化
-
客户端IP匿名化:
python复制import hashlib def anonymize_ip(ip_address, salt="your-unique-salt"): """对IP地址进行加盐哈希处理""" return hashlib.sha256((ip_address + salt).encode()).hexdigest()[:16] -
敏感域名过滤:
python复制def is_internal_domain(domain): """判断是否为内部域名""" internal_suffixes = ['.internal', '.corp', '.local', '.lan'] return any(domain.endswith(suffix) for suffix in internal_suffixes)
5.2.2 访问控制
-
Elasticsearch安全配置:
yaml复制# elasticsearch.yml xpack.security.enabled: true xpack.security.transport.ssl.enabled: true xpack.security.authc.api_key.enabled: true -
基于角色的访问控制(RBAC):
bash复制# 创建只读角色 POST /_security/role/read_only { "indices": [ { "names": ["passivedns-*"], "privileges": ["read", "view_index_metadata"] } ] } # 创建用户并分配角色 POST /_security/user/analyst { "password": "securepassword", "roles": ["read_only"], "full_name": "Security Analyst" }
5.2.3 合规考虑
-
数据保留策略:
bash复制# 设置ILM策略,自动删除过期数据 PUT /_ilm/policy/passivedns_policy { "policy": { "phases": { "hot": { "actions": { "rollover": { "max_size": "50gb", "max_age": "7d" } } }, "delete": { "min_age": "30d", "actions": { "delete": {} } } } } } -
审计日志:
yaml复制# elasticsearch.yml xpack.security.audit.enabled: true xpack.security.audit.logfile.events.include: "authentication_failed,access_denied,anonymous_access_denied"
6. 实际应用案例
6.1 恶意软件基础设施分析
通过被动DNS数据可以追踪恶意软件的控制服务器:
-
初始感染指标(IOC)获取:
- 从安全报告中获取已知的恶意域名或IP
- 在被动DNS数据库中查询相关记录
-
基础设施图谱构建:
- 查找恶意IP曾经绑定过的所有域名
- 分析这些域名的注册信息、解析模式等
- 识别可能的攻击者基础设施网络
-
时间线分析:
- 绘制恶意域名和IP的活动时间线
- 识别攻击活动的周期性或特定触发条件
6.2 内部威胁检测
被动DNS数据可以帮助发现内部网络中的异常行为:
-
数据外泄检测:
- 监控内部主机对可疑外部域名的解析请求
- 特别是解析频率异常或解析目标地理位置异常的请求
-
横向移动检测:
- 检测内部服务器域名被非常规IP地址解析的情况
- 这可能表明攻击者在网络内部进行横向移动
-
影子IT发现:
- 识别未经批准使用的云服务和外部应用
- 通过分析对这些服务域名的解析行为
6.3 供应链风险监控
通过被动DNS监控第三方供应商的域名解析行为:
-
供应商基础设施变更监控:
- 检测关键供应商域名解析IP的变更
- 及时发现可能的域名劫持或配置错误
-
子域名接管风险检测:
- 识别指向未使用云服务的CNAME记录
- 防止攻击者通过接管未使用的子域名入侵系统
-
证书透明度日志关联:
- 将被动DNS数据与证书透明度日志结合
- 发现新颁发的证书与异常解析行为的关联
7. 系统扩展与集成
7.1 与其他安全系统集成
7.1.1 SIEM集成
将被动DNS数据发送到SIEM系统(如Splunk、IBM QRadar)进行关联分析:
-
日志转发配置:
bash复制# 使用Filebeat将Elasticsearch数据发送到SIEM filebeat.inputs: - type: elasticsearch hosts: ["http://localhost:9200"] index: "passivedns-*" query: '{ "query": { "range": { "@timestamp": { "gte": "now-5m" } } } }' output.syslog: host: "siem.example.com" port: 514 protocol: "tcp" -
SIEM中的关联规则:
- 当检测到恶意IP时,自动查询被动DNS获取相关域名
- 当检测到可疑域名解析时,检查该域名的历史解析模式
7.1.2 威胁情报平台集成
将被动DNS数据与威胁情报平台(如MISP、ThreatConnect)集成:
-
IOC提取与共享:
python复制def extract_iocs_from_dns(domain, days=7): """从被动DNS数据中提取潜在的IOC""" records = query_domain_history(domain, days=days) iocs = { "ips": set(), "domains": set() } for record in records: iocs["ips"].add(record["dns"]["answers"]["data"]) if record["dns"]["question"]["type"] == "CNAME": iocs["domains"].add(record["dns"]["answers"]["data"]) return iocs -
自动提交到情报平台:
python复制import pymisp def submit_to_misp(iocs, event_id): """将IOC提交到MISP""" misp = pymisp.ExpandedPyMISP(MISP_URL, MISP_KEY, MISP_VERIFYCERT) for ip in iocs["ips"]: misp.add_ipdst(event_id, ip, comment=f"From passiveDNS for {domain}") for domain in iocs["domains"]: misp.add_domain(event_id, domain, comment=f"From passiveDNS for {domain}")
7.2 高级分析功能扩展
7.2.1 机器学习异常检测
使用机器学习算法识别异常的DNS解析模式:
-
特征工程:
- 域名特征:长度、熵、字符分布、n-gram频率
- 解析行为特征:解析频率、TTL分布、IP多样性、地理位置分布
- 时间特征:解析时间模式、周期性
-
模型训练:
python复制from sklearn.ensemble import IsolationForest import pandas as pd # 从Elasticsearch加载训练数据 def load_training_data(): query = { "query": { "range": { "@timestamp": { "gte": "now-30d" } } }, "size": 10000 } response = es.search(index="passivedns-*", body=query) return pd.DataFrame([hit["_source"] for hit in response["hits"]["hits"]]) # 特征提取 def extract_features(df): features = [] for _, row in df.iterrows(): domain = row["dns"]["question"]["name"] features.append({ "domain_length": len(domain), "entropy": calculate_entropy(domain), "num_dots": domain.count("."), "ttl": row["dns"]["answers"].get("ttl", 0), # 更多特征... }) return pd.DataFrame(features) # 训练异常检测模型 df = load_training_data() X = extract_features(df) model = IsolationForest(n_estimators=100, contamination=0.01) model.fit(X) -
实时检测:
python复制def detect_anomalies(domain_records): """检测异常的DNS记录""" features = extract_features(pd.DataFrame(domain_records)) scores = model.decision_function(features) return [score < -0.5 for score in scores] # 阈值可根据实际情况调整
7.2.2 图神经网络分析
使用图神经网络(GNN)分析域名-IP关联网络:
-
图构建:
- 节点:域名、IP、ASN等
- 边:解析关系、所属关系等
- 节点特征:域名字符特征、IP地理位置等
-
模型架构:
python复制import torch import torch_geometric class GNNModel(torch.nn.Module): def __init__(self, num_features, hidden_dim): super().__init__() self.conv1 = torch_geometric.nn.GCNConv(num_features, hidden_dim) self.conv2 = torch_geometric.nn.GCNConv(hidden_dim, hidden_dim) self.classifier = torch.nn.Linear(hidden_dim, 1) def forward(self, data): x, edge_index = data.x, data.edge_index x = self.conv1(x, edge_index).relu() x = self.conv2(x, edge_index).relu() x = torch_geometric.nn.global_mean_pool(x, data.batch) return self.classifier(x) -
应用场景:
- 恶意域名分类
- 攻击基础设施发现
- 异常关联检测
7.3 可视化与报告
7.3.1 Kibana仪表板
创建Kibana仪表板展示关键指标:
-
主要可视化组件:
- 解析请求时间序列
- 顶级域名分布
- 地理位置热图
- 异常解析活动警报
-
仪表板示例配置:
json复制{ "title": "PassiveDNS Monitoring", "panels": [ { "type": "timeseries", "title": "DNS Queries Over Time", "params": { "index": "passivedns-*", "timeField": "@timestamp", "metrics": [{"type": "count", "id": "query_count"}] } }, { "type": "pie", "title": "Top TLDs", "params": { "index": "passivedns-*", "size": 10, "field": "dns.question.name.keyword", "aggType": "terms" } } ] }
7.3.2 自定义Web界面
使用Python和Vue.js构建专用Web界面:
-
后端API(FastAPI):
python复制from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) @app.get("/api/domain/{domain}") async def get_domain_history(domain: str, days: int = 7): records = query_domain_history(domain, days=days) return {"domain": domain, "records": records} @app.get("/api/ip/{ip}") async def get_ip_reverse(ip: str, days: int = 30): domains = query_ip_reverse(ip, days=days) return {"ip": ip, "domains": domains} -
前端组件(Vue.js):
javascript复制// DomainHistory.vue export default { data() { return { domain: '', days: 7, records: [] } }, methods: { async fetchHistory() { const response = await axios.get(`/api/domain/${this.domain}?days=${this.days}`) this.records = response.data.records } } }
8. 维护与故障排除
8.1 日常维护任务
-
系统健康检查:
- 每日检查数据采集量是否在预期范围内
- 监控处理延迟和存储使用情况
- 验证备份完整性
-
数据质量监控:
python复制def check_data_quality(hours=24): """检查过去24小时的数据质量""" time_threshold = datetime.utcnow() - timedelta(hours=hours) query = { "query": {"range": {"@timestamp": {"gte": time_threshold.isoformat()}}}, "aggs": { "missing_fields": { "missing": {"field": "dns.answers.data"} }, "invalid_ips": { "scripted_metric": { "init_script": "state.invalid = 0", "map_script": """ try { def ip = doc['dns.answers.data'].value; if (ip == null || ip == '') { state.invalid++; } } catch (Exception e) { state.invalid++; } """, "combine_script": "return state.invalid", "reduce_script": "return states[0]" } } }, "size": 0 } response = es.search(index="passivedns-*", body=query) missing = response["aggregations"]["missing_fields"]["doc_count"] invalid = response["aggregations"]["invalid_ips"]["value"] total = response["hits"]["total"]["value"] return { "total_records": total, "missing_answer": missing, "invalid_ips": invalid, "quality_score": (total - missing - invalid) / total if total > 0 else 0 }
8.2 常见问题排查
8.2.1 数据采集问题
症状:传感器运行但未捕获数据
排查步骤:
- 确认网络镜像配置正确
bash复制# 检查网卡是否处于混杂模式 ip link show eth0 | grep PROMISC - 验证BPF过滤器是否正确
- 检查传感器日志是否有错误
- 测试手动捕获DNS流量
bash复制
tcpdump -i eth0 -n udp port 53 -c 10
8.2.2 数据处理延迟
症状:Kafka积压或Elasticsearch索引延迟
解决方案:
- 增加处理节点数量
- 优化Logstash管道配置
ruby复制input { kafka { consumer_threads => 4 } } filter { # 简化过滤逻辑 } output { elasticsearch { workers => 4 flush_size => 500 } } - 调整Elasticsearch批量写入参数
yaml复制# elasticsearch.yml thread_pool.write.queue_size: 1000