切換選單
切換偏好設定選單
切換個人選單
尚未登入
若您做出任何編輯,會公開您的 IP 位址。

搬瓦工 VPS 遭遇 DDoS 攻擊深度排障與自救手冊

出自md5.pw
於 2026年1月25日 (日) 14:54 由 Air留言 | 貢獻 所做的修訂 (添加分类)
(差異) ←上個修訂 | 最新修訂 (差異) | 下個修訂→ (差異)

面對搬瓦工 VPS 突然全網失聯且無法 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 已被系統自動放入黑洞(空路由)進行清洗。
KiwiVM 詳細統計頁面顯示 DDoS 攻擊導致的流量斷崖
通過 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分鐘

如果你的伺服器只入 不出的話..那麼這個腳本就不適合你了


可修改的參數


# 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='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[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 用戶身份運行(腳本需要控制網卡開關)。
  • 依賴:腳本依賴 ipawk 命令(BandwagonHost 的絕大多數系統已內置,無需額外安裝)。

二、 安裝腳本

  1. 創建存放目錄(推薦)

mkdir -p /root/scripts

nano /root/scripts/traffic_guard.sh

  1. 粘貼代碼 將上方的腳本完整複製並粘貼進去,按 Ctrl+O 保存,Ctrl+X 退出。
  2. 賦予執行權限 chmod +x /root/scripts/traffic_guard.sh

三、 運行方式

方式 A:Systemd 託管運行(推薦,生產環境標準)

這種方式可以實現開機自啟,後台靜默運行,且腳本意外退出會自動重啟。

  1. 創建服務文件 nano /etc/systemd/system/traffic_guard.service
  2. 填入以下內容
    [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
    
  3. 啟動並設置開機自啟

#重載配置文件

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/s\n", rxSpeed, txSpeed)
		}

		// 判断逻辑
		isHighTraffic := rxSpeed > TriggerLimitMB
		isAttackPattern := txSpeed < (rxSpeed * SafeRatio)

		if isHighTraffic && isAttackPattern {
			overloadCount++
			fmt.Printf("\033[31m[警报 %d/%d] 异常流量检测! RX: %.2f, TX: %.2f\033[0m\n", overloadCount, MaxRetries, rxSpeed, txSpeed)

			if overloadCount >= MaxRetries {
				triggerBlackhole(iface)
				// 黑洞结束后,重置计数器和基准值
				overloadCount = 0
				prevRx, prevTx = getBytes(iface)
			}
		} else {
			if overloadCount > 0 {
				fmt.Println("\033[32m流量恢复正常,警报解除。\033[0m")
			}
			overloadCount = 0
		}
	}
}

// 执行黑洞逻辑
func triggerBlackhole(iface string) {
	fmt.Println("\033[31m!!! 确认攻击,正在执行黑洞防御 !!!\033[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 即為成功

二、 編譯與安裝

  1. 創建文件 創建一個名為 traffic_guard.go 的文件,並將上方的代碼完整粘貼進去。Bash nano traffic_guard.go (粘貼代碼後,按 Ctrl+O 保存,Ctrl+X 退出)
  2. 修改配置(可選) 代碼頂部的 const 區域是配置項。
    • TriggerLimitMB: 觸發閾值(默認 50MB/s)。
    • SafeMode: 如果設為 true,只會報警不會真斷網(適合測試)。
  3. 編譯生成可執行文件 go build -o traffic_guard traffic_guard.go 此時你會發現當前目錄下多了一個名為 traffic_guard 的可執行文件。
  4. 賦予權限並移動 chmod +x traffic_guard mv traffic_guard /root/traffic_guard

三、運行方式

方式 A:Systemd 託管運行(強烈推薦)

這是最穩健的方式,支持開機自啟和進程守護。

  1. 創建服務文件 nano /etc/systemd/system/traffic_guard.service
  2. 填入以下內容
[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 面板點擊 ResetForce Stop 後再 Start 重啟伺服器。重啟後網卡會默認恢復連通狀態。(注意:這會導致您未保存的數據丟失或服務短暫中斷)。

六、 長期預案:建立高可用的 VPS 使用環境

防禦 DDoS 攻擊是一項長期工作。通過完善的預案體系和日常的加固措施,可以將潛在的停機風險降到最低。

安全預防建議:

  • 雙機備份方案:建議準備一台速度快的線路(如 香港CN2 GIA 或 日本東京CN2 GIA)作為直連主力機,同時準備一台便宜的KVM常規套餐作為備份伺服器。一旦主力機被黑洞,立即切換解析到備份機並開啟 Cloudflare 防禦。
  • 系統加固與監控:日常運維中應當修改SSH埠,定期通過 Audit Log 觀察異常操作,並確保開啟了搬瓦工自動備份功能以應對極端情況。
  • 資源合理分配:若業務量增長明顯,建議及時升級套餐。如果 IP 已被打殘無法解封,可諮詢搬瓦工客服關於購買IP的具體事宜或申請 退款。

注意事項

  • 當 IP 處於空路由狀態時,所有的 SSH 工具(如 XshellXftp)均無法連接,請耐心等待 1800 秒解封。
  • 若伺服器因 CPU 占用過高被暫停(Suspended),處理流程會有所不同。請務必確認失聯原因屬於流量攻擊而非資源超載。