KVM宿主机遭受网络攻击,虚拟机带宽打满限流
当 KVM 宿主机被攻击,最直接的表现就是某个虚拟机的带宽被异常流量打满,导致宿主机和其他虚拟机也一起卡顿。
解决思路不是先去追攻击源,而是先用 iptables 对受影响的虚拟机 IP 做限流,把带宽压到安全范围。
本文按零基础可直接操作的方式,讲清准备条件、限流命令、避坑点和验证方法,20 分钟内能落地。
先确认问题出在哪台虚拟机
动手之前,先看清是宿主机流量异常,还是某个虚拟机占用过高。
登录宿主机执行:
# 查看宿主机整体流量(每秒刷新)
watch -n 1 ifstat
# 用top看CPU占用是否有可疑进程
ps aux --sort=-%cpu | head -10
如果宿主机 CPU 不高但带宽被打满,再逐个查看虚拟机网卡流量。
一种直接方法是先用 virsh list 列出虚拟机,然后分别进入虚拟机内部观察,但对于已经卡死的虚拟机,最稳妥的方式是看宿主机上对应 vnet 接口的流量:
# 列出所有网卡和实时流量
top -i -n 1 | grep vnet
确认哪个 vnetX 对应的虚拟机有问题后,再通过 virsh dumpxml 虚拟机名 找到它的 IP,或者直接用 ARP 表反查。
记住:限流只针对具体的虚拟机 IP,不是把宿主机整个网卡堵死。
用 iptables 对虚拟机 IP 做带宽限制
iptables 本身没有直接写“限速几 Mbps”的功能,但可以配合 hashlimit 模块做每秒包数限制,间接控制带宽。
下面以限制某个虚拟 IP 的上行和下行带宽为例:
假设虚拟机 IP 是 192.168.1.100,宿主机对外网卡是 eth0,虚拟机的流量通过 virbr0 虚拟网桥转发。
先检查系统是否支持 hashlimit:
modprobe ipt_hashlimit
lsmod | grep hashlimit
确认后添加规则。
限制该 IP 的入站流量(从外部进虚拟机):
iptables -A FORWARD -o virbr0 -d 192.168.1.100 -m hashlimit \
--hashlimit-above 1000/sec --hashlimit-burst 3000 \
--hashlimit-mode 16 \
--hashlimit-name limit_in -j DROP
限制该 IP 的出站流量(从虚拟机出去):
iptables -A FORWARD -i virbr0 -s 192.168.1.100 -m hashlimit \
--hashlimit-above 800/sec --hashlimit-burst 2000 \
--hashlimit-mode 16 \
--hashlimit-name limit_out -j DROP
解释一下关键参数:--hashlimit-above 表示超过这个速率就丢包;--hashlimit-burst 是允许瞬间突发的包数量;--hashlimit-mode 16 表示按选择按 IP 维度匹配(这里是按源或目标 IP);--hashlimit-name 是规则名,必须唯一。
上面数值只是示例,如果攻击流量很大,建议先设置成正常业务带宽的一半,再逐渐调整。
保存规则让它重启后仍然生效:
# Ubuntu/Debian
apt install iptables-persistent -y
netfilter-persistent save
# CentOS/RHEL 7+
yum install iptables-services -y
systemctl enable iptables && systemctl restart iptables
高位宽下的坑:别把正常业务也限死了
第一个坑是只限 IP 不限端口。
如果攻击流量集中在 80/443 端口,应该把端口条件加上,避免影响其他端口的正常业务:
iptables -A FORWARD -o virbr0 -d 192.168.1.100 -p tcp --dport 80 -m hashlimit \
--hashlimit-above 500/sec --hashlimit-burst 1500 \
--hashlimit-mode 16 --hashlimit-name limit_http_in -j DROP
第二个坑是顺序问题。
iptables 规则是按顺序匹配的,如果你前面已经有一条 ACCEPT 规则把虚拟机所有流量放行,再放到下面限流就不会命中。
所以限流规则应该放在 FORWARD 链的前部,或者直接插入到指定位置。
用 iptables -L FORWARD --line-numbers -n 查看顺序,再用 iptables -I FORWARD 序号 插入。
第三个坑是忘了处理 NAT 场景。
如果虚拟机是通过宿主机做 NAT 上网的,流量在 FORWARD 链上仍是转发流量,上述规则可用;
但如果宿主机自身发往虚拟机的流量,则需要追加 OUTPUT/INPUT 链的规则。
实际攻击场景大多是外部流量,所以先处理和 FORWARD 相关的规则即可。
如何验证限流是否生效
设置完规则后,不要光看带宽数值,还需要看丢包率。
在宿主机上 ping 目标虚拟机 IP:
ping -c 100 192.168.1.100 | tail -2
如果丢包率明显上升,说明限流规则生效了。
也可以查看每个 hashlimit 规则的命中计数:
iptables -L FORWARD -v -x | grep limit_in
其中 pkts 列如果持续增加,说明有包被丢弃。
同时用 bmon 或 iftop 观察虚拟机实际带宽:
iftop -i virbr0 -F 192.168.1.100/32
当观察到带宽被压到预期值,再回到宿主机看整体负载是否恢复正常。
最后提醒一点:iptables 限流只是应急止损,不是治本方案。
如果攻击流量直接打满宿主机物理网卡,单纯在 FORWARD 链限流也救不了。
更稳妥的做法是配合机房防火墙、云盾或专业高防产品,在网络入口先把攻击流量清理掉。
如果你当前用的是云服务器,优先检查服务商控制台的安全组或丢包防护策略,再结合 iptables 做第二层兜底。
本文的命令在常见的 Ubuntu 18.04/20.04、CentOS 7/8 上实测可用,其他发行版只需调整保存规则的命令。