2026年5月19日6 分鐘fail2ban · Apache · 資安強化 · 維運除錯 · 系統管理 · 根因分析 · vhost · DNS

fail2ban 一個月後突然 ban 了我們自己 — Apache 多 vhost 環境的隱形陷阱

我們自架的 fail2ban 跑了一個月後,突然開始 ban 自己對外的 WAN IP — 客戶連我們網站慢、API 不通、SSH 異常。追了一整天才找到 root cause:vhost、log format、fail2ban filter、DNS lookup 設定的 4 個元件串起來的隱形陷阱。這篇拆完整 6 步因果鏈跟雙重防護解法。

陳先生 (Henry)

「為什麼客戶說我們網站連不上?」「為什麼公司 SSH 出去 timeout?」「為什麼 LINE Notify webhook 突然送不出去?」

那天早上看到 Mattermost 一堆 alert 同時跳出來。15 分鐘後我們發現 — fail2ban 把我們自己對外的 WAN IP ban 進 ipset blackhole

但詭異的地方:我們沒有任何成員從那個 IP 掃過任何漏洞 path。攻擊 log 顯示的 attacker IP 是其他國家的,為什麼會 ban 到自己?

追了一整天才找到 root cause,串起來是 6 步因果鏈。寫出來給跟我們一樣自架 fail2ban 的人參考 — 多 vhost 環境的隱形陷阱。

觸發環境 #

典型的小團隊自架 stack:

  • 一台對外 reverse proxy(Apache)
  • 後面接多個 vhost
  • fail2ban 跑數個 jail,含「漏洞掃描偵測」類 jail(抓 /wp-login.php /.env /phpmyadmin 等)
  • action 把違規者寫進 ipset blackhole(永久、全 port 一律 DROP)

這套用一陣子一直好好的。直到那天。

症狀 #

對外連線突然異常:

  • 從辦公室訪問自家網站 → timeout
  • 對外的 webhook 跟 API 整合 → 502
  • 部分 DNS 查詢也慢 / timeout

內網一切正常:機房內部互連、容器互連、本地服務都活著。

第一直覺:對外 router 掛了?檢查網路一切正常。 第二直覺:被 ISP 鎖了?打去客服說沒事。 第三直覺:看 fail2ban blackhole — 發現自家 WAN IP 在 ban list 裡

sudo ipset list blackhole | grep <我們的 WAN IP>
203.0.113.10  ← 對,就是公網出口 IP

自己被自己 ban 了

Root Cause 完整 6 步 #

從 access log + fail2ban.log 對齊時間 trace 出來的因果鏈:

Step 1:某些 vhost 沒設 CustomLog #

環境裡有一兩個 vhost 是「臨時實驗用」的,只開了 port 80 vhost、沒做 LE-SSL 版本,且沒明確指定 CustomLog

<VirtualHost *:80>
    ServerName vhost-a.our-domain.tw
    DocumentRoot /var/www/vhost-a
    # ← 沒有 CustomLog
</VirtualHost>

當時的想法:「短期用一下、不用設這麼齊」。就是這個短期帶來長期災難

Step 2:Fallback 到 other_vhosts_access.log #

Apache 的 apache2.conf 預設裡有一行:

CustomLog /var/log/apache2/other_vhosts_access.log vhost_combined

沒明確 CustomLog 的 vhost 的所有請求全部落這裡

我們以為「會被分流到對的 log」其實沒有 — fail2ban 一直在讀這個檔案。

Step 3:vhost_combined 格式行首是 hostname 不是 IP #

這是關鍵 — 標準 combined vs vhost_combined 兩個 LogFormat 行首完全不一樣

# combined(行首是 client IP)
%h %l %u %t "%r" %>s ...

# vhost_combined(行首是 vhost:port,然後才是 client IP)
%v:%p %h %l %u %t "%r" %>s ...

實際 log 長這樣:

vhost-a.our-domain.tw:80 203.0.113.45 - - [18/May/2026:06:54:23 +0800] "GET /wp-login.php HTTP/1.1" 404 ...
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
行首是 vhost hostname:port

Step 4:fail2ban filter logpath glob 把這檔案包進來了 #

漏洞掃描偵測類 jail 常見設定:

[漏洞掃描偵測 jail]
logpath = /var/log/apache2/*access*.log

Glob *access*.log 會 match 進 other_vhosts_access.log這個 fallback log 也被掃進來(容易忽略)。

Step 5:fail2ban <HOST> macro 不只 match IP,也 match hostname #

fail2ban 內建的 <HOST> macro 定義:

<HOST> = (?:::f{4,6}:)?(?P<host>[\w\-.^_]*\w)

這個 regex 同時 match IPv4、IPv6、hostname。Filter 寫 ^<HOST> .* /wp-login 在 vhost_combined 格式下會:

  • 抓到 vhost-a.our-domain.tw(行首的 hostname)
  • 而不是 203.0.113.45(真實 attacker IP,藏在後面)

Step 6:usedns = warn 預設 → DNS lookup → 解出自家 WAN IP → ban 自己 #

這是最後一根稻草。fail2ban [DEFAULT] 區塊沒明確寫 usedns 時,預設不是 no 而是 warn。意思:

  • 遇到 hostname 時做 DNS lookup 解析成 IP
  • log 一筆 warning(給 admin 知道)
  • 照樣 ban 解出來的 IP

vhost A 記錄指向自家 WAN IP → fail2ban 解出 WAN IP → 加進 blackhole ipset → 整套出口流量被自家 server 擋掉

時序對齊(典型 log 樣貌) #

凌晨某時刻,attacker 從某海外 IP 掃 /wp-login.php

02:05:06 fail2ban.log: [apache-vuln-scan] Found 203.0.113.10  ← 自家 WAN IP(DNS lookup 解出來的)
02:05:07 fail2ban.log: [apache-vuln-scan] Found 203.0.113.45    ← 真實 attacker IP
02:05:08 fail2ban.log: [apache-vuln-scan] Ban 203.0.113.10    ← 但 maxretry=1 → 先 ban 自己

Found 203.0.113.10Found 203.0.113.45 是同一行 access log 觸發的

  • vhost_combined 行首 vhost-a.our-domain.tw:80 → match hostname → DNS lookup → 解出 WAN IP
  • 行內後面 203.0.113.45 - - → match IP → 直接拿

兩個都被 fail2ban 當「來源 IP」處理,按出現順序 ban。自家 WAN IP 出現在前 → 先被 ban 掉。

解法:雙重防護 #

光修一個地方還不夠 — 雙保險才能避免類似事情再發生。

A. 每個 vhost 明確 CustomLog(首選) #

<VirtualHost *:80>
    ServerName vhost-a.our-domain.tw
    DocumentRoot /var/www/vhost-a
    ErrorLog ${APACHE_LOG_DIR}/vhost-a_error.log
    CustomLog ${APACHE_LOG_DIR}/vhost-a_access.log combined
    # ↑ 用 combined 不是 vhost_combined,行首是 client IP
</VirtualHost>

做完 apachectl configtest + systemctl reload apache2,新請求就會落到自己的 access.log(格式正確、fail2ban 能正確抓 IP)。

B. fail2ban 全域關 DNS lookup(輔助) #

/etc/fail2ban/jail.local

[DEFAULT]
usedns = no
sudo systemctl restart fail2ban
# 注意:jail config 改了要 restart,不能只 reload

意義:未來又有 vhost 漏設 CustomLog 時,至少不會誤殺

C. ignoreip 加自家 WAN IP(保險的保險) #

[DEFAULT]
ignoreip = 127.0.0.1/8 192.168.0.0/16 <YOUR_WAN_IP>/32

怎麼確認自己有沒有踩到 #

# 1. 看有沒有 vhost 落到 other_vhosts_access.log
tail -20 /var/log/apache2/other_vhosts_access.log
# 期望:沒有新增(或檔案不存在)

# 2. 看 fail2ban 有沒有反查過 hostname(log 會有 warning)
sudo grep -i "DNS.*resolved" /var/log/fail2ban.log | head
# 期望:沒輸出

# 3. 看 blackhole 有沒有自家 IP
sudo ipset list blackhole | grep <YOUR_WAN_IP>
# 期望:沒輸出(或 clean)

教訓 #

教訓 為什麼
新增 vhost 永遠寫明確 CustomLog combined vhost_combined 行首是 hostname,配上 fail2ban DNS lookup 就完了
fail2ban usedns = no 應該第一天設 Debian / Ubuntu 預設 warn,會解 hostname 然後 ban,是隱性陷阱
「臨時用一下」常常會變長期 缺設定的 vhost 一旦上線就容易被忘記
多元件互動的 bug 最難 debug 4 個元件(vhost、log format、filter、DNS)獨立看都沒問題,串起來才出事
自家 IP 被自己 ban 是難排查的 直覺會懷疑 ISP / router / firewall,最後才查到 fail2ban

想做類似的維運強化 #

我們做小規模機房 IT 維運 / 資安強化 / 老系統現代化。如果你也是「自己當 MIS、自己 deploy」的小團隊:

  • 一次性 audit + 強化清單(fail2ban、Apache、Nginx、Docker、SSH、防火牆)
  • 持續維運合約 — 我們幫看週報 + alert 處理
  • 跨地伺服器 / VPN 整合架構規劃

聯絡:0912852835 / henryccy@icloud.com / LINE @3q3tw / 線上表單

延伸閱讀:

想聊類似的東西?

諮詢免費,依工時報價。

聯絡我們