Ping通的情况,需要结合实际运维经验,深入剖析 DDoS 攻击下的空路由(Nullrouted)触发机制,并建立一套从精准判定到彻底自救的实战方案。
一、 为什么我的搬瓦工服务器会突然“消失”?
搬瓦工绝大多数套餐方案在设计上更偏向于提供高性能的底层架构,而非自带硬件级 DDoS 防御。这意味着当特定 IP 遭遇大规模恶意请求时,机房会采取自动化保护措施以确保整体链路的稳定。
- 空路由(Nullrouted)机制:当流量超过阈值,系统会将指向该 IP 的所有数据包丢弃,形成所谓的“黑洞”。此时网站无法打开、
SSH无法登陆、Ping测试也会完全失效。 - 1800 秒封禁周期:触发空路由后,默认的屏蔽时长通常为
1800 秒(30 分钟)。如果封禁解除后攻击仍在继续,屏蔽时间会循环延长,导致服务器反复失联。
二、 如何确认自己的服务器正处于 DDoS 攻击中?
在排查网络故障时,首先需要区分是软件配置错误还是受到了流量攻击。通过以下数据特征可以快速锁定故障性质。
1. 查看系统邮件通知当 IP 触发空路由时,系统会自动向注册邮箱发送一封包含is currently under a (D)DoS attack的邮件,明确告知 IP 已被暂时屏蔽。
2. 分析流量特征曲线
在排查过程中,通过图形化的数据回馈能最直观地捕捉攻击痕迹。相比于命令行,KiwiVM 后台的统计图表能更清晰地展现出入站流量的异常脉络。
- 进入路径:登录
KiwiVM管理后台,在左侧Security & Records栏目下点击Detailed statistics页面。 - 观察指标:在右侧详情页中,重点观察
Network I/O (Bits per second)图表。 - 黑洞特征判定:
- 流量尖峰:如果图表中出现如绿色阴影所示的垂直状极高尖峰,说明该时段遭受了大规模入站流量冲击。
- 断崖式归零:在尖峰之后,如果流量瞬间掉落并呈水平直线(接近
0 bps)持续延伸,说明IP已被系统自动放入黑洞(空路由)进行清洗。
通过Detailed statistics中的流量断层判定IP是否被封禁3. 状态矛盾核对如果控制面板显示主机状态为 Running 且未执行 重装系统 或主动关闭服务,但外部一切连接手段均失效,基本可以判定为遭受了 DDoS 攻击。
三、 应急方案:利用 Cloudflare 建立前端防线
由于搬瓦工普通套餐无法提供高防线路,利用Cloudflare的全球节点接住流量是目前成本最低且效果最好的自救方式。
1. 接入与隐藏逻辑
通过将域名DNS托管至Cloudflare并点亮橙色云朵图标(代理模式),访客的请求会先打到防护节点而非搬瓦工源站。这种方式能有效隐藏真实 IP,屏蔽掉大量的扫描与流量攻击。
2. 防护策略加固
- 开启攻击模式:在攻击期间开启
Under Attack Mode,强制访客进行 5 秒安全验证。 - 防火墙规则 (WAF):在后台针对异常频率的请求或特定国家/地区设置拦截规则。
- 优化缓存设置:适当提高静态资源的缓存比例,减少回源请求,减轻服务器的
CPU与网络压力。
四、 利用快照功能实现数据迁移与 IP 更换
如果攻击方持续对特定 IP 进行攻击,即使部署了Cloudflare,旧 IP 一旦解封仍可能被再次打进黑洞。此时,利用快照功能进行业务迁移是更为彻底的办法。
数据迁移与环境恢复流程:
- 制作快照:在 KiwiVM 界面进入
Snapshots页面,为当前受攻击的云服务器做一个全量 备份。 - 快照还原与迁移:利用搬瓦工的快照功能,可以新建一台同设置的 VPS 并还原该镜像。或者通过面板功能进行更换机房来获取全新的 IP 地址。
- 解析同步更新:在获取新 IP 后,只需在
Cloudflare后台修改A 记录,新 IP 会在几秒钟内生效,从而切断旧 IP 的受攻击链路。
五、遇DDoS自动断开网卡5分钟
检测DDoS就自动断网5分钟
如果你的VPS只入 不出的话..那么这个脚本就不适合你了
可修改的参数
1.触发检查的起步阈值 (MB/s) 这个可以填写你的最大带宽 注意单位是MB
TRIGGER_LIMIT_MB=50
2.出站流量比例因子 (0.0 – 1.0)
如果流出的流量比进入的流量*设置的值(默认是0.4)就认为发生了ddos
如果你的服务器只入 不出的话..那么这个脚本就不适合你了
如果 (出站 TX) < (入站 RX * 因子),则判定为攻击。
SAFE_RATIO=0.4
3. 连续确认次数 防止误报
MAX_RETRIES=3
4. 检查间隔 (秒)
CHECK_INTERVAL=2
5. 黑洞时长 (秒) – 300秒 = 5分钟 断网时长..
BLACKHOLE_TIME=300
6. 安全模式 (true=只报警不操作, false=执行断网)
SAFE_MODE=false
7. 日志显示阈值 (MB/s) 只有高过这个阈值才有显示出来的文本 就是 一些流入和 流出 数据计算的值
LOG_LIMIT_MB=5
Bash脚本版本
#!/bin/bash
# ================= 用户配置区域 =================
# 1. 触发检查的起步阈值 (MB/s)
TRIGGER_LIMIT_MB=50
# 2. 出站流量比例因子 (0.0 - 1.0)
# 如果 (出站 TX) < (入站 RX * 因子),则判定为攻击。
SAFE_RATIO=0.4
# 3. 连续确认次数
MAX_RETRIES=3
# 4. 检查间隔 (秒)
CHECK_INTERVAL=2
# 5. 黑洞时长 (秒) - 300秒 = 5分钟
BLACKHOLE_TIME=300
# 6. 安全模式 (true=只报警不操作, false=执行断网)
SAFE_MODE=false
# 7. 日志显示阈值 (MB/s)
LOG_LIMIT_MB=5
# ===============================================
# 自动获取默认网卡
IFACE=$(ip route get 8.8.8.8 | awk '{print $5; exit}')
OVERLOAD_COUNT=0
# 颜色定义
RED=' 33[0;31m'
GREEN=' 33[0;32m'
YELLOW=' 33[0;33m'
NC=' 33[0m'
echo -e "${GREEN}=== 智能流量保镖 v3.0 (黑洞版) 启动 ===${NC}"
echo "监控网卡: $IFACE"
echo "黑洞时长: $BLACKHOLE_TIME 秒"
echo "安全模式: $SAFE_MODE"
echo "========================================"
get_bytes() {
cat "/sys/class/net/$IFACE/statistics/$1"
}
# 初始化读数
PREV_RX=$(get_bytes "rx_bytes")
PREV_TX=$(get_bytes "tx_bytes")
while true; do
sleep $CHECK_INTERVAL
CURR_RX=$(get_bytes "rx_bytes")
CURR_TX=$(get_bytes "tx_bytes")
# 使用 awk 计算速度和逻辑
read RX_MB TX_MB IS_HIGH IS_ATTACK <<< $(awk -v r1=$PREV_RX -v r2=$CURR_RX
-v t1=$PREV_TX -v t2=$CURR_TX
-v time=$CHECK_INTERVAL
-v limit=$TRIGGER_LIMIT_MB
-v ratio=$SAFE_RATIO '
BEGIN {
delta_rx = r2 - r1
delta_tx = t2 - t1
# 防止负数 (重启网卡后可能发生)
if (delta_rx < 0) delta_rx = 0
if (delta_tx < 0) delta_tx = 0
rx_speed = delta_rx / 1024 / 1024 / time
tx_speed = delta_tx / 1024 / 1024 / time
is_high = (rx_speed > limit) ? 1 : 0
limit_tx = rx_speed * ratio
is_attack = (tx_speed < limit_tx) ? 1 : 0
printf "%.2f %.2f %d %d", rx_speed, tx_speed, is_high, is_attack
}')
# 更新上一轮数据
PREV_RX=$CURR_RX
PREV_TX=$CURR_TX
# 打印日志
RX_INT=${RX_MB%.*}
RX_INT=${RX_INT:-0}
if [ "$RX_INT" -ge "$LOG_LIMIT_MB" ]; then
echo "[$(date +%T)] RX: $RX_MB MB/s | TX: $TX_MB MB/s"
fi
# 核心判断逻辑
if [ "$IS_HIGH" -eq 1 ] && [ "$IS_ATTACK" -eq 1 ]; then
((OVERLOAD_COUNT++))
echo -e "${RED}[警报 $OVERLOAD_COUNT/$MAX_RETRIES] 流量异常 (RX高 TX低) 疑似攻击!${NC}"
if [ "$OVERLOAD_COUNT" -ge "$MAX_RETRIES" ]; then
echo -e "${RED}!!! 确认攻击,触发黑洞防御 !!!${NC}"
if [ "$SAFE_MODE" = true ]; then
echo -e "${YELLOW}[测试] 安全模式开启,不执行断网。${NC}"
OVERLOAD_COUNT=0
else
# === 执行黑洞 ===
echo -e "${RED}正在关闭网卡 [$IFACE]...${NC}"
ip link set dev "$IFACE" down
echo -e "${YELLOW}网卡已关闭,进入 $BLACKHOLE_TIME 秒静默期...${NC}"
sleep "$BLACKHOLE_TIME"
echo -e "${GREEN}黑洞结束,正在恢复网卡...${NC}"
ip link set dev "$IFACE" up
# 等待网络恢复
sleep 5
# === 关键:重置状态 ===
# 网卡重启后计数器会清零,必须重新获取基准值,否则下次计算会出错
PREV_RX=$(get_bytes "rx_bytes")
PREV_TX=$(get_bytes "tx_bytes")
OVERLOAD_COUNT=0
echo -e "${GREEN}监控已恢复。${NC}"
fi
fi
else
# 流量正常或恢复
if [ "$OVERLOAD_COUNT" -gt 0 ]; then
echo -e "${GREEN}流量特征恢复正常,警报解除。${NC}"
fi
OVERLOAD_COUNT=0
fi
done
bash版本使用
一、 前置要求
- 权限:必须以Root用户身份运行(脚本需要控制网卡开关)。
- 依赖:脚本依赖
ip和awk命令(BandwagonHost 的绝大多数系统已内置,无需额外安装)。
二、 安装脚本 - 创建存放目录(推荐)
mkdir -p /root/scripts
nano /root/scripts/traffic_guard.sh
- 粘贴代码将上方的脚本完整复制并粘贴进去,按
Ctrl+O保存,Ctrl+X退出。 - 赋予执行权限
chmod +x /root/scripts/traffic_guard.sh
三、 运行方式
方式 A:Systemd 托管运行(推荐,生产环境标准)
这种方式可以实现开机自启,后台静默运行,且脚本意外退出会自动重启。
- 创建服务文件
nano /etc/systemd/system/traffic_guard.service - 填入以下内容
[Unit]
Description=BandwagonHost Traffic Guard Service
After=network.target
[Service]
Type=simple
# 确保此处的路径与你实际保存的路径一致
ExecStart=/root/scripts/traffic_guard.sh
Restart=always
User=root
[Install]
WantedBy=multi-user.target
- 启动并设置开机自启
#重载配置文件
systemctl daemon-reload
#设置开机自动启动
systemctl enable traffic_guard
#运行此服务
systemctl start traffic_guard
4.查看运行日志
#查看实时日志
journalctl -u traffic_guard -f
方式 B: 临时运行(测试用)
如果你不想配置 Systemd,或者想在 SSH 窗口看着它/root/scripts/traffic_guard.sh
go版本
package main
import (
"fmt"
"io/ioutil"
"log"
"os/exec"
"strconv"
"strings"
"time"
)
// ================= 配置区域 =================
const (
TriggerLimitMB = 50.0 // 入站流量触发阈值 (MB/s)
SafeRatio = 0.4 // 出站/入站 最小安全比例
MaxRetries = 3 // 连续确认次数
CheckInterval = 2 // 检查间隔 (秒)
BlackholeTime = 300 // 黑洞时长 (秒)
LogLimitMB = 5.0 // 日志打印阈值
SafeMode = false // true=仅打印, false=执行操作
)
// ===========================================
var overloadCount = 0
func main() {
// 自动获取默认网卡
iface, err := getDefaultInterface()
if err != nil {
log.Fatalf("无法获取网卡: %v", err)
}
fmt.Printf("=== Go流量保镖启动 | 网卡: %s | 阈值: %.0fMB/s ===n", iface, TriggerLimitMB)
// 初始化读取
prevRx, prevTx := getBytes(iface)
for {
time.Sleep(time.Duration(CheckInterval) * time.Second)
currRx, currTx := getBytes(iface)
// 计算速度 (MB/s)
rxSpeed := float64(currRx-prevRx) / 1024 / 1024 / float64(CheckInterval)
txSpeed := float64(currTx-prevTx) / 1024 / 1024 / float64(CheckInterval)
// 防止负数(网卡重置后可能出现)
if rxSpeed < 0 { rxSpeed = 0 }
if txSpeed < 0 { txSpeed = 0 }
// 更新基准
prevRx = currRx
prevTx = currTx
// 日志输出
if rxSpeed > LogLimitMB {
log.Printf("[流量] RX: %.2f MB/s | TX: %.2f MB/sn", rxSpeed, txSpeed)
}
// 判断逻辑
isHighTraffic := rxSpeed > TriggerLimitMB
isAttackPattern := txSpeed < (rxSpeed * SafeRatio)
if isHighTraffic && isAttackPattern {
overloadCount++
fmt.Printf(" 33[31m[警报 %d/%d] 异常流量检测! RX: %.2f, TX: %.2f 33[0mn", overloadCount, MaxRetries, rxSpeed, txSpeed)
if overloadCount >= MaxRetries {
triggerBlackhole(iface)
// 黑洞结束后,重置计数器和基准值
overloadCount = 0
prevRx, prevTx = getBytes(iface)
}
} else {
if overloadCount > 0 {
fmt.Println(" 33[32m流量恢复正常,警报解除。 33[0m")
}
overloadCount = 0
}
}
}
// 执行黑洞逻辑
func triggerBlackhole(iface string) {
fmt.Println(" 33[31m!!! 确认攻击,正在执行黑洞防御 !!! 33[0m")
if SafeMode {
fmt.Println("[安全模式] 模拟执行:关闭网卡...")
time.Sleep(2 * time.Second)
fmt.Println("[安全模式] 模拟执行:开启网卡...")
return
}
// 关闭网卡
log.Printf("正在关闭网卡 %s ...", iface)
if err := exec.Command("ip", "link", "set", "dev", iface, "down").Run(); err != nil {
log.Printf("关闭网卡失败: %v", err)
return
}
log.Printf("网卡已关闭,等待 %d 秒...", BlackholeTime)
time.Sleep(time.Duration(BlackholeTime) * time.Second)
// 开启网卡
log.Printf("正在恢复网卡 %s ...", iface)
if err := exec.Command("ip", "link", "set", "dev", iface, "up").Run(); err != nil {
log.Printf("CRITICAL: 恢复网卡失败,请手动处理! Error: %v", err)
return
}
// 给一点时间让网络重新协商
time.Sleep(5 * time.Second)
log.Println("网络已恢复,重新开始监控。")
}
// 读取系统文件获取流量字节
func getBytes(iface string) (uint64, uint64) {
rxPath := fmt.Sprintf("/sys/class/net/%s/statistics/rx_bytes", iface)
txPath := fmt.Sprintf("/sys/class/net/%s/statistics/tx_bytes", iface)
rx, _ := readUint64(rxPath)
tx, _ := readUint64(txPath)
return rx, tx
}
func readUint64(path string) (uint64, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return 0, err
}
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
}
// 简单的获取默认网卡方法
func getDefaultInterface() (string, error) {
out, err := exec.Command("sh", "-c", "ip route get 8.8.8.8 | awk '{print $5; exit}'").Output()
if err != nil {
return "", err
}
return strings.TrimSpace(string(out)), nil
}
一、环境准备
在编译运行之前,你需要先在 VPS 上安装 Go 语言环境。Debian / Ubuntu:
apt update && apt install golang -y
CentOS / AlmaLinux:
yum install golang -y
验证安装是否成功:
go version
输出类似 go version go1.x.x linux/amd64 即为成功
二、 编译与安装
- 创建文件创建一个名为
traffic_guard.go的文件,并将上方的代码完整粘贴进去。Bashnano traffic_guard.go(粘贴代码后,按Ctrl+O保存,Ctrl+X退出) - **修改配置(可选)**代码顶部的
const区域是配置项。-TriggerLimitMB: 触发阈值(默认 50MB/s)。 SafeMode: 如果设为true,只会报警不会真断网(适合测试)。- 编译生成可执行文件
go build -o traffic_guard traffic_guard.go此时你会发现当前目录下多了一个名为traffic_guard的可执行文件。 - 赋予权限并移动
chmod +x traffic_guard mv traffic_guard /root/traffic_guard
三、运行方式
方式 A:Systemd 托管运行(强烈推荐)
这是最稳健的方式,支持开机自启和进程守护。
- 创建服务文件
nano /etc/systemd/system/traffic_guard.service - 填入以下内容
[Unit]
Description=BandwagonHost Traffic Guard (Go Version)
After=network.target
[Service]
Type=simple
# 确保这里的路径是你实际存放二进制文件的路径
ExecStart=/root/traffic_guard
Restart=always
User=root
[Install]
WantedBy=multi-user.target
3.启动并配置开机自启
#重载配置文件
systemctl daemon-reload
#设置开机自动启动
systemctl enable traffic_guard
#运行此服务
systemctl start traffic_guard
4.查看状态与日志
#查看运行状态
systemctl status traffic_guard
#查看实时日志
journalctl -u traffic_guard -f
方式 B:临时测试运行
如果你只是想看看它能不能正常工作:
./traffic_guard
(注意:必须以 Root 身份运行,否则无法控制网卡)
💡常见问题 (FAQ)
- Q: 编译时报错
command not found怎么办?– **A:**说明 Go 环境没装好,请重新执行apt install golang。如果 VPS 系统太老源里没有 Go,建议使用 Bash 版本脚本。 - Q: 程序报错
panic: 无法获取网卡?– **A:**程序会自动尝试检测默认网卡。如果检测失败,请检查你的 VPS 是否有特殊的网络配置,或者尝试手动修改代码中的getDefaultInterface函数,直接返回字符串"eth0"。
关键配置说明 - **怎么测试脚本是否有效?**建议将脚本中的
SAFE_MODE=false改为SAFE_MODE=true。 这样当流量超标时,脚本只会输出红色警报文字,而不会真的断网。确认触发逻辑正常后,再改回false。 - **触发黑洞断网后,我会彻底失联吗?**不会。黑洞防御只是暂时关闭网卡,您可以通过以下三种方式恢复:- 途径 1:等待自动恢复耐心等待
BLACKHOLE_TIME(默认 300秒/5分钟)结束,脚本会自动重新开启网卡,无需任何操作。 - 途径 2:通过 KiwiVM VNC 恢复登录 KiwiVM 面板,打开VNC Console(类似于物理显示器,不受网卡关闭影响)。在 VNC 窗口中按
Ctrl+C终止脚本,然后输入ip link set dev eth0 up手动恢复网络。 - 方法 3:重启服务器如果以上方法您都不会步骤,可以直接在 KiwiVM 面板点击Reset或Force Stop后再Start重启服务器。重启后网卡会默认恢复连通状态。(注意:这会导致您未保存的数据丢失或服务短暂中断)。
六、 长期预案:建立高可用的 VPS 使用环境
防御 DDoS 攻击是一项长期工作。通过完善的预案体系和日常的加固措施,可以将潜在的停机风险降到最低。
安全预防建议:
- 双机备份方案:建议准备一台速度快的线路(如 香港CN2 GIA 或 日本东京CN2 GIA)作为直连主力机,同时准备一台便宜的KVM常规套餐作为备份服务器。一旦主力机被黑洞,立即切换解析到备份机并开启
Cloudflare防御。 - 系统加固与监控:日常运维中应当修改SSH端口,定期通过
Audit Log观察异常操作,并确保开启了搬瓦工自动备份功能以应对极端情况。 - 资源合理分配:若业务量增长明显,建议及时升级套餐。如果 IP 已被打残无法解封,可咨询搬瓦工客服关于购买IP的具体事宜或申请 退款。
注意事项 - 当 IP 处于空路由状态时,所有的
SSH工具(如Xshell、Xftp)均无法连接,请耐心等待 1800 秒解封。 - 若VPS因 CPU 占用过高被暂停(Suspended),处理流程会有所不同。请务必确认失联原因属于流量攻击而非资源超载。
原创文章,作者:banwagong,如若转载,请注明出处:https://bwgcn2gia.com/archives/283.html
