如果有人嘗試從其它的地址,如 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 允許系統管理員去分解流量到每個唯一處理它的分類中,簡化了配置過程。因為它直觀的設計和語法,它在實踐中不但被用于簡單的單一區域中也被用于復雜的多區域配置中。