iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0
自我挑戰組

一鍵完成六套開源防禦系統整合系列 第 20 篇

Suricata:站在網卡旁邊的第二雙眼睛

  • 分享至 

  • xImage
  •  

先講結論。WAF 站在請求路徑上,只看經過它的 HTTP,看到攻擊就當場擋。Suricata 站在主機 B 的實體網卡旁邊,抄一份所有進出的封包,什麼協定都看,但一個封包都不擋。兩者的位置、規則來源、看的方向都不一樣,所以互補的不是「多擋一點」,而是「WAF 看不到的地方有人在看」:WAF 放行之後的那一跳,以及主機 B 自己往外的連線。它寫下的告警與 WAF 的告警走同一條路送到主機 A,在平台上變成同一種案件。

一、它是什麼

Suricata 是 OISF(Open Information Security Foundation)維護的開源網路威脅偵測引擎,授權 GPLv2。它能當入侵偵測(IDS,只看不擋)、入侵防禦(IPS,串在線上丟封包),也能單純記錄網路活動。本安裝包只用第一種:容器映像是 jasonish/suricata:7.0(實測版本 7.0.17),以 af-packet 監聽主機 B 的實體網卡,設定檔寫死 inline: no。

引擎本身不帶判斷,判斷來自規則。安裝時下載的是 Emerging Threats Open 規則集,實測啟用 46,135 條。把這些規則依名稱分類,可以看出它主要在防什麼:

規則在看什麼 啟用條數 包含的類別
內部主機往外的可疑行為 31,920(約七成) MALWARE、MOBILE_MALWARE(惡意程式回連)、DYN_DNS(動態網域查詢)、PHISHING(釣魚頁)、EXPLOIT_KIT(漏洞利用套件)
值得知道、但不一定是攻擊 5,756 INFO、HUNTING
對伺服器的攻擊 2,556 WEB_SPECIFIC_APPS、WEB_SERVER、EXPLOIT、SCAN
攻擊成功後的回應特徵 645 ATTACK_RESPONSE

這張表是理解它角色的關鍵。WAF 的 OWASP CRS 有 850 條,全部在看「打進來的 HTTP 請求」。ET Open 的重心剛好相反,七成規則在看「裡面的主機往外做了什麼」。同一台主機上裝這兩套,重疊的部分其實不多。

二、它接在哪裡

https://ithelp.ithome.com.tw/upload/images/20261004/20184261oEJ81124Lw.png

圖上有三件事要看。

  • 它不在請求路徑上。請求從 cloudflared 到 WAF 再到主機 A,Suricata 只是在實體網卡旁邊抄一份。它停了,網站照常;網站被打,它也擋不了。
  • 它看到的請求,都是 WAF 已經放行的。tunnel 裡是密文,cloudflared 到 WAF 那一段在 docker 網段內不經網卡,被 WAF 擋下的請求則當場回頭。明文請求第一次出現在實體網卡上,是 WAF 把它轉給主機 A 的那一跳。
  • 網卡上不只有訪客的請求。主機 B 與所有容器往外的連線(套件更新、DNS 查詢、cloudflared 自己的連線)也都經過這張網卡。這是 WAF 完全不在場的方向。

各種流量看得到與看不到的完整對照,在原系列第 5 篇第四節。

三、為什麼有了 WAF 還要它

比較 WAF Suricata
位置 請求路徑上 實體網卡旁邊
看的協定 只有經它轉送的 HTTP 網卡上的所有封包;另外解讀 HTTP、DNS、TLS 握手
看的方向 進來的請求,以及對應的回應 進出都看,包含主機 B 自己往外連
規則 OWASP CRS 850 條,描述的是通用的攻擊手法 ET Open 四萬六千多條,多數指名特定的惡意程式、網域、漏洞
反應 當場回 403 只寫一行告警
留下的紀錄 只記有規則命中或回應碼異常的請求 告警之外,還記每條連線、每個 HTTP 交易、DNS 查詢、TLS 握手
停掉的後果 網站斷線 網站照常,少一路告警

由這張表可以整理出它補上的三塊:

  1. WAF 放行之後的第二次檢查。CRS 以通用手法判斷,沒涵蓋的特定漏洞利用碼會通過。Suricata 在轉出的那一跳用另一套來源的規則再看一次,也看主機 A 回來的回應裡有沒有攻擊成功的特徵。它補不了這一次(請求已經到了),但平台可以據此建案、封來源,補下一次。
  2. 主機 B 自己往外的方向。防禦節點握有平台的事件金鑰與執行帳號,本身就是值得攻擊的目標。它一旦被入侵,回連控制伺服器、查詢惡意網域這些行為都不經過 WAF,卻正是 ET Open 七成規則在看的東西。
  3. HTTP 以外的協定。對主機 B 的埠掃描、SSH 探測、異常的 DNS,WAF 一概看不到。

它的代價很低。旁路的元件不會讓網站多一個故障點,這也是原系列第 9 篇的原則:線上的元件越少越好,其餘的都放到線外。

四、eve.json:不只是告警

Suricata 的輸出只有一個檔案 eve.json,一行一筆 JSON。它與 WAF 的 audit.log 性質不同:audit.log 只記有問題的請求,天生是告警清單;eve.json 連正常流量也記。在一台對外服務的節點上,某次輪替後 46 分鐘內的內容是這樣:

1495 行
 746 http     每個 HTTP 交易一筆
 692 flow     每條連線結束時一筆
  44 dns
  10 tls
   2 alert
   1 anomaly

告警不到千分之二,其餘是網路活動紀錄。安裝包對這個檔案的分工如下:

誰在讀 拿什麼 用途
Vector 只取 alert,而且要有來源與目的位址 轉成與 WAF 事件相同的格式,送 ClickHouse 與主機 A
EveBox(選用) 整個檔案 在瀏覽器裡翻告警與它前後的連線紀錄
CrowdSec 整個檔案 有掛載,但出廠沒有對應的解析器,實際不產生判斷(原系列第 2 篇第四節)

保留期限因此分成兩種。告警進了 ClickHouse,保留 90 天。連線、DNS、TLS 這些紀錄只留在檔案裡,每小時輪替一次、保留 48 小時。事後要回頭查「那個來源當時還連了哪裡」,只有兩天的窗口。

五、一筆告警怎麼變成主機 A 上的案件

步 在哪裡 做什麼
1 主機 B,Suricata 規則命中,寫一行 alert。請求帶有 CF-Connecting-IP 標頭時,來源位址會被換成標頭裡的訪客位址,否則走 tunnel 的告警來源永遠是主機 B 自己
2 主機 B,Vector 轉成共同格式:來源系統記為 suricata,規則編號取 signature_id,嚴重度由 Suricata 的 1(高)到 4(資訊)對映成 4 到 1
3 主機 B,Vector 過濾:來源與目的都是內網的丟掉,資訊級的丟掉
4 主機 B,Vector 封頂:同一條規則、同一個來源,一小時只放 1 筆(WAF 是 5 分鐘 5 筆)。被動偵測的告警重複性高,所以收得最緊
5 主機 B → 主機 A od-bridge 簽章後送進平台的事件受理端點
6 主機 A 依路由規則建案、併案、算風險分數,進處置流程。封鎖決策由 od-bridge 拉回,落地在 nftables 與 EDL

兩台主機在這條線上的關係可以這樣說:主機 B 負責看與記,主機 A 負責判斷與追蹤。主機 A 不知道 Suricata 的存在,它只收到一筆來源系統是 suricata 的事件;案件頁的「跨系統關聯」會把同一個位址在 WAF 與 Suricata 各自留下的紀錄並排,這是兩個偵測系統第一次被放在一起看的地方。反過來,主機 A 停了,Suricata 照寫,告警仍然進 ClickHouse,只是沒有人把它變成案件。

告警裡的「攻擊者」是觸發規則的那個封包的來源,不一定是外面的人。主機 B 往外連而觸發的告警,攻擊者欄位就是主機 B 自己。這種案件該做的是檢查主機 B,不是封鎖來源;平台的保護清單也不會讓內網位址被封(原系列第 5 篇第三節)。

六、實測:十天 1,115 筆,沒有一筆是攻擊

下表是一台經 Cloudflare Tunnel 對外服務的防禦節點,連續十天的全部 Suricata 告警(取自 ClickHouse,位址已去識別化)。量測當時出廠停用清單只有四條;表中的 2013504 與 2047123 在這次量測之後已加入出廠停用清單,現在安裝的節點不會再出現這兩條。同一段時間 WAF 的事件是 18 筆,其中 9 筆是被擋下的攻擊請求。

規則 告警名稱 筆數 方向 實際是什麼
2013504 ET INFO GNU/Linux APT User-Agent Outbound 545 主機 B → 外部 主機自己的套件更新檢查
2221050 SURICATA HTTP too many warnings 508 內網 → 內網 HTTP 解讀器對同一條連線的提醒,不是特徵命中
2210044 SURICATA STREAM Packet with invalid timestamp 45 外部 → 主機 B 主機 B 對外連線的回程封包,TCP 重組的檢查
2047123 ET INFO Observed Cloudflare Tunneling Domain 10 主機 B → 外部 cloudflared 自己的連線

1,115 筆全部是低嚴重度,沒有任何一筆屬於掃描、漏洞利用或惡意程式。這個結果要分三層讀:

  • 安靜是預期的。走 tunnel 的攻擊者碰不到主機 B 的網卡,被 WAF 擋下的請求又到不了轉出的那一跳。同期被 WAF 擋下的那 9 筆,Suricata 一筆都沒看到。
  • 安靜的位置才需要有人看。它守的是「WAF 放行之後」與「主機 B 自己往外」,兩個平常沒事的地方。哪天這裡出現一筆 ET MALWARE,意思是主機 B 已經出事,而這是 WAF 永遠不會告訴你的。
  • 雜訊要自己修剪。上表的 INFO 類告警嚴重度是「低」而不是「資訊」,過濾不會丟掉它;來源是主機 B、目的是外部,也不算內網對內網。它們會以每條規則每小時一筆的速度送到主機 A。上表的 2013504(主機 B 自己的套件更新檢查)與 2047123(cloudflared 自己的連線)就是這樣找出來的,量完之後已併入出廠停用清單。主機 B 上裝了別的會往外連的軟體,就會有自己的版本:確定是自家行為的,把規則編號加進主機 B 的 suricata/disable.conf,再執行 sudo bash /opt/integrated-waf/install.sh --update-rules。出廠已停用六條,每一條的理由與什麼環境可以拿掉,寫在該檔的註解裡。

七、它不做的事

限制 說明
不擋任何封包 旁路模式。告警裡的 action 永遠是 allowed
看不到加密的內容 tunnel 與 HTTPS 的內文都是密文,只看得到 TLS 握手時的主機名稱與指紋
不分辨標頭可不可信 來源位址的覆寫是無條件的,誰帶 CF-Connecting-IP 標頭它都採信。能直接連到 WAF 埠的機器因此可以偽造告警來源,所以主機 B 用 nftables 把那個埠的來源限制在管理名單內(原系列第 8 篇)
沒有記憶 每筆告警各自獨立。「同一個來源第幾次了」由主機 A 的併案與風險分數回答
規則不會自己更新 安裝時下載一次,之後要手動執行 --update-rules。安裝包沒有排程做這件事
內網對內網多半不觸發 多數規則限定「外部到內部」或「內部到外部」,而內部的定義(HOME_NET)預設是三段私有位址

八、怎麼確認它在做事

容器狀態是 Up 不算證據,打 127.0.0.1 也測不到(它只看實體網卡)。最短的驗法是在主機 B 上用一個規則認得的 User-Agent 對外連一次,內容完全無害:

# 主機 B
curl -s -o /dev/null -A 'Installed OK' http://example.com/

cd /opt/integrated-waf
sudo docker compose exec -T suricata grep '"signature_id":2030880' /var/log/suricata/eve.json | tail -1 | python3 -c '
import sys, json
e = json.loads(sys.stdin.readline())
print(e["event_type"], e["src_ip"], "->", e["dest_ip"], e["dest_port"], e["app_proto"])
print(e["alert"]["signature_id"], e["alert"]["signature"], "severity", e["alert"]["severity"])
print(e["http"]["hostname"], e["http"]["http_user_agent"])'
alert 192.168.0.112 -> 104.20.23.154 80 http
2030880 ET USER_AGENTS Suspicious User-Agent (Installed OK) severity 2
example.com Installed OK

這證明 Suricata 有在抓封包、規則有載入。幾秒後同一筆應該出現在 ClickHouse,證明 Vector 有在讀這個檔案:

#### 主機 B
CH_PW=$(sudo grep '^CLICKHOUSE_PASSWORD=' .env | cut -d= -f2-)
sudo docker compose exec -T clickhouse clickhouse-client --user secstack --password "$CH_PW" --query \
  "SELECT event_time, finding_rule_id, severity_id, IPv6NumToString(actor_ip) AS actor, target_host
   FROM secstack.events WHERE source_system='suricata' AND finding_rule_id='2030880'
   ORDER BY event_time DESC LIMIT 1"
2026-10-03 16:04:48.853	2030880	3	::ffff:192.168.0.112	104.20.23.154

嚴重度由 Suricata 的 2 變成 3,攻擊者是主機 B 自己,目標是外部位址。這正是第五節說的出站告警的樣子。這筆事件過得了過濾,會往主機 A 送;去程那一段怎麼驗,照原系列第 10 篇第四節的做法。

沒有告警時先查兩件事:.env 的 NODE_IFACE 是不是真正在收送流量的那張網卡;suricata/rules/suricata.rules 是不是空的(安裝時規則下載失敗只會印一行警告,Suricata 仍會以空規則啟動)。


上一篇
驗證清單:怎麼證明每條線在做它該做的事(下)
系列文
一鍵完成六套開源防禦系統整合 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言