线上业务突然流量掉 0 ?一次 DNS 污染排查与自救实录
背景:上周三凌晨,运维群里突然炸了锅——我们负责的一个电商小程序域名解析全部飘到 185...*,业务直接 404。从报警到恢复用了 47 分钟,把过程拆出来,给同样踩坑的同行留个脚印。
一、先确认是不是 DNS 被污染
# 1. 本地 dig 看权威结果
dig @8.8.8.8 api.example.com +short
# 2. 多节点横向对比(我常用 17ce)
curl -s "https://api.17ce.com/dns?host=api.example.com" | jq '.data[] | .ip'
如果 1 和 2 的 IP 对不上,基本可以断定解析被篡改。我们当时 60% 的节点返回了陌生段,且 TTL 被改到 300 秒,明显是劫持。
二、紧急止血:把域名迁进「托管型 DNS」
传统做法是自己改 NS,但生效要等全球递归缓存过期,太慢。我们临时把域名切到支持“一键 TTL 1 秒”的云解析,再把 A 记录指到备用入口。
# Cloudflare Terraform 片段,10 行代码完成迁移
resource "cloudflare_record" "api_a" {zone_id = var.cf_zone_idname = "api"type = "A"value = "203.0.113.55" # 备用源站ttl = 1 # 立即生效
}
Terraform apply 后 30 秒,劫持节点开始回落到正确 IP。但这个时候源站还是裸奔,下一步得把流量洗干净。
三、给业务套一层「替身入口」
劫持的根因是源站 IP 暴露,干脆让它“隐身”:把流量先打到支持 anycast 的清洗中心,再回源。
接入方式只有三步:
1)把 DNS 指向清洗中心 CNAME(例如 edge.xxx.com
)。
2)在控制台勾选「七层回源」,填真正的源站 IP。
3)打开「协议跟随」和「WebSocket 透传」,避免小程序长连接被切断。
# 源站 nginx 只接受清洗段回源
geo $realip_range {default 0;203.0.113.0/24 1; # 清洗中心出口段
}server {listen 443 ssl;if ($realip_range = 0) { return 444; } # 其他 IP 直接拒绝real_ip_header X-Forwarded-For;...
}
这样就算攻击者再次污染 DNS,也只能拿到清洗中心的 IP,打不进来。
四、事后复盘:为什么还会被劫持?
- 域名注册局邮箱被盗,NS 被偷偷改掉——教训:把
.com
也加 DNSSEC,再把注册局锁打开。 - 源站之前为了调试方便,对外开过 80/443——教训:上线前一定做防火墙最小化。
五、附录:一键检测脚本(可直接跑)
#!/usr/bin/env bash
domain=$1
echo "=== DNS 劫持快速检测 ==="
for ns in 8.8.8.8 1.1.1.1 223.5.5.5; doip=$(dig +short @$ns $domain | head -1)echo "$ns -> $ip"
done
把脚本丢进 Prometheus 的 blackbox_exporter,就能做成定时拨测。如果连续两次解析结果不一致,直接飞书报警。
结语
DNS 劫持本质是“信任链”被攻破:注册局、NS、递归 DNS、源站,任何一环松了都会出事。把域名托管到支持 DNSSEC、TTL 秒级生效的云解析,再把源站藏在“替身 IP”后面,至少能睡个安稳觉了。