BGP多线主机抗D实战:ExaBGP + RTBH/FlowSpec 秒级牵引攻击流量,1分钟全自动黑洞
当机房防火墙被打满、家里带宽被抽干之前,你还有一条“逃生通道”——通过BGP协议直接向上游路由器下发黑洞路由或FlowSpec规则。本教程不是理论科普,是一套在BGP多线主机上落地可跑的DDoS防护方案。所有配置都在本人实测环境下通过,核心组件只有ExaBGP和不到100行的Python脚本。
为什么不在服务器上死磕 iptables,而要上升到 BGP 层?
BGP多线主机通常接入多个运营商,单机牵引带宽再大也扛不住多G的SYN Flood。网络层风暴未到网卡就已经被交换机打满,服务器内的任何手段都无效。正确姿势是让流量在“更靠近源头”的路由器上被丢弃。这就是RTBH(远程触发黑洞)和FlowSpec的用武之地。
- RTBH:把攻击目标IP的下一跳指向黑洞(通常为0.0.0.0/null0),该IP的所有入向流量在边缘被丢弃。优点是兼容性广,缺点是“全盘丢弃”,连正常流量也没了。
- FlowSpec:下发精确匹配规则,比如丢弃来自某个源的UDP流量或特定DSCP标记的包,比RTBH更细腻。但需要上游路由器开启BGP FlowSpec能力。
我们将在BGP多线主机上同时实现这两者。
环境准备
用一台轻云互联的BGP多线主机作为实验机,几个BGP公网IP,上游有至少1个能接收IPv4 Unicast和IPv4 FlowSpec的对等邻居。安装所需工具:
# CentOS / Ubuntu 均可
apt install -y python3-pip python3-psutil
pip3 install exabgp
第一步:ExaBGP 部署与基本配置
ExaBGP需要用进程守护,不需要root权限,但习惯上跑在`exabgp`用户下。这里是关键配置,我已删掉了不相关的参数。
cat > /etc/exabgp/exabgp.conf <<'EOF'
process blackhole {
run /opt/ddos/blackhole_handler.py;
encoder text;
}
neighbor 203.0.113.1 {
description "Upstream ISP";
router-id 10.0.0.1;
local-address 10.0.0.2;
local-as 65002;
peer-as 65001;
family {
ipv4 unicast;
ipv4 flowspec; # 如果上游不支持可以去掉,RTBH仍然可用
}
capability {
ipv4-flowspec;
}
}
EOF
上述配置中,process blackhole会在ExaBGP启动时拉起我们的管道脚本。关键文件在下一节。
第二步:自动RTBH黑洞,5秒内让你眼不见攻击包
思路是:一个monitor脚本周期采样`/proc/net/dev`,入向PPS超过阈值时,把攻击IP写入FIFO;blackhole_handler作为ExaBGP子进程,从FIFO读到IP后,立即向BGP邻居宣告黑洞路由。
2.1 创建FIFO和监控脚本
mkdir -p /opt/ddos
mkfifo /var/run/blackhole.fifo
chmod 666 /var/run/blackhole.fifo
monitor.py(5秒采样一次,超过80000 PPS触发):
#!/usr/bin/env python3
import time, os, sys
THRESHOLD_PPS = 80000
NET_DEV = '/proc/net/dev'
IF_NAME = 'ens4' # 换成你的网卡名
def read_rx_packets():
with open(NET_DEV) as f:
lines = f.read().strip().split('\n')[2:]
for line in lines:
if IF_NAME in line:
p_data = line.split()
return int(p_data[1].replace(':', '')) # Linux中的rxPackets是第1列
return 0
prev = read_rx_packets()
last_ts = time.time()
pending_ip = None # 简化:这里演示只记录一个IP。实际可从日志/连接数/abuse报告提取
while True:
time.sleep(5)
cur = read_rx_packets()
now = time.time()
pps = (cur - prev) / (now - last_ts)
if pps > THRESHOLD_PPS:
# 真实场景请从netstat/ss或硬中断数据中找出嫌疑IP
# 这里仅作为示例,写死一个受害IP
pending_ip = '198.51.100.10'
with open('/var/run/blackhole.fifo', 'w') as fifo:
fifo.write(pending_ip + '\n')
fifo.flush()
print(f'{now} Alert! PPS={pps:.0f}, blackhole {pending_ip}', flush=True)
# 下一次循环前重置
prev = cur; last_ts = now
else:
prev = cur; last_ts = now
2.2 blackhole_handler.py —— ExaBGP子进程,负责宣告路由
#!/usr/bin/env python3
import sys, os, threading, time
FIFO = '/var/run/blackhole.fifo'
BLACKHOLE_NH = '192.0.2.254' # 黑洞下一跳,通常用保留地址
BLACKHOLE_COMMUNITY = '65000:666'
def read_fifo_loop():
if not os.path.exists(FIFO):
os.mkfifo(FIFO)
with open(FIFO, 'r') as f:
while True:
ip = f.readline().strip()
if ip:
print(f'announce route {ip}/32 next-hop {BLACKHOLE_NH} community [{BLACKHOLE_COMMUNITY}]', flush=True)
print('DEBUG: sent blackhole for', ip, file=sys.stderr, flush=True)
if __name__ == '__main__':
# 保持进程存活
read_fifo_loop()
注意`python3`的`print`到stdout就是ExaBGP的控制命令。启动ExaBGP后,写入FIFO观察日志:
systemctl start exabgp
journalctl -u exabgp -f
echo "198.51.100.10" > /var/run/blackhole.fifo
如果看到`announce route 198.51.100.10/32 ...`并且邻居收到了,RTBH已经生效。可以通过ip route get在网关侧验证。
第三步:FlowSpec 精确拦截,只丢掉攻击流量
RTBH会断了所有服务,因此需要精确攻击特征的场景,Flowspec是更好的选择。ExaBGP可以直接通告这条流规则:
# 丢弃来自198.51.100.0/24的UDP流量(假设攻击是UDP放大)
exabgpcli "announce flow route { match { source 198.51.100.0/24; protocol udp; } then { discard; } }"
同样,你可以把这段逻辑嵌入到监控脚本中,比如检测到目标端口是53且UDP包速率极高,就只对UDP协议下发黑名单:
#!/usr/bin/env python3
# flow_trigger.py - 示例:当UDP入包超过阈值,针对源IP段下发FlowSpec丢弃
import time
from collections import defaultdict
# 简化版伪代码,真实环境可用DPDK/EBPF采集源IP
def build_flowspec(src_prefix):
return f'flow route {{ match {{ source {src_prefix}; protocol udp; }} then {{ discard; }} }}'
# 调用exabgpcli
def push_flowspec(rule):
import subprocess
subprocess.run(['exabgpcli', rule], check=True)
# 检测逻辑略,直接模拟一次
if __name__ == '__main__':
src = '198.51.100.0/24'
push_flowspec(build_flowspec(src))
FlowSpec的灵活度很大:可以匹配端口、协议、ICMP type、TCP flags等。具体字段名请参考RFC 5575,ExaBGP支持大部分字段。注意并非所有ISP都开启FlowSpec,需提前确认。
第四步:本地兜底 —— 防火墙与速率限制
无论BGP通道延迟多少,本地千万不要裸奔。推荐给关键端口加`hashlimit`,即使上游没把流量全丢掉,服务器也能靠该规则撑住第一波冲击。
iptables -A INPUT -p udp --dport 53 -m hashlimit --hashlimit-above 20/sec --hashlimit-burst 50 --hashlimit-mode srcip --hashlimit-name dns_flood -j DROP
这个规则对源IP限速20QPS,突发50,超过就丢。UDP 53端口放大攻击立刻见效。
效果验证与避坑
用hping3做压力演练:
# 攻击者机器上
hping3 -S --flood -p 80 -U 你的BGP多线主机IP
# 主机上观察
sar -n DEV 1 5 # 看rxPck/s有没有飙升
触发monitor.py后,几秒内上游邻居收到黑洞路由,攻击流量在源头被丢弃。你可以用`tcpdump -nn -i eth0 'host 攻击IP'`看到包速率急剧下降。
几个血泪坑:
- 黑洞路由的community值必须和上游运营商确认,最常见的666(黑洞)不一定每家通用。
- ExaBGP必须保持常驻,否则线路被攻击时你没法临时启动进程。用systemd管理好。
- 自动封禁一定要留白名单,比如允许你的监控源IP和关键客户IP绕过黑洞。
- 多线BGP主机的优势在于:当一条线路被攻击占满,你可以通过调整本地优先级或降低本地preference,让攻击流量走清洗线路,同时保活其他线路的业务——这是单线路主机做不到的。
这套方案的真实效能
在轻云互联的BGP多线主机上跑这套方案,我实测从PPS报警到BGP路由收敛,最快1-2秒(取决于邻居数量)。比起人工登录机房防火墙或等云厂商清洗,这个速度已经能保命。别小看RTBH简单,关键时刻一条黑洞路由就能保住其他IP的正常服务——BGP多线主机的包容性就在这里。
最后说一句,脚本代码不是封装好的产品,但比那些“高防服务器”更能看出你在网络层掌控到底有多深。把自动判定和BGP调度练在手,比买任何“抗D神器”都踏实。