2021-05-12 14:32:11
理解多區域設定中的 firewalld
現在的新聞裡充斥著伺服器被攻擊和資料失竊事件。對於一個閱讀過安全公告部落格的人來說,通過存取錯誤設定的伺服器,利用最新暴露的安全漏洞或通過竊取的密碼來獲得系統控制權,並不是件多困難的事情。在一個典型的 Linux 伺服器上的任何網際網路服務都可能存在漏洞,允許未經授權的系統存取。
因為在應用程式層面上強化系統以防範任何可能的威脅是不可能做到的事情,而防火牆可以通過限制對系統的存取提供了安全保證。防火牆基於源 IP、目標埠和協定來過濾入站包。因為這種方式中,僅有幾個 IP/埠/協定的組合與系統互動,而其它的方式做不到過濾。
Linux 防火牆是通過 netfilter 來處理的,它是核心級別的框架。這十幾年來,iptables 被作為 netfilter 的使用者態抽象層(LCTT 譯註: userland,一個基本的 UNIX 系統是由 kernel 和 userland 兩部分構成,除 kernel 以外的稱為 userland)。iptables 將包通過一系列的規則進行檢查,如果包與特定的 IP/埠/協定的組合匹配,規則就會被應用到這個包上,以決定包是被通過、拒絕或丟棄。
Firewalld 是最新的 netfilter 使用者態抽象層。遺憾的是,由於缺乏描述多區域設定的文件,它強大而靈活的功能被低估了。這篇文章提供了一個範例去改變這種情況。
Firewalld 的設計目標
firewalld 的設計者認識到大多數的 iptables 使用案例僅涉及到幾個單播源 IP,僅讓每個符合白名單的服務通過,而其它的會被拒絕。這種模式的好處是,firewalld 可以通過定義的源 IP 和/或網路介面將入站流量分類到不同區域zone。每個區域基於指定的準則按自己設定去通過或拒絕包。
另外的改進是基於 iptables 進行語法簡化。firewalld 通過使用服務名而不是它的埠和協定去指定服務,使它更易於使用,例如,是使用 samba 而不是使用 UDP 埠 137 和 138 和 TCP 埠 139 和 445。它進一步簡化語法,消除了 iptables 中對語句順序的依賴。
最後,firewalld 允許互動式修改 netfilter,允許防火牆獨立於儲存在 XML 中的永久設定而進行改變。因此,下面的的臨時修改將在下次重新載入時被覆蓋:
# firewall-cmd <some modification>
而,以下的改變在重載入後會永久儲存:
# firewall-cmd --permanent <some modification>
# firewall-cmd --reload
區域
在 firewalld 中最上層的組織是區域。如果一個包匹配區域相關聯的網路介面或源 IP/掩碼 ,它就是區域的一部分。可用的幾個預定義區域:
# firewall-cmd --get-zones
block dmz drop external home internal public trusted work
任何設定了一個網路介面和/或一個源的區域就是一個活動區域active zone。列出活動的區域:
# firewall-cmd --get-active-zones
public
interfaces: eno1 eno2
Interfaces (介面)是系統中的硬體和虛擬的網路介面卡的名字,正如你在上面的範例中所看到的那樣。所有的活動的介面都將被分配到區域,要麼是預設的區域,要麼是使用者指定的一個區域。但是,一個介面不能被分配給多於一個的區域。
在預設設定中,firewalld 設定所有介面為 public 區域,並且不對任何區域設定源。其結果是,public
區域是唯一的活動區域。
Sources (源)是入站 IP 地址的範圍,它也可以被分配到區域。一個源(或重疊的源)不能被分配到多個區域。這樣做的結果是產生一個未定義的行為,因為不清楚應該將哪些規則應用於該源。
因為指定一個源不是必需的,任何包都可以通過介面匹配而歸屬於一個區域,而不需要通過源匹配來歸屬一個區域。這表示通過使用優先順序方式,優先到達多個指定的源區域,稍後將詳細說明這種情況。首先,我們來檢查 public
區域的設定:
# firewall-cmd --zone=public--list-all
public(default, active)
interfaces: eno1 eno2
sources:
services: dhcpv6-client ssh
ports:
masquerade:no
forward-ports:
icmp-blocks:
rich rules:
# firewall-cmd --permanent --zone=public--get-target
default
逐行說明如下:
public (default, active)
表示public
區域是預設區域(當介面啟動時會自動預設),並且它是活動的,因為,它至少有一個介面或源分配給它。interfaces: eno1 eno2
列出了這個區域上關聯的介面。sources:
列出了這個區域的源。現在這裡什麼都沒有,但是,如果這裡有內容,它們應該是這樣的格式 xxx.xxx.xxx.xxx/xx。services: dhcpv6-client ssh
列出了允許通過這個防火牆的服務。你可以通過執行firewall-cmd --get-services
得到一個防火牆預定義服務的詳細列表。ports:
列出了一個允許通過這個防火牆的目標埠。它是用於你需要去允許一個沒有在 firewalld 中定義的服務的情況下。masquerade: no
表示這個區域是否允許 IP 偽裝。如果允許,它將允許 IP 轉發,它可以讓你的計算機作為一個路由器。forward-ports:
列出轉發的埠。icmp-blocks:
阻塞的 icmp 流量的黑名單。rich rules:
在一個區域中優先處理的高階設定。default
是目標區域,它決定了與該區域匹配而沒有由上面設定中顯式處理的包的動作。
一個簡單的單區域設定範例
如果只是簡單地鎖定你的防火牆。簡單地在刪除公共區域上當前允許的服務,並重新載入:
# firewall-cmd --permanent --zone=public--remove-service=dhcpv6-client
# firewall-cmd --permanent --zone=public--remove-service=ssh
# firewall-cmd --reload
在下面的防火牆上這些命令的結果是:
# firewall-cmd --zone=public--list-all
public(default, active)
interfaces: eno1 eno2
sources:
services:
ports:
masquerade:no
forward-ports:
icmp-blocks:
rich rules:
# firewall-cmd --permanent --zone=public--get-target
default
本著儘可能嚴格地保證安全的精神,如果發生需要在你的防火牆上臨時開放一個服務的情況(假設是 ssh),你可以增加這個服務到當前對談中(省略 --permanent
),並且指示防火牆在一個指定的時間之後恢復修改:
# firewall-cmd --zone=public--add-service=ssh--timeout=5m
這個 timeout
選項是一個以秒(s
)、分(m
)或小時(h
)為單位的時間值。
目標
當一個區域處理它的源或介面上的一個包時,但是,沒有處理該包的顯式規則時,這時區域的目標target決定了該行為:
ACCEPT
:通過這個包。%%REJECT%%
:拒絕這個包,並返回一個拒絕的回復。DROP
:丟棄這個包,不回復任何資訊。default
:不做任何事情。該區域不再管它,把它踢到“樓上”。
在 firewalld 0.3.9 中有一個 bug (已經在 0.3.10 中修復),對於一個目標是除了“default”以外的源區域,不管允許的服務是什麼,這的目標都會被應用。例如,一個使用目標 DROP
的源區域,將丟棄所有的包,甚至是白名單中的包。遺憾的是,這個版本的 firewalld 被打包到 RHEL7 和它的衍生版中,使它成為一個相當常見的 bug。本文中的範例避免了可能出現這種行為的情況。
優先權
活動區域中扮演兩個不同的角色。關聯介面行為的區域作為介面區域,並且,關聯源行為的區域作為源區域(一個區域能夠扮演兩個角色)。firewalld 按下列順序處理一個包:
- 相應的源區域。可以存在零個或一個這樣的區域。如果這個包滿足一個富規則rich rule、服務是白名單中的、或者目標沒有定義,那麼源區域處理這個包,並且在這裡結束。否則,向上傳遞這個包。
- 相應的介面區域。肯定有一個這樣的區域。如果介面處理這個包,那麼到這裡結束。否則,向上傳遞這個包。
- firewalld 預設動作。接受 icmp 包並拒絕其它的一切。
這裡的關鍵資訊是,源區域優先於介面區域。因此,對於多區域的 firewalld 設定的一般設計模式是,建立一個優先源區域來允許指定的 IP 對系統服務的提升存取,並在一個限制性介面區域限制其它存取。
一個簡單的多區域範例
為演示優先權,讓我們在 public
區域中將 http
替換成 ssh
,並且為我們喜歡的 IP 地址,如 1.1.1.1,設定一個預設的 internal
區域。以下的命令完成這個任務:
# firewall-cmd --permanent --zone=public--remove-service=ssh
# firewall-cmd --permanent --zone=public--add-service=http
# firewall-cmd --permanent --zone=internal --add-source=1.1.1.1
# firewall-cmd --reload
這些命令的結果是生成如下的設定:
# firewall-cmd --zone=public--list-all
public(default, active)
interfaces: eno1 eno2
sources:
services: dhcpv6-client http
ports:
masquerade:no
forward-ports:
icmp-blocks:
rich rules:
# firewall-cmd --permanent --zone=public--get-target
default
# firewall-cmd --zone=internal --list-all
internal (active)
interfaces:
sources:1.1.1.1
services: dhcpv6-client mdns samba-client ssh
ports:
masquerade:no
forward-ports:
icmp-blocks:
rich rules:
# firewall-cmd --permanent --zone=internal --get-target
default
在上面的設定中,如果有人嘗試從 1.1.1.1 去 ssh
,這個請求將會成功,因為這個源區域(internal
)被首先應用,並且它允許 ssh
存取。
如果有人嘗試從其它的地址,如 2.2.2.2,去存取 ssh
,它不是這個源區域的,因為和這個源區域不匹配。因此,這個請求被直接轉到介面區域(public
),它沒有顯式處理 ssh
,因為,public 的目標是 default
,這個請求被傳遞到預設動作,它將被拒絕。
如果 1.1.1.1 嘗試進行 http
存取會怎樣?源區域(internal
)不允許它,但是,目標是 default
,因此,請求將傳遞到介面區域(public
),它被允許存取。
現在,讓我們假設有人從 3.3.3.3 拖你的網站。要限制從那個 IP 的存取,簡單地增加它到預定義的 drop
區域,正如其名,它將丟棄所有的連線:
# firewall-cmd --permanent --zone=drop --add-source=3.3.3.3
# firewall-cmd --reload
下一次 3.3.3.3 嘗試去存取你的網站,firewalld 將轉發請求到源區域(drop
)。因為目標是 DROP
,請求將被拒絕,並且它不會被轉發到介面區域(public
)。
一個實用的多區域範例
假設你為你的組織的一台伺服器設定防火牆。你希望允許全世界使用 http
和 https
的存取,你的組織(1.1.0.0/16)和工作組(1.1.1.0/8)使用 ssh
存取,並且你的工作組可以存取 samba
服務。使用 firewalld 中的區域,你可以用一個很直觀的方式去實現這個設定。
public
這個命名,它的邏輯似乎是把全世界存取指定為公共區域,而 internal
區域用於為本地使用。從在 public
區域內設定使用 http
和 https
替換 dhcpv6-client
和 ssh
服務來開始:
# firewall-cmd --permanent --zone=public--remove-service=dhcpv6-client
# firewall-cmd --permanent --zone=public--remove-service=ssh
# firewall-cmd --permanent --zone=public--add-service=http
# firewall-cmd --permanent --zone=public--add-service=https
然後,取消 internal
區域的 mdns
、samba-client
和 dhcpv6-client
服務(僅保留 ssh
),並增加你的組織為源:
# firewall-cmd --permanent --zone=internal --remove-service=mdns
# firewall-cmd --permanent --zone=internal --remove-service=samba-client
# firewall-cmd --permanent --zone=internal --remove-service=dhcpv6-client
# firewall-cmd --permanent --zone=internal --add-source=1.1.0.0/16
為容納你提升的 samba
的許可權,增加一個富規則:
# firewall-cmd --permanent --zone=internal --add-rich-rule='rule family=ipv4 source address="1.1.1.0/8" service name="samba" accept'
最後,重新載入,把這些變化拉取到對談中:
# firewall-cmd --reload
僅剩下少數的細節了。從一個 internal
區域以外的 IP 去嘗試通過 ssh
到你的伺服器,結果是回復一個拒絕的訊息。它是 firewalld 預設的。更為安全的作法是去顯示不活躍的 IP 行為並丟棄該連線。改變 public
區域的目標為 DROP
,而不是 default
來實現它:
# firewall-cmd --permanent --zone=public--set-target=DROP
# firewall-cmd --reload
但是,等等,你不再可以 ping 了,甚至是從內部區域!並且 icmp (ping 使用的協定)並不在 firewalld 可以列入白名單的服務列表中。那是因為,icmp 是第 3 層的 IP 協定,它沒有埠的概念,不像那些綑綁了埠的服務。在設定公共區域為 DROP
之前,ping 能夠通過防火牆是因為你的 default
目標通過它到達防火牆的預設動作(default),即允許它通過。但現在它已經被刪除了。
為恢復內部網路的 ping,使用一個富規則:
# firewall-cmd --permanent --zone=internal --add-rich-rule='rule protocol value="icmp" accept'
# firewall-cmd --reload
結果如下,這裡是兩個活動區域的設定:
# firewall-cmd --zone=public--list-all
public(default, active)
interfaces: eno1 eno2
sources:
services: http https
ports:
masquerade:no
forward-ports:
icmp-blocks:
rich rules:
# firewall-cmd --permanent --zone=public--get-target
DROP
# firewall-cmd --zone=internal --list-all
internal (active)
interfaces:
sources:1.1.0.0/16
services:ssh
ports:
masquerade:no
forward-ports:
icmp-blocks:
rich rules:
rule family=ipv4 source address="1.1.1.0/8" service name="samba" accept
rule protocol value="icmp" accept
# firewall-cmd --permanent --zone=internal --get-target
default
這個設定演示了一個三層巢狀的防火牆。最外層,public
,是一個介面區域,包含全世界的存取。緊接著的一層,internal
,是一個源區域,包含你的組織,它是 public
的一個子集。最後,一個富規則增加到最內層,包含了你的工作組,它是 internal
的一個子集。
這裡的關鍵資訊是,當在一個場景中可以突破到巢狀層,最外層將使用介面區域,接下來的將使用一個源區域,並且在源區域中額外使用富規則。
偵錯
firewalld 採用直觀正規化來設計防火牆,但比它的前任 iptables 更容易產生歧義。如果產生無法預料的行為,或者為了更好地理解 firewalld 是怎麼工作的,則可以使用 iptables 描述 netfilter 是如何設定操作的。前一個範例的輸出如下,為了簡單起見,將輸出和紀錄檔進行了修剪:
# iptables -S
-P INPUT ACCEPT
...(forward and output lines)...
-N INPUT_ZONES
-N INPUT_ZONES_SOURCE
-N INPUT_direct
-N IN_internal
-N IN_internal_allow
-N IN_internal_deny
-N IN_public
-N IN_public_allow
-N IN_public_deny
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j INPUT_ZONES_SOURCE
-A INPUT -j INPUT_ZONES
-A INPUT -p icmp -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -j REJECT --reject-with icmp-host-prohibited
...(forward and output lines)...
-A INPUT_ZONES -i eno1 -j IN_public
-A INPUT_ZONES -i eno2 -j IN_public
-A INPUT_ZONES -j IN_public
-A INPUT_ZONES_SOURCE -s 1.1.0.0/16-g IN_internal
-A IN_internal -j IN_internal_deny
-A IN_internal -j IN_internal_allow
-A IN_internal_allow -p tcp -m tcp --dport 22-m conntrack --ctstate NEW -j ACCEPT
-A IN_internal_allow -s 1.1.1.0/8-p udp -m udp --dport 137-m conntrack --ctstate NEW -j ACCEPT
-A IN_internal_allow -s 1.1.1.0/8-p udp -m udp --dport 138-m conntrack --ctstate NEW -j ACCEPT
-A IN_internal_allow -s 1.1.1.0/8-p tcp -m tcp --dport 139-m conntrack --ctstate NEW -j ACCEPT
-A IN_internal_allow -s 1.1.1.0/8-p tcp -m tcp --dport 445-m conntrack --ctstate NEW -j ACCEPT
-A IN_internal_allow -p icmp -m conntrack --ctstate NEW -j ACCEPT
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public -j DROP
-A IN_public_allow -p tcp -m tcp --dport 80-m conntrack --ctstate NEW -j ACCEPT
-A IN_public_allow -p tcp -m tcp --dport 443-m conntrack --ctstate NEW -j ACCEPT
在上面的 iptables 輸出中,新的鏈(以 -N
開始的行)是被首先宣告的。剩下的規則是附加到(以 -A
開始的行) iptables 中的。已建立的連線和本地流量是允許通過的,並且入站包被轉到 INPUT_ZONES_SOURCE
鏈,在那裡如果存在相應的區域,IP 將被傳送到那個區域。從那之後,流量被轉到 INPUT_ZONES
鏈,從那裡它被路由到一個介面區域。如果在那裡它沒有被處理,icmp 是允許通過的,無效的被丟棄,並且其餘的都被拒絕。
結論
firewalld 是一個文件不足的防火牆設定工具,它的功能遠比大多數人認識到的更為強大。以創新的區域正規化,firewalld 允許系統管理員去分解流量到每個唯一處理它的分類中,簡化了設定過程。因為它直觀的設計和語法,它在實踐中不但被用於簡單的單一區域中也被用於複雜的多區域設定中。
via: https://www.linuxjournal.com/content/understanding-firewalld-multi-zone-configurations
作者:Nathan Vance 譯者:qhwdw 校對:wxy
本文永久更新連結地址:http://www.linuxidc.com/Linux/2017-11/148795.htm
相關文章