iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
自我挑戰組

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

EDL 黑名單:從哪來、放在哪、誰該吃它

  • 分享至 

  • xImage
  •  

安裝包裡有六套開源元件,但 EDL 不是任何一套產出的,它是自建的 od-bridge 產出的。這篇把 EDL 的來源、落地路徑、對外 URL、以及「誰該讀它、誰不該讀它」一次講清楚。特別要說的是:不建議拿 Linux 的軟體防火牆去吃這份 EDL,理由在第六節。

先講結論。EDL 是 od-bridge 把平台核可的封鎖決策「翻譯成純文字檔」再用 HTTP 供應出去的產物,格式是一行一個 IP,設計給企業既有的邊界防火牆(Palo Alto、FortiGate 這類支援 External Dynamic List 的設備)定期抓取。安裝包內的六套開源元件沒有任何一套讀它;防禦節點自己的封鎖靠的是 nftables 與 CrowdSec,那兩條是 od-bridge 直接寫入的,不經過 EDL 檔。沒有專業設備的環境,EDL 產出來就只是擺著,這是預期狀態,不是故障。另外平台本身還自產一份只含黑名單的 EDL(第四節),給沒有防禦節點的部署用。

一、EDL 是什麼

EDL 是 External Dynamic List 的縮寫,來自 PAN-OS 的用語。概念很單純:防火牆上設一條規則,來源不寫死 IP,而是指向一個 URL;防火牆依排程重新抓那個 URL,抓到的內容就是當下的清單。它有三個特性,後面的設計都是從這三點推出來的:

特性 對設計的影響
格式極簡,一行一個 IP 或 CIDR 沒有欄位可以放到期時間。清單裡的每個項目「什麼時候該消失」只能由產出端管,設備端不知道
抓到什麼就是什麼 每次抓取都是全量替換,不是增量。所以產出端必須每次重繪整份檔案,不能只在檔尾追加
只能從 URL 取得 檔案寫在磁碟上沒有用,得有一個 HTTP 服務把它送出去,而且回應格式要是設備認得的純文字

二、EDL 從哪來:決策落地的第三條路

第 3 篇講過,事件送上平台建案、流程核可封鎖之後,決策是由 od-bridge 拉回防禦節點的(每 5 秒輪詢一次 /api/open_defense/decisions)。每筆決策帶一個 enforcement_points 欄位,寫明這筆決策要落在哪些執行點;od-bridge 拿自己 .env 裡的 MY_ENFORCEMENT_POINTS(預設 nftables,crowdsec,edl)與它取交集,交集內的每個執行點各跑一個 enforcer。EDL 是三個 enforcer 之一,與 nftables、CrowdSec 平行,彼此不相依。
https://ithelp.ithome.com.tw/upload/images/20260923/201842615T5UObIjqp.png

edl enforcer 的實作重點只有一個:它是 reconciler,不是 appender。理由回到第一節的第一個特性:EDL 格式沒有 TTL 欄位,追加式寫法永遠刪不掉到期項目,清單只會長到設備拒絕載入。所以 od-bridge 把權威集合放在 state.json(每筆自帶 expires_at),每次收到決策或每 60 秒的 prune 都重繪整份 blocklist.txt 與 allowlist.txt。新增會累積,到期會自己掉出去。寫檔走 temp file 加 os.replace,設備抓到一半不會讀到半截檔。

平台決策 action 寫進哪份清單 行為
block blocklist.txt 加入或更新該 target 的到期時間(ttl_seconds 為空即永久)
allow allowlist.txt 同上;設備端要把 allow 規則放在 deny 規則之上才有意義
unblock 兩份都檢查 從持有它的清單移除,已不在也視為成功(冪等)
target 不是 ip / cidr — 回 unsupported_target_type,EDL 只認位址

三、路徑與 URL:查問題時看這張表

項目 值(以本系列的主機 B 192.168.0.112 為例)
安裝目錄 /opt/integrated-waf
EDL 目錄(主機 OS) /opt/integrated-waf/od-bridge/state/edl/
黑名單檔 .../edl/blocklist.txt
白名單檔 .../edl/allowlist.txt
權威狀態檔 .../edl/state.json(含每筆的 expires_at、severity、reason、決策與案件 secure_code)
容器內對應路徑 /state/edl,由 compose 的 ./od-bridge/state:/state 掛入;.env 的 EDL_DIR=/state/edl
檔案擁有者與權限 root,0600。查看要 sudo cat
黑名單 URL http://192.168.0.112:8500/edl
白名單 URL http://192.168.0.112:8500/edl/allow
供應者 od-bridge 容器內的 aiohttp(INGEST_LISTEN=0.0.0.0:8500),因 network_mode: host 直接綁在主機埠上,與 /events、/stats、/decisions 同一個服務
回應格式 text/plain; charset=utf-8,一行一個位址,排序後輸出(兩次抓取之間可 diff)
EDL_HEADER 預設 0。設 1 會在檔頭加 # 註解行,方便人用 curl 看,但 PAN-OS 只文件化 [address][space][comment] 格式,獨立的 # 行設備可以拒收。給設備抓的環境一律維持 0
EDL_PRUNE_INTERVAL 預設 60 秒。沒有新決策時,到期項目最晚 60 秒後從檔案消失
8500 埠的來源限制 nftables input chain 只放行 ADMIN_IPS 與平台 IP,其餘 drop。設備 IP 不在清單內的症狀是連線逾時,不是 403

空清單回 200 而不是 404,是刻意的。還沒有任何封鎖時 /edl 回一個空的 200。若回 404,PAN-OS 會沿用上一次抓到的內容,等於把一份過期的黑名單靜默釘在設備上。所以 curl 看到「200 但沒內容」是正常狀態,代表目前沒有生效中的封鎖。

四、另一份 EDL:主機 A 平台端自產的黑名單

前三節講的都是主機 B 上 od-bridge 那份。平台本身還有一份:主機 A 每分鐘用排程把該企業所有有效的封鎖決策渲染成一個 blocklist.txt,同時可從檔案分享與 HTTP 取得。兩份的內容本來就不同,看到不一致不要當成同步失敗。

項目 主機 B od-bridge 那份(第二、三節) 主機 A 平台端那份
檔案位置 /opt/integrated-waf/od-bridge/state/edl/blocklist.txt <OD_EDL_OUTPUT_DIR>/<企業 secure_code>/blocklist.txt,未設定時是安裝目錄下的 data/edl;每個企業各一份
URL http://192.168.0.112:8500/edl http://192.168.0.111:8000/beakplatform/edl/<企業 secure_code>/blocklist.txt
收哪些決策 只收 enforcement_points 標了 edl 的決策,而且只有這台節點拉得到的(受 SA 的授權範圍限制) 該企業所有有效的 block,不看 enforcement_points
清單 黑名單+白名單兩份,各自獨立 只有黑名單。allow 與 unblock 都當 override:決策時間晚於某筆 block 的 override 會把那筆 block 排除,之後再來新的 block 會重新出現
更新時機 決策拉到即重繪,另有每 60 秒的 prune cron 每分鐘一次(scripts/cron/od_render_edl.py),寫入走 temp file 加 rename
過期判定 state.json 內每筆的 expires_at 決策的 expires_at,刻意不看 status:只接 EDL、沒有其他執行端的部署裡,決策會一直停在 pending,看 status 的話過期項目永遠掉不出去
來源限制 ADMIN_IPS,nftables 層 drop,症狀是逾時 平台 .env 的 OD_EDL_ALLOWED_IPS,未設定=一律拒絕;另有每分鐘 60 次的流量限制
拒絕時的回應 不在名單內連不上 來源不在名單、企業不存在、檔案還沒產生,一律 404 且不區分原因,避免對外洩漏哪個企業存在
故障時的行為 沒有封鎖時回空的 200(第三節的說明) fail-closed=不寫檔,保留舊檔。空清單在防火牆語意上等於解除全部封鎖,一次資料庫故障就清空防線,所以任何例外都不覆蓋既有檔案
取得方式 只有 HTTP HTTP,或把 OD_EDL_OUTPUT_DIR 指到既有的 Samba/NFS 分享目錄,防火牆或管理者從網路磁碟讀同一個 txt

為什麼要有第二份:沒有防禦節點的部署也能接防火牆。只裝主機 A、不裝主機 B 的環境,決策沒有任何執行點會落地,平台端這份就是它唯一的出口;有主機 B 的環境,它則是不依賴 od-bridge 存活的備援來源。

要接哪一份,看你要的語意。需要「放行清單壓過其他封鎖來源」(第 7 篇第三節)、或設備已經在抓主機 B 的其他服務,接 od-bridge 那份;沒有主機 B、想從網路磁碟讀檔、或只想要一份「已經算好誰該封」的黑名單,接平台端那份。不要兩份都接進同一台設備:同一個 IP 在兩邊的答案可能不同(第 7 篇第四節),設備會依規則順序取其一,排錯時很難看出是哪份說了算。

五、誰該吃它:三個執行點各自的消費者

三個 enforcer 平行,但消費者完全不同。這張表是判斷「我的環境該接哪一條」的依據:

執行點 od-bridge 怎麼落地 誰在消費 擋在哪
nftables 直接下 nft 命令進主機的 blocklist set(帶 timeout) 主機 B 的 kernel,立即生效 主機 B 自己的 input hook。保護的是防禦節點本機(SSH、8500、host network 服務),詳見第 5 篇第二節
CrowdSec 經 LAPI 寫入 decision(帶 duration) 任何接了 CrowdSec bouncer 的主機 裝 bouncer 的那台。官方有 nftables / iptables 的 firewall-bouncer,也有 nginx、Cloudflare 等 bouncer
EDL 重繪純文字檔,HTTP 供應 安裝包外支援 External Dynamic List 的邊界設備 那台設備所在的網路邊界。安裝包內沒有任何元件讀它

EDL 的正確消費者是「已經站在攻擊者與網站之間」的邊界設備。典型是企業既有的 Palo Alto(EDL)、FortiGate(External Resource 的 IP address 類型)、或其他能訂閱 URL 清單的次世代防火牆。設備上只要做兩件事:新增指向 http://192.168.0.112:8500/edl 的外部清單、以它為來源建一條 deny 規則;再把設備的抓取來源 IP 加進主機 B 的 ADMIN_IPS。這是「防禦節點以外還有第二道邊界」以及「多台防禦節點想共享一份黑名單」這兩種場景的解法。

六、不建議用 Linux 軟體防火牆吃 EDL

沒有專業設備的讀者第一個想到的通常是:那我在某台 Linux 上寫個 cron,定期 curl 這個 URL 塞進 iptables 或 nftables,不就有邊界防火牆了?三種常見的放法都不建議,理由各不相同。

放法 建議 理由
主機 B(防禦節點)自己的 nftables / ufw 讀自己的 EDL 不要 純粹重複。od-bridge 的 nftables enforcer 已經把同一筆決策直接寫進主機 B 的 blocklist set,而且帶 timeout。再讓它去抓自己的 EDL,是把同一份資料走兩條路寫進同一個 kernel,多一條會出錯的路、不多任何保護
另一台 Linux 用 cron 抓 EDL,塞進 ipset / nftables set 不要 EDL 格式沒有 TTL,所以那支 cron 必須每次全量替換整個 set,等於自己重寫一個劣化版的 od-bridge nftables enforcer。失敗模式都很難看:cron 停了,清單永久卡在最後一版;抓不到 URL 時要決定是清空還是沿用,兩種都不對;抓到一半換檔要處理原子性。而這台 Linux 若要吃平台決策,正確做法是裝 CrowdSec 的 firewall-bouncer:它從 LAPI 拿的決策自帶 duration、增量同步、官方維護,od-bridge 已經把決策寫進 LAPI 了,不必經過 EDL 這個中間格式
Proxmox 宿主機(pve-firewall) 不要 pve-firewall 不支援訂閱 URL 清單,一樣得自建 cron 塞 ipset 或 alias,前一列的問題全部照樣存在。更根本的是位置不對:在 tunnel 部署下,流量從 cloudflared 出來時已經在主機 B 內部,宿主機的防火牆看不到攻擊者的真實 IP,抓到清單也沒有東西可以比對

不建議的核心理由只有一個:EDL 是為「只能吃 URL 清單」的設備設計的最低共同格式,它把 TTL、增量、原子性全部丟給產出端。Linux 主機從來不受這個限制,它有 CrowdSec bouncer 這條帶 TTL 與增量的正規路徑,也有 od-bridge 直接寫 nftables 的實作可以參考。拿 Linux 去吃 EDL,是主動放棄較好的介面去遷就較差的格式。

那沒有專業設備的讀者怎麼辦?誠實的答案是:在純 tunnel 部署下,EDL 沒有適合的落點,nftables 就是這個架構裡唯一有效的邊界(第 5 篇第二節的表格已經講過它擋得住什麼)。若手邊有 pfSense 或 OPNsense 這類自帶 URL 清單 alias 功能的防火牆發行版當網路邊界,那是可以接 EDL 的(它們的 URL table alias 本身就處理排程抓取與全量替換),但這已經是「有邊界設備」的場景,不是 Linux 軟體防火牆。本系列沒有實測這兩者,設定方式請以其官方文件為準。

七、驗證:三分鐘確認整條路是通的

在 ADMIN_IPS 內的任一台主機執行(不在清單內會逾時,這不是 EDL 壞了,是第三節最後一列,平台端那份則看 OD_EDL_ALLOWED_IPS):

# 1. 現況:預期 200,內容為空或一串 IP
curl -s -D - http://192.168.0.112:8500/edl

# 2. 送一筆測試事件,讓平台建案並產生封鎖決策(在主機 B 上)
sudo bash /opt/integrated-waf/install.sh --test-event

# 3. 平台核可封鎖後最多 5 秒,測試 IP 應出現在清單裡
curl -s http://192.168.0.112:8500/edl

# 4. 對照主機端檔案與權威狀態(含到期時間)
sudo cat /opt/integrated-waf/od-bridge/state/edl/blocklist.txt
sudo cat /opt/integrated-waf/od-bridge/state/edl/state.json

# 5. 等 TTL 到期後再抓一次,該 IP 應消失(最晚多等 60 秒的 prune 週期)

第 3 步與第 4 步的內容應一致,因為 /edl 就是原樣讀 blocklist.txt 回出去。若檔案有內容而 URL 是空的,先查 od-bridge 容器有沒有在跑(docker ps 找 od-bridge);若 URL 逾時,查 .env 的 ADMIN_IPS。

八、常見誤解

誤解 實際
EDL 是 Suricata 或 CrowdSec 產出的 都不是。六套開源元件沒有一套產出或讀取 EDL,它由自建的 od-bridge 的 edl enforcer 產出、由同一個 od-bridge 的 HTTP 服務供應
外網進來第一關是 Suricata Suricata 以 -i 旁路監聽網卡,是被動 IDS,不在流量路徑上、不擋也不轉送。實際順序是 nftables(input hook 的 blocklist)→ WAF(nginx + ModSecurity)→ 後端網站;Suricata 與 ModSecurity 只是偵測來源,事件經 Vector 送 od-bridge 再上平台
接了 EDL 就等於封鎖生效 只在那台設備真的站在攻擊者與網站之間才生效。純 tunnel 部署下攻擊流量從 Cloudflare 進來,不經過任何內網邊界,EDL 沒有落點
清單抓下來是空的,代表 EDL 壞了 空的 200 是「目前沒有生效中的封鎖」。要看有沒有壞,用 --test-event 造一筆決策再抓
主機 A 與主機 B 的兩份 EDL 內容不一樣,同步壞了 本來就不同(第四節的表):一份收所有 block 只出黑名單,一份只收標了 edl 的決策且黑白分開。它們之間沒有同步機制
把 EDL 檔複製到別處用 抓到什麼就是什麼,複製出去的那一份沒有 TTL、永遠不會更新。要用就訂閱 URL,不要複製檔案

上一篇
信任邊界與盲區
下一篇
白名單不是一份 IP 清單:四種語意、一個不對稱
系列文
一鍵完成六套開源防禦系統整合 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言