Anycast IP被污染是DDoS防护中最棘手的场景之一——当攻击者通过BGP劫持、路由泄漏或大规模流量牵引导致你的Anycast节点IP在全球路由表中被"污染"后,正常用户访问会被错误引导到被攻击节点或黑洞路由,业务直接中断。快速切换的核心逻辑只有一句话:在检测到IP污染的第一时间,将流量牵引到备用Anycast IP或备用清洗中心,同时通过BGP宣告策略把污染路由覆盖掉。下面我把这套实践从头到尾拆开讲清楚。
一、Anycast IP污染到底是怎么发生的
Anycast的本质是同一个IP地址在全球多个节点同时宣告,BGP根据最近跳数把流量引到最近的节点。但这套机制有个天然弱点:一旦某个节点被大流量DDoS打穿,或者攻击者通过BGP劫持把你的IP前缀从合法节点"抢"走,全球路由表就会出现异常——你的IP被指向了攻击者控制的节点,或者被上游运营商直接黑洞掉。这就是所谓的"IP污染"。
常见的污染场景有三种:第一,攻击者通过BGP劫持宣告你的/24甚至更大前缀,导致部分地区流量被牵引到攻击者机房;第二,上游运营商为了自保,在检测到异常流量后直接对你的IP做null route,把流量全部丢弃;第三,你自己的某个Anycast节点被打穿后,BGP路由收敛异常,其他节点的路由权重被错误调整,流量分配失衡。不管哪种情况,结果都一样——用户访问不了你的服务。
二、快速切换前必须具备的基础设施
想在IP被污染后几分钟内完成切换,不是临时抱佛脚能做到的,你必须提前搭好以下几套东西:
第一,备用Anycast IP池。你至少要有一组完全独立的Anycast IP,这些IP在不同的ASN下宣告,跟主用IP没有任何路由关联。很多团队犯的错误是备用IP和主用IP在同一个ASN或者同一个上游,一旦上游出问题,备用IP同样被污染。正确做法是备用IP走完全不同的运营商和交换中心,比如主用走电信+联通,备用走移动+教育网+海外运营商。
第二,多清洗中心部署。你的流量清洗能力不能只集中在一个地方。至少要在两个以上地理区域部署清洗节点,每个节点都能独立处理流量。这样即使一个区域的Anycast IP被污染,另一个区域的节点还能正常工作。
第三,自动化切换系统。人工切换在DDoS场景下根本来不及,你需要一套能自动检测IP污染、自动触发BGP切换、自动更新DNS解析的编排系统。后面我会给出具体的自动化脚本思路。
三、IP污染的快速检测机制
切换的前提是发现得快。你需要从三个维度同时监控:
BGP路由监控:通过RouteViews、RIPE RIS或者商业BGP监控服务,实时观察你的IP前缀在全球各大交换点的宣告情况。一旦发现异常ASN在宣告你的前缀,或者你的前缀突然从某个交换点消失,立刻告警。检测脚本可以这样写:
import requests
import json
def check_bgp_announcement(prefix):
url = f"https://api.bgpview.io/v1/prefix/{prefix}/announcements"
resp = requests.get(url, timeout=10)
data = resp.json()
asns = set()
for ann in data.get("data", {}).get("announcements", []):
asns.add(ann.get("asn"))
return asns
# 主用IP前缀
primary_asns = check_bgp_announcement("203.0.113.0/24")
# 备用IP前缀
backup_asns = check_bgp_announcement("198.51.100.0/24")
if len(primary_asns) > 5 or 64512 in primary_asns:
print("ALERT: Primary IP may be hijacked or polluted!")
流量质量监控:在每个Anycast节点部署流量采样,如果某个节点的流量中攻击流量占比突然飙升到80%以上,或者正常流量断崖式下跌,说明该节点大概率被污染或被打穿。
用户可达性探测:从全球多个探测点(至少覆盖国内主要运营商和海外主要区域)对你的服务IP做HTTP/TCP探测,一旦多个探测点同时出现超时或连接重置,基本可以确认IP层面出了问题。
四、快速切换的具体操作流程
确认IP污染后,切换流程按以下步骤执行,目标是在5分钟内完成:
步骤一:立即撤回污染IP的BGP宣告。通过你的路由控制平台(比如自建的BGP管理系统或者与上游的API对接),对被污染的IP前缀发送withdraw消息。这一步是为了防止更多流量被错误引导。
步骤二:同时宣告备用Anycast IP。在备用IP对应的所有节点上,立即发起BGP宣告,把备用IP的路由推送出去。注意,宣告的时候要控制路由前缀长度,建议用/24而不是更大的前缀,避免被上游过滤。同时可以适当调高LOCAL_PREF值,让备用路由优先被选中。
步骤三:更新DNS解析。如果你的业务域名是通过DNS解析到Anycast IP的,需要立刻把DNS记录中的A记录或CNAME指向备用IP。这里有个关键技巧:把DNS TTL提前设成60秒甚至30秒,这样切换后生效速度快。如果你用的是智能DNS或者HTTPDNS,切换会更快,因为不依赖本地DNS缓存。
步骤四:流量牵引验证。切换完成后,立刻从各个探测点验证备用IP的可达性和流量质量。确认攻击流量是否被正确牵引到清洗中心,正常业务是否恢复。
整个流程如果是自动化的,可以压缩到2-3分钟。下面是一个简化的自动化切换逻辑:
import time
import subprocess
def emergency_switch(polluted_prefix, backup_prefix, dns_zone):
# 1. Withdraw polluted prefix
subprocess.run(["birdc", "configure", f"withdraw route {polluted_prefix}"])
# 2. Announce backup prefix on all nodes
for node in backup_nodes:
subprocess.run(["ssh", node, f"birdc configure announce {backup_prefix}"])
# 3. Update DNS
update_dns_record(dns_zone, backup_prefix)
# 4. Verify
time.sleep(30)
if verify_reachability(backup_prefix):
print("Switch successful")
else:
print("Switch failed, escalate manually")
五、切换过程中容易踩的坑
第一个坑:备用IP的BGP宣告没有提前做好路由过滤。有些团队的备用IP从来没正式宣告过,等到紧急切换时才发现上游运营商对新前缀有过滤策略,宣告根本推不出去。解决办法是备用IP必须提前至少一周开始正常宣告和收流量,让路由表中有合法记录。
第二个坑:DNS切换后部分用户仍然访问旧IP。这是因为本地DNS缓存和运营商DNS缓存没过期。解决办法除了降低TTL,还可以在切换后通过HTTP 302重定向把旧IP的流量临时引到新IP,作为过渡手段。
第三个坑:切换后备用节点同样被攻击。如果攻击者知道你有备用IP,可能会同时对备用IP发起攻击。所以备用IP必须严格保密,不要在公开文档、WHOIS信息或者SSL证书中暴露。同时备用节点的清洗能力要跟主用节点对等。
第四个坑:BGP切换引起路由震荡。如果你在短时间内频繁withdraw和announce,会导致全球路由表震荡,反而影响恢复速度。建议切换时一次性完成,不要反复试探。
六、长期防护策略:不只是切换
快速切换是应急手段,真正的长期防护需要从架构层面解决问题:
采用多IP多ASN架构。不要把所有鸡蛋放在一个ASN里。核心业务至少用3个以上不同ASN的Anycast IP,分布在不同运营商和不同地理区域。这样即使一个ASN被污染,其他ASN的IP还能正常工作。
部署实时BGP异常检测系统。市面上有成熟的BGP监控方案,能在秒级发现路由劫持和异常宣告。把这个系统和你的自动切换系统打通,形成闭环。
定期做切换演练。每个季度至少做一次模拟IP污染的切换演练,验证备用IP、清洗中心、DNS系统、自动化脚本是否都能正常工作。很多团队的备用系统从来没测过,真到紧急时刻才发现各种问题。
与上游运营商建立快速沟通通道。提前和你的上游运营商签好应急协议,明确在IP被污染时他们能在多长时间内配合做路由调整。有些运营商的响应速度很慢,这会直接影响你的恢复时间。
七、总结
Anycast IP被污染后的快速切换,本质上是一场和时间赛跑的技术操作。核心要点就三条:提前准备好独立的备用IP和清洗中心、建立自动化的检测和切换系统、定期演练确保每个环节都能跑通。不要等到真被攻击了才想起来搭备用系统,那时候一切都晚了。把这套体系建好,你的DDoS防护能力会上一个完全不同的台阶。
