Linux中國

理解多區域配置中的 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 按下列順序處理一個包:

  1. 相應的源區域。可以存在零個或一個這樣的區域。如果這個包滿足一個 富規則 rich rule 、服務是白名單中的、或者目標沒有定義,那麼源區域處理這個包,並且在這裡結束。否則,向上傳遞這個包。
  2. 相應的介面區域。肯定有一個這樣的區域。如果介面處理這個包,那麼到這裡結束。否則,向上傳遞這個包。
  3. 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)。

一個實用的多區域示例

假設你為你的組織的一台伺服器配置防火牆。你希望允許全世界使用 httphttps 的訪問,你的組織(1.1.0.0/16)和工作組(1.1.1.0/8)使用 ssh 訪問,並且你的工作組可以訪問 samba 服務。使用 firewalld 中的區域,你可以用一個很直觀的方式去實現這個配置。

public 這個命名,它的邏輯似乎是把全世界訪問指定為公共區域,而 internal 區域用於為本地使用。從在 public 區域內設置使用 httphttps 替換 dhcpv6-clientssh 服務來開始:

# 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 區域的 mdnssamba-clientdhcpv6-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=&apos;rule family=ipv4 source address="1.1.1.0/8" service name="samba" accept&apos;

最後,重新載入,把這些變化拉取到會話中:

# 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=&apos;rule protocol value="icmp" accept&apos;
# 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

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
1
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國