VLAN、IP 網段與 OPNsense 介面已經建立清楚的路由邊界,接下來要把流量需求轉成可以執行、驗證與維護的防火牆政策。
一筆新連線進入 OPNsense 後,狀態表(State Table)與防火牆規則會如何處理?規則應放在哪個介面?浮動規則(Floating Rule)、介面群組規則(Interface Group Rule)與單一介面規則的先後順序是什麼?Alias 如何降低維護成本,又為什麼不等於權限?
本文閱讀方式
- 完整理解技術與底層原理:依序閱讀全文。
- 掌握完成部署所需的基本概念:優先閱讀標有「⭐」的章節。
- 快速了解當天的部署內容:閱讀文末的 Lab/實作章節。
- 跟著系列建立完整系統:依照 GitHub 詳細部署文件 的天數順序操作。
前文已介紹狀態式防火牆、安全區域與最小權限。今天先複習必要概念,再從 OPNsense 底層使用的 pf 說明封包如何比對狀態與規則,最後把規則模型套用到已建立的 VLAN 介面。
封包抵達防火牆時,第一個可觀察位置是入站介面(Inbound Interface)。規則預設在封包進入 OPNsense 的方向比對,因此來源位於 SERVICE 的連線通常從 SERVICE 介面開始判斷。由 Internet 進入的連線則從 WAN 開始判斷。

圖(一)新連線從入站介面進入規則表,既有連線則可直接符合狀態表
第一個符合允許規則(Pass Rule)的封包會建立連線狀態,後續屬於同一筆連線的封包可以直接符合狀態表,不必每次重新比對完整規則集(Ruleset)。因此一般只需允許連線發起方向,合法的回程封包會沿用既有狀態。

圖(二)狀態式防火牆只需允許連線發起方向,回程封包可沿用既有狀態
這也會影響測試。修改規則或 Alias 後,既有連線可能繼續符合舊狀態,看起來像是新規則沒有生效。排錯時應先確認 Firewall → Diagnostics → States,必要時只移除相關狀態。清除全部狀態會中斷其他連線。
同 VLAN 主機可能直接交換訊框,不會進入 OPNsense。這裡建立的政策只能控制實際跨越 OPNsense 介面的流量。同 VLAN 的限制由更細的網路分段、PVE 防火牆或主機防火牆(Host Firewall)補足。
建立防火牆規則時,依實際流量需求設定下列欄位:
| 規則欄位 | 設定原則 |
|---|---|
| 動作(Action) | 必要流量使用 Pass。需要明確阻擋時使用 Block 或 Reject。 |
| 介面/方向(Interface/Direction) | 選擇封包進入 OPNsense 的介面,方向通常使用 in。 |
| IP 版本/協定(IP Version/Protocol) | 只選實際使用的 IPv4、IPv6、TCP、UDP 或 ICMP。 |
| 來源(Source) | 限定真正獲准發起連線的主機、網段或 Alias。 |
| 來源連接埠(Source Port) | 用戶端通常使用臨時連接埠,因此多數規則保持 any。 |
| 目的(Destination) | 限定實際服務主機、網段、VIP 或防火牆本身。 |
| 目的連接埠(Destination Port) | 只填入伺服器實際提供的服務連接埠。 |
| 紀錄(Log) | 只替需要稽核、排錯或驗證拒絕結果的規則啟用。 |
| 說明(Description) | 寫明流量用途、來源、目的與服務,讓規則可以稽核。 |
三種常見動作的差異如下:
Pass:允許封包,預設會建立連線狀態。Block:直接丟棄,用戶端通常等待到逾時。不受信任來源常使用這種方式。Reject:拒絕並主動回覆。TCP 通常回覆 RST,UDP 則回覆 ICMP Unreachable。內部排錯時可以更快顯示連線遭拒。OPNsense 會依規則所屬區段決定處理優先序。系統自動規則之外,主要順序為:

圖(三)OPNsense 各規則區段的主要處理優先序
每個區段內還要再看順序(Sequence)。一般規則預設啟用 Quick,表示由上往下比對,第一條符合的規則立即決定結果。若取消 Quick,則會繼續比對並採用最後一條符合的規則。沒有明確需求時,維持 Quick,用「較精確的規則在上、較廣泛的規則在下」閱讀最容易稽核。
新版 OPNsense 的 Firewall → Rules [new] 中,一條規則選擇單一介面時是介面規則。選擇介面群組時是群組規則(Group Rule)。同時選擇多個介面或反向選取介面,會被歸入浮動規則。浮動規則的處理時間早於群組規則與單一介面規則。
介面群組規則又早於單一介面規則。若群組中先出現一條廣泛的 Block,後面單一介面的 Pass 沒有機會命中。共同政策與例外政策必須按照實際優先序設計,審查時應查看完整處理順序與所有相關區段。
介面群組建立的是共同規則入口,不會合併成員介面的廣播域。封包從任一成員介面進入 OPNsense 時,都會先比對該群組的規則。例如 SERVICE 是 LAB_INTERNAL 的成員,10.77.20.31 向 SERVICE 介面的 10.77.20.1 查詢 DNS 時,封包從 SERVICE 進入,便會比對 LAB_INTERNAL → This Firewall TCP/UDP 53 Pass。由管理用 LAN 進入的封包不屬於這個群組,因此不會套用這條群組規則。
Alias 是可以重複引用的命名集合,常見類型包括:

圖(四)不同 Alias 類型保存不同形式的位址或連接埠資料
圖中由左至右分別是 Alias 類型、可供規則引用的名稱,以及實際展開內容。Host Alias 可以集合多台主機,Network Alias 保存一個或多個 CIDR 網段,Port Alias 集合服務連接埠。Network Group 則把既有的 Host、Network 或其他 Network Group Alias 再組成較大的位址集合,不能混入 Port Alias。
例如 PG_NODES 代表 10.77.30.11~10.77.30.13,比在多條規則中散落三個 PostgreSQL IP 更容易閱讀。圖中的 WEB_PORTS 則集中表示 TCP 80 與 443。Alias 只保存資料。防火牆規則引用 Alias 後才會形成實際權限。
名稱應描述穩定角色,Description 則記錄用途。修改一個 Alias 會同時影響所有引用它的規則,因此變更前要先查詢引用關係,變更後再到 Firewall → Diagnostics → Aliases 檢查實際展開內容。
介面群組組合的是介面,目的是讓多個介面套用共同規則。本系列把 SERVICE、DATABASE、BACKUP 與 BASTION 加入 LAB_INTERNAL,只用來承接 DNS、NTP、內部網段阻擋與套件更新等共同基準。
前面提到,網路層的最小權限要同時縮小來源、目的、協定與目的連接埠。沒有符合明確允許條件的流量,則交由預設拒絕或明確的阻擋規則處理。
製作防火牆規則時,可以依下列流程把業務需求拆成規則欄位,並規劃完成後的驗證方式:

圖(五)從業務需求拆解防火牆規則條件並規劃驗證方式
DNS、NTP、套件更新、監控、備份與管理連線都要明確記錄,並以具體的來源、目的與服務取代 any to any。必要流量尚未盤點完成前,直接收緊規則可能中斷名稱解析、校時、更新或管理路徑。
規則順序應先放必要而精確的允許規則,再放明確的區域間阻擋規則,最後才放較廣泛的外部服務允許規則。例如內部網段阻擋規則必須位於「到任何目的地的 TCP 80/443」之前,否則後者也可能允許存取其他內部區域的 Web 服務。
規則紀錄(Log)保存啟用紀錄的規則命中結果。Firewall → Log Files → Live View 可以查看介面、來源、目的、動作與規則 ID(Rule ID),適合確認封包是被哪一條規則允許或阻擋。
狀態式規則(Stateful Rule)通常只在建立狀態的第一個封包留下規則紀錄,後續封包沿用狀態時不會持續出現在 Live View。因此沒有新紀錄不代表沒有流量。紀錄應保留給重要管理入口、預期遭拒的負向測試與需要稽核的區域邊界,避免大量低價值紀錄干擾排錯。
Firewall → Diagnostics → States 顯示目前由 pf 追蹤的連線狀態,以及來源、目的、協定、連接埠與命中的規則。它適合判斷回程流量是否正在沿用既有狀態,也能解釋規則修改後舊連線為何仍可通過。
狀態表只顯示已建立且尚未逾時的連線。需要重新測試規則時,只移除與測試流量相關的狀態,避免中斷其他使用者連線。
Interfaces → Diagnostics → Packet Capture 直接擷取指定介面上的封包,能確認封包是否抵達 OPNsense、從哪個介面進入,以及是否從預期介面離開。封包擷取看得見實際流量,但不會直接告訴管理者是哪一條防火牆規則做出決定,需要與規則紀錄及狀態表交叉比對。

圖(六)封包擷取可依介面與流量條件縮小觀察範圍
排錯時先用規則紀錄確認規則命中結果,再用狀態表確認既有連線。若路徑不清楚,最後使用封包擷取比較各介面。三種工具各自提供不同證據,排錯時應交叉使用。
每一條重要允許規則都要搭配負向測試。除了確認正確來源能連線,也要改用錯誤來源、錯誤目的地或錯誤連接埠,確認預設拒絕或明確的 Block 確實接住不符合條件的流量。
不同 VLAN 已經形成可路由的介面邊界,今天接著把重複使用的網段、主機與連接埠整理成 Alias,再將四個服務 VLAN 納入 LAB_INTERNAL 介面群組,建立共同的 DNS、NTP、內部網段阻擋與套件更新規則。
服務端點尚未全部建立,因此今天只驗證 Alias、群組成員與規則順序是否正確套用。跨 VLAN 成功結果與服務專屬規則、WAN Port Forward 留待服務端點建立後測試與設定。
GitHub 詳細實作文件:Day 14|從預設拒絕到最小權限:OPNsense 防火牆的別名與規則順序
LAB_INTERNAL。LAB_INTERNAL,沒有誤建成較早比對的浮動規則。config.xml 備份。到 Firewall → Aliases 建立 INTERNAL_NETWORKS,Type 選 Network(s),並將五個 CIDR 分別加入 Content:
10.77.10.0/24
10.77.20.0/24
10.77.30.0/24
10.77.40.0/24
10.77.50.0/24

圖(七)INTERNAL_NETWORKS 將五個內部網段集中成一個 Network Alias
接著建立已規劃主機使用的 Host Alias:
| Alias | Content |
|---|---|
PROXY_NODES |
10.77.20.21、10.77.20.22 |
APP_NODES |
10.77.20.31、10.77.20.32 |
PG_NODES |
10.77.30.11、10.77.30.12、10.77.30.13 |
CA_NODE |
10.77.30.10 |
BACKUP_NODE |
10.77.40.11 |
BASTION_HOST |
10.77.50.11 |
WEB_VIP |
10.77.20.10 |
DB_RW_VIP |
10.77.20.11 |
DB_RO_VIP |
10.77.20.12 |
BASTION_TARGETS |
10.77.20.21、10.77.20.22、10.77.30.11~10.77.30.13、10.77.40.11 |
Port Alias 則保存規則會重複引用的目的連接埠:
| Alias | Content |
|---|---|
WEB_PORTS |
80、443 |
PGSQL_PORT |
5432 |
PATRONI_API |
8008 |
ETCD_PORTS |
2379、2380 |
BASIC_OUTBOUND_TCP |
80、443 |

圖(八)Alias 清單顯示已建立的部分 Host 與 Port Alias
Alias 只替條件命名,本身不會建立主機或放行流量。Port Alias 也只保存連接埠號碼。TCP 或 UDP 由引用它的規則決定。BASTION_TARGETS 則是跳板機在網路層可接近的主機上限,個別使用者由 OpenSSH 設定、目標帳號與公鑰繼續限制。
到 Firewall → Groups 建立 LAB_INTERNAL,Members 選擇 SERVICE、DATABASE、BACKUP 與 BASTION,不加入管理用 LAN。No GUI groups 保持未勾選,儲存後按下 Apply。

圖(九)LAB_INTERNAL 只包含四個服務 VLAN,不包含管理用 LAN
介面群組讓四個成員介面共用一組基礎規則。各 VLAN 保持獨立廣播域,跨 VLAN 流量依防火牆規則決定。
到 Firewall → Rules,Interface 只選 LAB_INTERNAL,依序建立並排列以下四條規則:
| 順序 | Action | Protocol | Source | Destination | Destination Port | 用途 |
|---|---|---|---|---|---|---|
| 1 | Pass | TCP/UDP | LAB_INTERNAL network |
This Firewall |
53 | 使用防火牆提供的 DNS |
| 2 | Pass | UDP | LAB_INTERNAL network |
This Firewall |
123 | 使用防火牆提供的 NTP |
| 3 | Block+Log | any | LAB_INTERNAL network |
INTERNAL_NETWORKS |
any | 接住未明確允許的跨區流量 |
| 4 | Pass | TCP | LAB_INTERNAL network |
any | BASIC_OUTBOUND_TCP |
允許 HTTP/HTTPS 對外更新 |

圖(十)四條共同規則位於 LAB_INTERNAL 的 Group rules 區段
規則啟用 Quick 後,第一條符合條件的規則就會決定結果。內部網段 Block 必須位於外部 HTTP/HTTPS Pass 之前,否則目的為其他內部網段 TCP 80/443 的流量可能先被較寬的對外規則允許。後續若建立服務專屬的跨區 Pass,要使用明確的來源、目的與連接埠,並放在內部網段 Block 上方。
在規則畫面以 LAB_INTERNAL 篩選,確認四條規則顯示在 Group rules,順序與上表一致。再開啟其中一條規則,確認 Interface 只有 LAB_INTERNAL。同時選取 SERVICE、DATABASE、BACKUP 與 BASTION 會讓規則進入較早處理的浮動規則區段,改變原本規劃的比對順序。
圖(七)至圖(十)可以證明設定結構與順序已建立。規則是否真的依條件允許或阻擋封包,還要從實際來源端送出流量,並配合用戶端結果與 OPNsense 規則紀錄交叉確認。
以下畫面是在 app01 與 pg01 建立後補測,用來驗證本日規則的實際行為。從 SERVICE 網段的 app01 測試防火牆的 TCP 53 連線,再嘗試連向 DATABASE 網段未授權的 TCP 5433。前者應成功,後者應逾時:
nc -vz -w 3 10.77.20.1 53
nc -vz -w 3 10.77.30.11 5433

圖(十一)從 SERVICE 網段驗證允許的 TCP 53 連線與未授權的跨 VLAN 連線
接著到 Firewall → Log Files → Live View,使用來源 10.77.20.31 與目的 10.77.30.11 篩選,再執行一次 TCP 5433 測試。畫面應顯示封包被 Block LAB_INTERNAL to internal networks 規則阻擋:

圖(十二)Live View 確認未授權跨 VLAN 流量命中內部網段阻擋規則
成功連到 TCP 53 只驗證 TCP 路徑與監聽端點。完整 DNS 驗證應實際送出查詢。TCP 5433 逾時搭配 Live View 的 Block 紀錄後,可以確認未授權跨區流量確實被第三條規則接住。
| 驗證 | 主要證據 | 可以得到的結論 |
|---|---|---|
| 驗證一:Alias | Alias 編輯畫面與清單 | 網段、主機與連接埠條件已建立並可供規則引用 |
| 驗證二:介面群組 | LAB_INTERNAL 的 Members |
四個服務 VLAN 共用基礎政策,管理用 LAN 不在群組內 |
| 驗證三:共同規則 | LAB_INTERNAL 規則清單 |
DNS、NTP、內部網段阻擋與外部更新規則依序存在 |
| 驗證四:比對區段 | Group rules 與規則的 Interface |
規則套用於介面群組,沒有誤建成浮動規則 |
| 驗證五:規則行為 | app01 連線結果與 Live View | 允許的 TCP 53 連線可通過,未授權跨 VLAN 流量命中內網 Block |
完成後到 System → Configuration → Backups 下載 config.xml。設定備份可能含有敏感資料,不應提交到 Git。
LAB_INTERNAL 介面群組。下一篇〈Day 15|SSH 跳板機的部署與權限設計(上):Linux 路由切換與建立安全入口〉會先替 PVE 建立必要的出口規則,再逐台將一般對外路徑切換到 OPNsense。確認管理路徑可用後,再部署並強化唯一公開的 SSH 跳板入口。