伺服器就得要使用防火牆來處理未公開的服務!同時,辦公室或企業內部,要分不同資安條件,也需要用到它!
Linux 的防火牆機制直接整合在 Linux 核心當中,稱為 Netfilter 的防火牆機制!達成這個防火牆機制的服務非常多! 為了簡化設定,不同的 distributions 提供了很多不同的管理服務~但是基本上萬變不離其宗。所以,鳥哥將以 iptables 為基礎開始介紹, 然後才會談到簡單的 firewalld 以及較新的 nftables 這兩個 Red Hat 建議的防火牆管理機制。事實上,大部分的 distributions 都還是保留著 iptables 的管理服務模式,所以,鳥哥認為,先了解一下這東西,應該沒有什麼壞處!
『資料安全』應該有所謂的實體資料安全與網路安全,實體資料安全例如存放的磁碟硬體系統、檔案系統等等所管理,為了讓資料保存較不容易損毀/遺失, 適當的磁碟陣列與備份機制就顯得很重要。資料的網路安全則應該是指誰有權限來讀取這份資料,在本機上,我們主要透過 rwx 權限設計與帳號/群組的規範, 來限制讀寫的權限。而在本機之外的存取權限,則主要是透過網路伺服器服務來提供相關功能,並透過服務本身的存取規範,或透過防火牆機制, 來限制不同的存取權限。防火牆就是透過訂定一些有順序的規則,並管制進入到我們網域內的主機 (或者可以說是網域) 資料封包的一種機制!更廣義的來說,只要能夠分析與過濾進出我們管理之網域的封包資料,就可以稱為防火牆。
防火牆又可以分為硬體防火牆與本機的軟體防火牆。硬體防火牆是由廠商設計好的主機硬體, 這部硬體防火牆內的作業系統主要以提供封包資料的過濾機制為主,並將其他不必要的功能拿掉。因為單純作為防火牆功能而已, 因此封包過濾的效率較佳。軟體防火牆本身就是在保護系統網路安全的一套軟體(或稱為機制),例如 Netfilter 與 nftables 都可以稱為軟體防火牆。
仔細分析第一章的圖 1.3.1 可以發現,封包進入本機時會通過:防火牆、伺服器軟體程序、檔案系統與權限、 SELinux 流程等。所以基本上,如果你的系統 (1)已經關閉不需要而且危險的服務; (2)已經將整個系統的所有軟體都保持在最新的狀態; (3)權限設定妥當且定時進行備份工作; (4)已經教育使用者具有良好的網路、系統操作習慣。 那麼你的系統實際上已經頗為安全了!要不要架設防火牆?那就見仁見智囉!
不過,畢竟網路世界是很複雜的,而 Linux 主機也不是一個簡單的東西,說不定哪一天你在進行某個軟體的測試時, 主機突然間就啟動了一個網路服務,如果你沒有管制該服務的使用範圍,那麼該服務很可能就會對所有 Internet 開放, 那就麻煩了!因為該服務可能可以允許任何人登入你的系統,那不是挺危險?
所以囉,防火牆能作什麼呢?防火牆最大的功能就是幫助你『限制某些服務的存取來源』!舉例來說: (1)你可以限制檔案傳輸服務 (FTP) 只在子網域內的主機才能夠使用,而不對整個 Internet 開放; (2)你可以限制整部 Linux 主機僅可以接受客戶端的 WWW 要求,其他的服務都關閉; (3)你還可以限制整部主機僅能主動對外連線。反過來說,若有用戶端對我們主機發送主動連線的封包狀態 (TCP 封包的 SYN flag) 就予以抵擋等等。這些就是最主要的防火牆功能了!
所以鳥哥認為,防火牆最重要的任務就是在規劃出:
當然啦,咱們 Linux 的防火牆軟體還可以進行更細部深入的 NAT (Network Address Translation) 的設定,並進行更彈性的 IP 封包偽裝功能,不過,對於單一主機的防火牆來說,最簡單的任務還是上面那三項就是了!所以,你需不需要防火牆呢? 理論上,當然需要!而且你必須要知道『你的系統哪些資料與服務需要保護』,針對需要受保護的服務來設定防火牆的規則吧!
Linux 上面的防火牆,除了可以進行上面的三項功能之外,也能作為路由器!就是剛剛談到的 NAT 技術!無論是針對本機的防護, 還是區域網路的防護,或者是路由的切換,這些防火牆的類型,大概可以分為封包過濾式以及代理伺服器等類型。
封包過濾式防火牆: 所謂的封包過濾,亦即是分析進入主機的網路封包,將封包的表頭資料捉出來進行分析,以決定該連線為放行或抵擋的機制。 由於這種方式可以直接分析封包表頭資料,所以包括硬體位址(MAC), 軟體位址 (IP), TCP, UDP, ICMP 等封包的資訊都可以進行過濾分析的功能, 因此用途非常的廣泛。與網路基礎相呼應的話,大概可以理解防火牆主要分析的是 OSI 七層協定的 2, 3, 4 層。至於目前常見的封包過濾機制, 大致有 Netfilter 與 nftable 兩種~Red Hat 官方建議使用新的 nftable 機制就是了。
代理伺服器: 代理伺服器基本上並不是防火牆~只是,代理伺服器的網路邏輯位置通常就是在防火牆的角色上,所以, 很容易被當成防火牆這樣~所謂的代理伺服器 (proxy) 就是一個服務,用戶端需要連網時,就呼叫 proxy,請 proxy 代為處理! 當 proxy 處理完畢之後,會將結果回傳給用戶端~看看連網情況,跟封包過濾式非常類似!所以,許多朋友才將 proxy 也視為一種防火牆。
強者我師父網中人曾經說過一個很有趣的比喻,怎麼說明封包過濾式防火牆與 proxy 伺服器的差異。現在,假設以你要去 google 查資料好了, 那假設你得要通過巷口檢查站 (路由,亦即封包過濾防火牆或 proxy 伺服器),才能夠去到 google 中心。這兩個服務的情況分別會是這樣:
目前一般都用封包過濾式防火牆啦~比較少使用 proxy 伺服器了!
封包過濾式防火牆主要可以針對 OSI 的 layer 2, 3, 4 進行管理~但是防火牆並不是萬能的!否則我們就不需要額外安裝防木馬、防毒軟體啊! 基本上,防火牆還是有些做不到的地方:
因此,在講防火牆之前,鳥哥在前面的章節才會先說明,我們一定要先關閉不必要的網路服務、升級全系統的軟體、做好權限方面的控管、 進行良好的備份規則。當然啦,企業內部人員的教育訓練應該是最重要的!大家要有良好的操作習慣喔!
Linux 核心的防火牆機制持續在變更當中,當然也是因為網路技術的進步與改變,核心防火牆機制也不得不做因應之故。 比較重大的變化是這樣的:
目前 Linux 核心已經是 6.x (2023) 版了,基本上,應該是使用較新的 nftables 才對~不過,因為 iptables 實在使用的太廣泛, 所以,即使到目前的版本中,兩者都還是同步提供啦!看你想用哪個就用哪個。但是,當使用 iptables 機制時,因為最早設計 iptables 時,還沒有 IPv6 的版本,早期只有針對 IPv4 的 iptables 防火牆功能。後來網路陸陸續續更新機制,搞到一個 iptables 需要載入一大堆模組! 連操作指令都需要有 iptables, ip6tables, ebtables, arptables 等等,程式碼變得很複雜。因此,在 3.13 核心版本之後,加入 nftables, 這個指令可以支援不同的網路類型,降低防火牆程式碼的重複性,改善錯誤回報的訊息功能,更好的執行效能等,因此目前建議使用 nftables 這個防火牆機制。
事實上,目前的 Linux 核心防火牆,都是使用 Netfilter 這個團隊所開發出來的專案,這個團隊的專案主要就是 iptables 與 nftables 啦! 如前所述,iptables 與 nftables 使用的模組並不相同,語法也不同~由於軟體會衝突,因此,只能選擇其中一個來運作喔!另外, 為了方便管理員架設自己的防火牆規則,所以 RHEL 也提供一個相對簡單的 firewalld 服務,這個服務內有區分數個領域 (zone), 不同領域有相對的預設功能,只要將網路卡放入相對的領域內,該網卡就擁有該領域的防火牆設定了!相當的簡易!也就是說, 目前 RHEL 9.x 衍生的 distributions 當中,提供可以設定的防火牆軟體,主要有這幾個:
也就是說,從 3.13 Linux 核心版本之後,建議都使用 nftables,使用的是 nft 管理工具~不過,為了向下相容,我們還是會從 iptables 開始介紹!等到大家了解 iptables 了,再轉來使用 firewalld 以及 nftables 囉!另外,nftables 適用於所有的系統上, 如果僅只是單機系統,則建議直接使用 firewalld 即可喔!
那麼 Linux 的防火牆,是怎麼判斷來自網路的封包到底是能不能進入我本機或者是某些環境的呢?基本上:『 封包過濾的防火牆就是根據封包的分析資料 "比對" 你預先定義的規則內容,若封包資料與規則內容相同則進行動作, 否則就繼續下一條規則的比對!』重點在那個『比對與分析順序』上。以底下的過濾流程圖示來解釋:
當封包通過防火牆的分析 (圖示上方中央 IP Filter) 後,最終結果可能是 (1) ACCEPT(接受) 或 (2) DROP(丟棄), 若動作為接受時,封包就順利進入到本機,開始進行服務的運作。如果是 DROP 的話,用戶端的連線就會被丟棄而無法使用本機服務了。 如圖所示,假定有 10 條規則,這 10 條規則依序建立完畢之後,封包的資料會先比對第 1 條,若符合設定值,就會執行第一個動作 (Action 1), 每個動作都可能是 ACCEPT/DROP,當開始動作之後,就不會繼續比對此規則後面的其他規則!若不符合第 1 條規則,則開始第 2 條規則比對。 以此類推,直到 10 條規則都比對完成,若還是無法執行動作,就會以預設的 Policy 政策動作來運作。預設的 Policy 政策同樣可以設定為接受或丟棄!
現在,假定你的防火牆底下依序有這幾條規則,且預設政策為接受 (ACCEPT) (底下的規則都是亂設定的,不是正確的設定方式! 目的是讓我們理解規則順序的重要性而已!)
假設:設定上面防火牆規則的系統稱為 server,且區域網路內有部系統稱為 client,且網際網路上有部系統為 interclient。 那麼來想一想,假設有底下發起連線的方式,先不要看答案,自己思考一下,該連線有沒有可能成功或失敗? 而成功或失敗的可能性來自於那一條防火牆規則!
說明如下:
說明如下:
說明如下:
看了原本設定的 7 條規則,你可能會以為你的伺服器對所有來源放行 WWW 服務,但是由於更優先的第 6 條規則的緣故, 第 7 條規則將永遠不會被使用!因為第 6 條拒絕全部連線,一定是符合所有的封包狀態!所以囉,規則順序就顯的非常重要啊! 至於規則與政策的設定上,一般我們建議使用比較嚴格的方式來處理,亦即是『關閉所有連線,開放特定服務』的設定機制較佳。 所以,規則的動作大部份都是接受,最後不符合所有規則的封包,就用預設政策去丟棄它!
為了實現區域網路的搭建,在我們的 Master 骨幹系統上,需要事先處理好全部需要的網路參數才好! 如第六章、圖 6.2-1 所示,我們會具有四個網路界面,三個有線及一個無線。 目前預計的網路界面參數設計如下 (WAN 需要跟你的實際網路環境搭配,至於 LAN, DMZ, AP 則可以使用鳥哥的建議設定即可):
習慣上,作為區域網路的管理端,大概使用的都是整個 IP 位址段的最後一個可用 IP 位址,因此,上述除了 WAN 的網卡之外, 其他的網段大部分都是最後一個 IP 位址的設定~另外,我們一直忘記設定各系統的主機名稱了... 在這裡補充處理~讓 Master 的主機名稱成為: master.vbird 好了!
# 1. 設定 Master 骨幹系統的主機名稱 [root@localhost ~]# hostnamectl hostname master.vbird [root@localhost ~]# hostnamectl Static hostname: master.vbird Icon name: computer-vm Chassis: vm 🖴 ..... [root@localhost ~]# vim /etc/hosts .... 192.168.201.249 cloud.vbird cloud 192.168.201.245 master.vbird master 192.168.10.254 masterap.vbird masterap 192.168.20.254 masterlan.vbird masterlan 192.168.30.254 masterdmz.vbird masterdmz 192.168.20.1 client001.vbird client001 192.168.30.1 server001.vbird server001
接下來,讓我們來設定好對外的網路、對內的兩個區域網路,以及無線 AP 架設吧!
# 1. 觀察網路狀態,刪除無用的網路設定: [root@master ~]# nmcli connection show NAME UUID TYPE DEVICE enp1s0 eddac43d-2bb6-4575-81b9-30477ef394e3 ethernet enp1s0 lo c57a4996-72fd-4af2-991d-f791d1bd204f loopback lo Wired connection 1 5fb71d7d-b9c7-3262-9e34-8ed1d24ef77d ethernet -- Wired connection 2 ccbb41fc-de77-3cf6-9b5a-84bbac325865 ethernet -- # 上面的 Wired connection XX 就是系統自己設定的!建議先拿掉自己重建! [root@master ~]# nmcli connection delete Wired\ connection\ 1 [root@master ~]# nmcli connection delete Wired\ connection\ 2 # 2. 先建立好 WAN 的環境: [root@master ~]# nmcli connection modify enp1s0 ipv4.method manual \ > ipv4.addresses 192.168.201.245/24 ipv4.gateway 192.168.201.254 \ > ipv4.dns 120.114.100.1,168.95.1.1 [root@master ~]# nmcli connection up enp1s0 [root@master ~]# ip addr show enp1s0 | grep 'inet ' inet 192.168.201.245/24 brd 192.168.201.255 scope global noprefixroute enp1s0 [root@master ~]# ip route show default via 192.168.201.254 dev enp1s0 proto static metric 100 192.168.201.0/24 dev enp1s0 proto kernel scope link src 192.168.201.245 metric 100 [root@master ~]# grep nameserver /etc/resolv.conf nameserver 120.114.100.1 nameserver 168.95.1.1 # 3. 開始處理 switch LAN 接的網路: [root@master ~]# nmcli connection add con-name enp2s0 type ethernet ifname enp2s0 \ > ipv4.method manual ipv4.addresses 192.168.20.254/24 [root@master ~]# nmcli connection up enp2s0 [root@master ~]# ip addr show enp2s0 | grep 'inet ' inet 192.168.20.254/24 brd 192.168.20.255 scope global noprefixroute enp2s0 # 4. 開始處理 switch DMZ 接的網路: [root@master ~]# nmcli connection add con-name enp3s0 type ethernet ifname enp3s0 \ > ipv4.method manual ipv4.addresses 192.168.30.254/24 [root@master ~]# nmcli connection up enp3s0 [root@master ~]# ip addr show enp3s0 | grep 'inet ' inet 192.168.30.254/24 brd 192.168.30.255 scope global noprefixroute enp3s0 # 5.1. 先確認無線 USB 網路卡在虛擬機器當中,是否支援 AP 模式: [root@master ~]# nmcli device DEVICE TYPE STATE CONNECTION enp1s0 ethernet connected enp1s0 enp2s0 ethernet connected enp2s0 enp3s0 ethernet connected epn3s0 lo loopback connected (externally) lo wlp7s0u1 wifi unmanaged -- [root@master ~]# nmcli -f WIFI-PROPERTIES.AP device show wlp7s0u1 WIFI-PROPERTIES.AP: yes # 確定是有支援 AP 模式的! # 5.2. 安裝 Wifi 模式,並且重新啟動: [root@master ~]# yum install NetworkManager-wifi [root@master ~]# systemctl restart NetworkManager # 5.3. 建立網卡、連線名稱與 SSID 還有密碼資料: [root@master ~]# nmcli device wifi hotspot ifname wlp7s0u1 con-name masterap ssid masterap \ > password "mylanIsveryGood" [root@master ~]# nmcli connection modify masterap ipv4.addresses 192.168.10.254/24 [root@master ~]# nmcli connection up masterap # 6. 最後檢測所的網卡對應的 IP 是否正確: [root@master ~]# ip addr show | egrep '^[0-9]|inet '| sed 's/<.*$//g' | sed 's/scop.*$//g' 1: lo: inet 127.0.0.1/8 2: enp1s0: inet 192.168.201.245/24 brd 192.168.201.255 3: enp2s0: inet 192.168.20.254/24 brd 192.168.20.255 4: enp3s0: inet 192.168.30.254/24 brd 192.168.30.255 5: wlp7s0u1: inet 192.168.10.254/24 brd 192.168.10.255 [root@master ~]# ip route show default via 192.168.201.254 dev enp1s0 proto static metric 100 192.168.10.0/24 dev wlp7s0u1 proto kernel scope link src 192.168.10.254 metric 600 192.168.20.0/24 dev enp2s0 proto kernel scope link src 192.168.20.254 metric 103 192.168.30.0/24 dev enp3s0 proto kernel scope link src 192.168.30.254 metric 104 192.168.201.0/24 dev enp1s0 proto kernel scope link src 192.168.201.245 metric 100
在這樣的情況下,Master 骨幹系統的網路參數應該就設定妥當了!當然,目前僅 master 骨幹系統本身可以自行對外連線, 內部的網路連線通通是不可行的!因為我們還沒有設定好 IP 位址的偽裝之故。同時,透過 AP 來的連線, 則是因為『找不到 IP 位址』的緣故而無法上網~接下來,我們就要一個一個處理設定了!
一般來說,作為路由器 (router, gateway) 功能的伺服器,才需要啟動核心的 IP 轉遞功能,否則不需要啟動這功能! IP 轉遞的目的,在於讓封包流向不同的網路界面。所以,底下的功能只需要在第六章、圖 6.2-1 當中的 Master 骨幹系統上面設定即可,其他單一任務的伺服器,不需要啟動此功能!
啟動 IP forward 功能很簡單,只需要使用 sysctl 的設定即可!建議直接多加一個設定檔於 /etc/sysctl.d/ 內就好。 最簡單的方法如下:
[root@master ~]# vim /etc/sysctl.d/server.conf net.ipv4.ip_forward = 1 [root@master ~]# sysctl -p /etc/sysctl.d/server.conf [root@master ~]# cat /proc/sys/net/ipv4/ip_forward 1 # 最後出現的訊息為 1,就是啟動了 IP 轉遞的功能了!
/etc/sysctl.d/*.conf 設定檔,在核心開機之後,會主動去喚醒設定的核心資源,所以寫入到這個設定檔之後,所有的設定值, 在下次開機都會持續存在喔!
如前所述,RHEL 9.x 的衍生產品中,防火牆的軟體至少提供了三種,而且你不能同時啟動任何兩種以上的防火牆機制, 否則,你的防火牆不是變個更嚴密,而是會神仙打架...相當困惑~因此,我們得要先將防火牆全部關閉後,僅啟動我們需要防火牆軟體才行。 第一個我們要先來討論的是 iptables 這個舊舊但是比較好用來理解防火牆設定的機制~我們可以透過 iptables 來達成單機防火牆, 也可以用它來處理後端主機的 IP 分享器功能以及外部連線到內部伺服器的方式~至於我們一定要了解的,就是所謂的 table (表格) 以及 chain (鏈) 的相關概念!尤其是後面的 nat 表格以及 POSTROUTING 鏈!相當重要!
其實目前 RHEL 9.x 的衍生產品中,已經沒有早期實際的 iptables 模組了,目前使用的其實是 nftables 向下相容的支援模組! 所以,我們要安裝的軟體,其實也不是以前的 iptables-services 了!而是 iptables-nft-services 才對!讓我們來查詢一下吧! 看看目前的系統支援的軟體名稱與說明!
# 1. 先查詢軟體名稱關鍵字,然後再查詢該軟體的說明! [root@master ~]# yum search iptable*serv* iptables-nft-services.noarch : Services for nft-variants of iptables, ebtables and arptables [root@master ~]# yum info iptables-nft-services.noarch Last metadata expiration check: 0:20:21 ago on Thu Nov 2 09:43:35 2023. Available Packages Name : iptables-nft-services Version : 1.8.8 Release : 6.el9_1 Architecture : noarch Size : 22 k Source : iptables-1.8.8-6.el9_1.src.rpm Repository : appstream Summary : Services for nft-variants of iptables, ebtables and arptables URL : https://www.netfilter.org/projects/iptables License : GPLv2 and Artistic 2.0 and ISC Description : Services for nft-variants of iptables, ebtables and arptables : This package provides the services iptables, ip6tables, arptables and ebtables : for use with iptables-nft which provides nft-variants of these tools. # 2. 安裝起來吧! [root@master ~]# yum install iptables-nft-services [root@master ~]# rpm -ql iptables-nft-services | egrep 'iptables($|[.-])' /etc/sysconfig/iptables # 主要的規則紀錄檔 /etc/sysconfig/iptables-config # 載入模組的紀錄檔 /usr/lib/systemd/system/iptables.service # 服務啟動的 systemd 設定檔 /usr/libexec/initscripts/legacy-actions/iptables /usr/libexec/iptables /usr/libexec/iptables/iptables.init # 3. 先關閉 firewalld, nftables 之後,啟動 iptables [root@master ~]# systemctl stop nftables [root@master ~]# systemctl disable nftables [root@master ~]# systemctl stop firewalld [root@master ~]# systemctl disable firewalld [root@master ~]# systemctl start iptables [root@master ~]# systemctl enable iptables # 很重要!一定要注意順序!不要的防火牆軟體要先關閉!最後才開需要的軟體!流程很重要! [root@master ~]# iptables-save # Generated by iptables-save v1.8.8 (nf_tables) on Thu Nov 2 10:29:26 2023 *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [40:4176] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT # Completed on Thu Nov 2 10:29:26 2023 # 最後 iptables-save 有看到輸出類似上面這樣的資料,就是成功啟動 iptables 了!
再次強調,防火牆軟體的啟動/關閉流程中,可以的話,將目前的所有看到的防火牆軟體先通通關掉 (甚至連原本要開的,也關掉!), 然後再僅針對需要的軟體項目去開啟即可!否則會有衝突!規則會亂掉...要注意!要注意!而當你使用 iptables-save 這個觀察的指令時, 看到如上的顯示,那就是順利啟動 iptables 服務啦!接下來,就讓我們開始來談談什麼是 filter 與 nat 表格, 然後再來開始處理怎麼操作 iptables 這個指令。
剛剛圖 7.1.3 的規則內容僅只是某個 chain (鏈) 而已!預設的情況下,iptables 至少就有三個表格, 包括管理本機進出的 filter 、管理後端主機 (防火牆內部的其他電腦) 的 nat 、管理特殊旗標使用的 mangle 等。 在本章,我們只會介紹 filter 與 nat,這兩個表格與其中鏈的用途分別是這樣的:
所有表格內的鏈都有點關係~也就是說,網路封包想要進入我們的伺服器,不是只有經過一群規則,則是得要經過好幾群規則! 每個鏈裡面都有自己的一群規則的意思。簡化過後剩下兩個表格 (filter, nat) 之後,各個鏈的關係有點像底下這樣:
你可以看到上圖有路徑方向,依據封包進入的行進,主要分為三個方向來查看:
在預設的情況底下,除了 filter 的 INPUT/FORWARD 鏈有規則之外,其他鏈規則都是空的!而且預設政策都是接受的! 這是針對單機環境底下的防火牆設定~所以,底下我們先來聊一聊,如果以 Linux 伺服器本身提供服務的環境下, 單機的防火牆規則應該是如何設定呢?
防火牆規則的制定要很注意,最好能夠在本機上面透過 tty1~tty6 來測試會比較好!這是因為...很多時候,我們會將自己的連線切斷... 然後就再也無法跟 Linux 主機連線了...還好,我們現在使用的是虛擬機器,所以,只要 KVM 母機器沒有亂動,隨意更動虛擬機的防火牆, 看起來算是還挺保險的~反正出狀況時,就透過 VNC 協定去登入虛擬機器的終端機即可~
剛剛接觸 iptables 時,鳥哥比較喜歡使用 iptables -L -n 來查看,不過後來發現,其實使用 iptables-save 更簡便!因此後來都直接用 iptables-save 了無過, iptables -L -n 真的很容易觀察~所以,還是先來看看怎麼用它吧!
# 1. 基本語法與說明 [root@master ~]# iptables [-t tables] [-L] [-nv] 選項與參數: -t :後面接 table ,例如 nat 或 filter ,若省略此項目,則使用預設的 filter -L :列出目前的 table 的規則 -n :不進行 IP 與 HOSTNAME 的反查,顯示訊息的速度會快很多! -v :列出更多的資訊,包括通過該規則的封包總位元數、相關的網路介面等 # 2. 列出目前 filter 表格三條鏈的規則 [root@master ~]# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT) target prot opt source destination REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT) target prot opt source destination
上面的輸出,以 Chain 開頭的項目,就是該鏈的預設政策,所以我們可以看到預設的三條鏈政策都是 ACCEPT 喔! 從下往上看, OUTPUT 鏈裡面沒有規則, FORWARD 鏈有一條規則,該規則為全部拒絕。INPUT 鏈底下有 5 條規則, 在說明規則之前,先來看看輸出的資訊裡面有什麼:
根據上面的說明,那麼 INPUT 鏈裡面的 5 條規則功能分別是:
上面的顯示中,除了第 3 條,其他的說明我們應該是看得懂的!第 3 跟第 5 條規則,似乎剛剛好相反~所以,如果只看第 3 條, 好像所有的規則都被放行了!那怎麼得了!但其實實際設定並不是這樣的!這就是直接使用 iptables -L 觀察時的困擾! 所以,建議你還是使用底下的 iptables-save 來觀察比較妥當:
# 直接列出規則來觀察 [root@master ~]# iptables-save # Generated by iptables-save v1.8.8 (nf_tables) on Thu Nov 2 11:54:11 2023 *filter <==星號 (*) 開頭的是表格名稱,這裡是 filter :INPUT ACCEPT [0:0] <==冒號 (:) 開頭的是鏈的名稱,三條內建的鏈 :FORWARD ACCEPT [0:0] <==鏈後面接的就是預設政策,這裡都是 ACCEPT :OUTPUT ACCEPT [129:15208] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT <==針對 INPUT 的規則 -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited <==針對 FORWARD 的規則 COMMIT # Completed on Thu Nov 2 11:54:11 2023
上表當中,出現 # 的都不用看,那僅是告知 iptables-save 的執行與輸出時間而已,至於 * 與 : 與 -A 開頭的就得要注意了:
關於 5 條規則的詳細資訊,之後的小節談到的綜合規則中,就會有所解釋~請耐心繼續往下瞧瞧~
你要建立新的規則之前,最好將以前的規則全部刪除後,依序建立一條一條的規則順序,畢竟規則之間是有順序差別的!這點很重要! 清除鏈與規則需要一條一條來執行,無法一口氣執行完畢。底下為預設的語法,同時請清除所有預設鏈的規則,並且觀察看看是否正確清除了?
# 1. 基本的語法 [root@master ~]# iptables -[F|X|Z] (清除規則|自訂鏈|統計資訊) [root@master ~]# iptables -P [INPUT|OUTPUT|FORWARD] [ACCEPT|DROP] (是大寫的 P) # 2. 實際清除規則後,查看規則 [root@master ~]# iptables -F [root@master ~]# iptables -X [root@master ~]# iptables -Z [root@master ~]# iptables-save *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT # 可以看到,沒有任何的 -A 出現! # 3. 讓 INPUT/OUTPUT 政策為 ACCEPT,但是 FORWARD 為 DROP [root@master ~]# iptables -P INPUT ACCEPT [root@master ~]# iptables -P OUTPUT ACCEPT [root@master ~]# iptables -P FORWARD DROP [root@master ~]# iptables-save *filter :INPUT ACCEPT [112:8104] :FORWARD DROP [0:0] :OUTPUT ACCEPT [43:4152] COMMIT
我們這邊僅是做個範例示意,並不是真的要將 FORWARD 設定為 DROP 喔!未來我們會再修改~
一般防火牆大多針對『要連線到本伺服器』來的封包做分析,所以,大部分都是針對 INPUT 這條鏈的設定啦! 那分析的封包有幾個地方要考慮:
複合式的語法有點像底下這樣,沒寫到的代表『通通接受』的意思:
[root@master ~]# iptables [-[ADI] INPUT] [-i lo|eth0|ens3] [-s IP/Netmask] [-d IP/Netmask] \ > [-p tcp|udp [--sport ports] [--dport ports]] [-j ACCEPT|REJECT|DROP]
上面的 -A INPUT 代表接續當前的規則之後新增此規則, -D INPUT 代表刪除後面寫的這條規則,-I INPUT 代表將此規則插入成為第一條, 既有的規則往後順延的意思。現在,讓我們回想一下原本規則當中的第 2~5 條的內容,分別解釋如下:
# 復原原有的第 2 條規則 [root@master ~]# iptables -A INPUT -p icmp -j ACCEPT # 不論什麼界面、不論什麼網域、只要是 ICMP 封包協定,此封包就放行 # 復原原有的第 3 條規則 [root@master ~]# iptables -A INPUT -i lo -j ACCEPT # 任何封包格式類型,只要透過 lo 這個裝置,該封包就予以放行,亦即 lo 為『信任裝置』 # 復原原有的第 4 條規則 [root@master ~]# iptables -A INPUT -m state --state NEW -p tcp --dport 22 -j ACCEPT # 不論什麼界面,無論什麼網域,只要是主動連線 (NEW) 且使用 port 22/tcp 封包,就予以放行 # 這種方式也是最常用來設定伺服器服務的規則! # 復原原有的第 5 條規則 [root@master ~]# iptables -A INPUT -j REJECT # 任何封包通通拒絕 [root@master ~]# iptables-save :INPUT ACCEPT [469:54016] :FORWARD DROP [0:0] :OUTPUT ACCEPT [271:27400] -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-port-unreachable COMMIT
如果你是在外部系統連線到這部虛擬機器練習防火牆的話,那麼上面第 5 條規則復原後,你的連線應該會立刻停止了... 而且可能無法繼續連線到這部主機上~這是因為缺乏 ESTABLISHED,RELATED 那條規則的關係!鳥哥也是因為這樣被暫停住... 如果是這樣的話,那妳可能得要將第 5 條復原的規則刪除才行!請從本機登入,然後刪除的方式為
# 無論你是否還可以連線,若無法使用終端機了,就從本機端登入後,執行下列指令刪除規則: [root@master ~]# iptables -D INPUT -j REJECT
現在就剩下 3 條規則才對~再來,想想看,現在使用上面的複合式指令,測試一下完成底下的防火牆規範:
# 1. 信任裝置,不論任何來源,連線到此裝置的封包,都會被放行! [root@master ~]# iptables -A INPUT -i enp2s0 -j ACCEPT # 2. 信任網域,只要來自這個網域,任何封包都可被放行 [root@master ~]# iptables -A INPUT -s 192.168.20.0/24 -j ACCEPT # 3./4. 限制服務的使用,只信任部份來源 (因為 ssh 太重要!只放行內部來源使用較佳) [root@master ~]# iptables -A INPUT -i enp3s0 -s 192.168.30.0/24 -p tcp --dport 22 -j ACCEPT [root@master ~]# iptables -A INPUT -i enp1s0 -s 192.168.201.0/24 -p tcp --dport 22 -j ACCEPT # 5. 放行根本機網頁伺服器有關的服務 [root@master ~]# iptables -A INPUT -p tcp --dport 80 -j ACCEPT [root@master ~]# iptables -A INPUT -p tcp --dport 443 -j ACCEPT # 6. 因為 DNS 同時具有 UDP 與 TCP 的連線功能,所以要放行兩條規則 [root@master ~]# iptables -A INPUT -p udp --dport 53 -j ACCEPT [root@master ~]# iptables -A INPUT -p tcp --dport 53 -j ACCEPT # 7. 拒絕不信任的用戶!這裡是拒絕喔!並不是丟棄! [root@master ~]# iptables -A INPUT -s 10.30.30.0/24 -j REJECT # 8. 放行連線到本機的 port 5901~5910,連續埠口可以這樣寫: [root@master ~]# iptables -A INPUT -p tcp --dport 5901:5910 -j ACCEPT
為什麼說上面的第 7 條可能放錯地方呢?看一下整體的流程,我們可以發現,原本想要拒絕 10.30.30.0/24 的所有連線,但是因為 port 80, 443, 53 等服務寫在前面,所以,基本上該網域還是可以使用我們伺服器的某些服務!因此,基本上,要拒絕的項目, 應該要挪到放行的規則之前!否則就無法順利被拒絕~
在目前的規則下,假設最後一條規則是全部拒絕時,那如果你在本機端連線到 google 去,但是沒有判斷從 google 回來的封包,你的這條連線就會被拒絕...這是因為我們的 INPUT 規則,並沒有針對 google 的來源封包做判斷處理的緣故。 那怎麼辦?沒關係,這就是原本規則的第 1 條內容,透過封包的『狀態 (state)』來進行過濾!封包狀態過濾基本語法像底下這樣:
[root@master ~]# iptables -A INPUT [-m state] [--state 狀態] [--mac-source MAC_ADDR] ... 選項與參數: -m :一些 iptables 的外掛模組,主要常見的有: state :狀態模組 mac :網路卡硬體位址 (hardware address) --state :一些封包的狀態,主要有: INVALID :無效的封包,例如資料破損的封包狀態 ESTABLISHED:已經連線成功的連線狀態; NEW :想要新建立連線的封包狀態; RELATED :這個最常用!表示這個封包是與我們主機發送出去的封包有關 --mac-source :就是網卡卡號設定!
試著完成底下規則看看:
# 1. 這次是『插入』規則,而且應用的是狀態模組 [root@master ~]# iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 2. 讓區域網路內的某個裝置,變成為信任來源 [root@master ~]# iptables -A INPUT -i enp2s0 -m mac --mac-source aa:bb:cc:dd:ee:ff -j ACCEPT # 3. 將全部連線都拒絕的規則加回來 [root@master ~]# iptables -A INPUT -j REJECT # 4. 修改預設政策 [root@master ~]# iptables -P INPUT DROP [root@master ~]# iptables -P FORWARD ACCEPT [root@master ~]# iptables-save *filter :INPUT DROP [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [4579:315039] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT <==確實回到第 1 條 -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT -A INPUT -i enp2s0 -j ACCEPT -A INPUT -s 192.168.20.0/24 -j ACCEPT -A INPUT -s 192.168.30.0/24 -i enp3s0 -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -s 192.168.201.0/24 -i enp1s0 -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A INPUT -p udp -m udp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -s 10.30.30.0/24 -j REJECT --reject-with icmp-port-unreachable <==應該要放到 port 80 前 -A INPUT -p tcp -m tcp --dport 5901:5910 -j ACCEPT -A INPUT -i enp2s0 -m mac --mac-source aa:bb:cc:dd:ee:ff -j ACCEPT -A INPUT -j REJECT --reject-with icmp-port-unreachable
基本上,直接手動輸入的 iptables 指令,其動作只寫入到目前的環境中,並沒有寫入設定檔,因此下次重新開機後, 剛剛努力建立的規則,就通通消失了...那麼資料儲存檔案在哪裡?就是在 /etc/sysconfig/iptables 這個檔案中! 這個檔案其實就是 iptables-save 的輸出資料耶!所以,要將目前的規則順序儲存下來,我們可以這樣做:
# 儲存目前的防火牆規則成為預設值 [root@master ~]# iptables-save > /etc/sysconfig/iptables
我們知道順序很重要,例如上面練習的環節中,我們就有特別說明, 10.30.30.0/24 那一條相關規則應該放錯地方...那怎辦?最簡單的方法,使用 -D INPUT 刪除後, 再用 -I INPUT 插入到某條規則之後...真不直觀~因此,建立一個簡單的防火牆腳本檔,每次修改完之後,直接執行一次腳本就好了! 簡單方便又直觀~現在,使用 vim 或 nano 打開你的防火牆規則檔,建立如下的規則看看:
[root@master ~]# vim ~/iptables.sh #!/bin/bash # part 1: 清除規則並設定預設政策 iptables -F iptables -X iptables -Z iptables -P INPUT DROP iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT # part 2; 基礎的三條防火牆規則 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -p icmp -j ACCEPT # part 3; 拒絕黑名單、開放白名單的設定 iptables -A INPUT -s 10.30.30.0/24 -j REJECT iptables -A INPUT -i enp1s0 -s 192.168.201.254 -j ACCEPT iptables -A INPUT -i enp2s0 -s 192.168.10.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT iptables -A INPUT -i enp2s0 -s 192.168.20.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT iptables -A INPUT -i enp3s0 -s 192.168.30.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT # part 4: 一般通用放行的網際網路服務 iptables -A INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT iptables -A INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT iptables -A INPUT -m state --state NEW -p udp --dport 53 -j ACCEPT iptables -A INPUT -m state --state NEW -p tcp --dport 53 -j ACCEPT # part 5: 不要忘記儲存規則 iptables-save > /etc/sysconfig/iptables [root@master ~]# sh ~/iptables.sh
未來如果有新的服務設定,那只要在第四部份增加相關的設定規則之後,重新執行這個腳本即可!輕鬆愉快!
NAT 的全名是 Network Address Translation,字面上的意思是『網路位址的轉換』。由字面上的意思我們來想一想, TCP/IP 的網路封包不是有 IP 位址嗎?那 IP 位址不是有來源與目的嗎?我們的 iptables 指令就能夠修改 IP 封包的表頭資料, 連目標或來源的 IP 位址都可以修改呢!甚至連 TCP 封包表頭的 port number 也能修改!
再回想一下 nat 這個 iptables 的表格有什麼鏈?以及可以進行的動作大概是什麼?
參考第六章、圖 6.2-1 的圖示內容,我們會知道由 switch LAN 用戶端要連線到網際網路上面, 那麼封包應該是會透過 Master 骨幹系統進行如下的流程:
NAT 伺服器功能主要就是實行上面步驟 1, 4,主要修改的鏈為 nat 表格的 PREROUTING, POSTROUTING。那個本機 FORWARD 鏈則需要打開啊! 至於 nat 的鏈主要的目的:POSTROUTING 在修改來源 IP/port ,PREROUTING 則在修改目標 IP/port。由於修改的 IP 不一樣,所以就稱為來源 NAT (Source NAT, SNAT) 及目標 NAT (Destination NAT, DNAT)。
當你想要成為 IP 分享器時,那麼所有從內部 LAN 要出去到 Internet 的封包,都要將『出去的那個 IP 改為 server 的對外 IP』, 這改的就是『來源』位址,因此稱為 SNAT!圖示如下:
如上圖所示,在用戶端 192.168.20.1 這部主機要連線到 https://linux.vbird.org 去時,他的封包表頭會如何變化?
此時 linux.vbird.org 看到這個封包並將要求處理完畢之後,就會將回應封包資料回傳到 public IP 上面,linux.vbird.org 並不會知道這個 IP 位址的來源其實是在你的區域網路內~再來想一想,這個從 linux.vbird.org 回傳回來的回應封包又是如何回到用戶端電腦上? 這裡要先注意喔,剛剛傳出去的封包狀態會有資料紀錄在 iptables 的記憶體中,所以,回應封包是這麼跑的:
經過上面這兩個流程後,所有內部 LAN 的主機都可以透過這部 Master 伺服器連線出去,外界看到這些封包表頭資訊,都只有 Master 的對外 public IP 而已。所以,如果內部 LAN 主機沒有連上不明網站的話,那麼內部主機其實是具有一定程度的安全性的啦! 因為 Internet 上的其他人沒有辦法主動攻擊你 LAN 內的 PC 嘛!所以我們才會說,NAT 最簡單的功能就是類似 IP 分享器啦!那也是 SNAT 的一種。
那如何達成 SNAT 的功能呢?很簡單!透過 SNAT 的『偽裝』功能即可!那如果你有好幾個 public IP 位址怎辦?可以指定某一個嘛? 也是可以的~就說可以改變你的『來源 IP 位址或埠口』功能~基本語法有點像這樣:
[root@master ~]# iptables -t nat -A POSTROUTING [-s LAN_IP] [-o 對外界面] -j MASQUERADE [root@master ~]# iptables -t nat -A POSTROUTING [-o 對外界面] -j SNAT [--to 對外的某個IP位址]
第 1 條規則,是針對內部網路的網域 (LAN_IP) ,當該封包要從『對外界面』那張網卡傳出時,才偽裝 (MASQUERADE) 成為該網卡的 IP 位址之意。第 2 條規則,則是讓任何通過『對外界面』的封包,其來源 IP 位址改為固定的『某個IP位址』之意。 因為每個人家裡的 public IP 一定不相同,所以使用上面第 1 條會是比較正確的作法。
在前一章我們有事先做了 switch LAN 連線的用戶端,現在,請在終端機上面登入系統 (因為我們還沒有設定網路), 然後將 client_raw 主機的網路環境設定成為底下這樣:
# 1. 設定主機名稱: [root@localhost ~]# hostnamectl hostname client001.vbird # 2. 設定正確的網路參數: [root@client001 ~]# nmcli connection modify enp1s0 ipv4.method manual ipv4.addresses 192.168.20.1/24 \ > ipv4.gateway 192.168.20.254 ipv4.dns 120.114.100.1,168.95.1.1 [root@client001 ~]# nmcli connection up enp1s0 [root@client001 ~]# ip addr show enp1s0 2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:00:20:01 brd ff:ff:ff:ff:ff:ff inet 192.168.20.1/24 brd 192.168.20.255 scope global noprefixroute enp1s0 .... [root@client001 ~]# ip route show default via 192.168.20.254 dev enp1s0 proto static metric 100 192.168.20.0/24 dev enp1s0 proto kernel scope link src 192.168.20.1 metric 100 [root@client001 ~]# ping -c 3 192.168.20.254 PING 192.168.20.254 (192.168.20.254) 56(84) bytes of data. 64 bytes from 192.168.20.254: icmp_seq=1 ttl=64 time=0.295 ms 64 bytes from 192.168.20.254: icmp_seq=2 ttl=64 time=0.324 ms 64 bytes from 192.168.20.254: icmp_seq=3 ttl=64 time=0.303 ms --- 192.168.20.254 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2049ms rtt min/avg/max/mdev = 0.295/0.307/0.324/0.012 ms [root@client001 ~]# ping -c 3 -w 3 168.95.1.1 PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data. --- 168.95.1.1 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2043ms # 3. 取得 /etc/hosts 的正確設定 [root@client001 ~]# scp -p 192.168.20.254:/etc/hosts /etc [root@client001 ~]# ping -c 3 masterlan PING masterlan.vbird (192.168.20.254) 56(84) bytes of data. 64 bytes from masterlan.vbird (192.168.20.254): icmp_seq=1 ttl=64 time=0.287 ms ...
簡單的發現到,目前這部 client001 系統可以連接到 gateway 上面,但是卻無法連線到 internet 上!就是因為我們的 Master 系統尚未處理 SNAT 啦!現在,你加入這條規則在 Master 系統上面看看:
# 1. 在 Master 骨幹系統上面修改防火牆,加入 SNAT 機制: [root@master ~]# iptables -t nat -A POSTROUTING -s 192.168.20.0/24 -o enp1s0 -j MASQUERADE # 2. 在 client001 上面,直接 ping 網際網路 IP 位址 [root@client001 ~]# ping -c 3 -w 3 168.95.1.1 PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data. 64 bytes from 168.95.1.1: icmp_seq=1 ttl=52 time=4.53 ms ...
這樣就做好 SNAT 的 IP 分享器功能!簡單方便又快速!很容易理解,對吧!
如果你的系統因為某些緣故,所以需要將本機的 port 2121 用來作為 port 22 的連接埠口,那麼需要將 sshd 這個服務啟動到非正規的 port 2121 嘛?似乎不太需要~我們可以透過重導向 (redirection) 的方式來處理即可! 其實想法很簡單,就是在 PREROUTING 的鏈裡面,讓 port 2121 『轉向』到 port 22 去而已。亦即是,不論你連接 port 2121 還是 port 22,最終都是連線到 port 22 !而能不能從 port 2121 連線成功,還需要看 port 22 的設定值才行! 更簡單的說, REDIRECT 有點像是『符號連結檔』的概念,port 22 是實際檔案,而 port 2121 則是符號連結 (捷徑), 這樣想,有沒有比較簡單呢?
做個簡單的測試,先看看我們的 sshd 這個服務啟動在哪個埠口?
[root@master ~]# netstat -tlnp
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 665/sshd: /usr/sbin
tcp6 0 0 :::22 :::* LISTEN 665/sshd: /usr/sbin
我們看到僅有 port 22 有放行而已~現在,讓我們使用底下的語法來將 port 2121 導向 port 22 看看:
[root@master ~]# iptables -t nat -A PREROUTING -p tcp --dport 2121 -j REDIRECT --to 22
現在,請『從外網連線到 Master 的 port 2121』測試看看能不能連接到 sshd 這個服務呢?非常簡單! 測試過後你就知道重導向的意義啦!
管理系統的操作者,通常會希望『系統越簡單越好』,所以會希望在一部系統上面只開啟一個服務~但是,我們沒有這麼多的 public IP 可以使用。所以,這個時候就得要透過所謂的埠口對應 (port mapping) 來進行後端主機的服務對應!因為會修改目的地端的網路參數, 所以就稱為目標 NAT (destination NAT, DNAT)。簡單的說,DNAT 主要是用在內部主機想要架設可以讓 internet 存取的伺服器。 以下圖為例來說明好了:
假設我的 192.168.30.1 啟動了 Web 伺服器,這個服務的埠口放行在 port 80,那麼網際網路來源 (IP: aa.bb.cc.dd) 要如何連接到我內部的這部系統? 這當然還是要透過 Master 系統的外部 public IP 來對應埠口的!整個流程分析如下:
同樣的,當 192.168.30.1 處理好 aa.bb.cc.dd 這個用戶端需要的資料後,將回應封包回傳到 Master 主機的 192.168.30.254, 再透過路由將封包轉到 public IP 那個界面上,最終再將來源 IP 位址改成 public IP 之後,就可以送回去給 aa.bb.cc.dd 了! 這樣也就完成了內部伺服器的連線!
至於達成 DNAT 的指令其實也很簡單!只要記得封包轉遞是在 PREROUTING 的鏈之前處理即可:
[root@master ~]# iptables -t nat -A PREROUTING [-i 外部界面] [-p tcp --dport 分析的埠口] \ > -j DNAT [--to 後端IP:port]
前一章我們有使用了 server_raw 系統附掛在 switch DMZ 的橋接 (serverbr0) 上面,現在請登入該系統,然後將 server_raw 主機的環境設定成為這樣:
# 1. 設定主機名稱 [root@localhost ~]# hostnamectl hostname server001.vbird # 2. 設定正確的網路參數: [root@server001 ~]# nmcli connection modify enp1s0 ipv4.method manual ipv4.addresses 192.168.30.1/24 \ > ipv4.gateway 192.168.30.254 ipv4.dns 120.114.100.1,168.95.1.1 [root@server001 ~]# nmcli connection up enp1s0 [root@server001 ~]# ip addr show enp1s0 2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:00:30:01 brd ff:ff:ff:ff:ff:ff inet 192.168.30.1/24 brd 192.168.30.255 scope global noprefixroute enp1s0 [root@server001 ~]# ip route show default via 192.168.30.254 dev enp1s0 proto static metric 100 192.168.30.0/24 dev enp1s0 proto kernel scope link src 192.168.30.1 metric 100 [root@server001 ~]# ping -c 3 192.168.30.254 PING 192.168.30.254 (192.168.30.254) 56(84) bytes of data. 64 bytes from 192.168.30.254: icmp_seq=1 ttl=64 time=0.299 ms ... [root@server001 ~]# ping -c 3 -w 3 168.95.1.1 PING 168.95.1.1 (168.95.1.1) 56(84) bytes of data. --- 168.95.1.1 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2048ms # 3. 取得 /etc/hosts 的正確設定 [root@server001 ~]# scp -p 192.168.30.254:/etc/hosts /etc [root@server001 ~]# ping -c 3 masterdmz PING masterdmz.vbird (192.168.30.254) 56(84) bytes of data. 64 bytes from masterdmz.vbird (192.168.30.254): icmp_seq=1 ttl=64 time=0.232 ms ...
基本上,跟之前 client 的設定幾乎一模一樣!而且 server_raw 系統也是沒有對外網路的喔!現在,讓我們來啟用 SNAT 與 DNAT, 讓外部連線可以連到內部 server 上!底下的作法是在 Master 系統上喔!
# 1. 修改 iptables.sh 檔案,增加 nat 相關的機制 [root@master ~]# vim ~/iptables.sh .... # part 2.1: 清除 nat 表格的相關資料,並設定預設政策 iptables -t nat -F iptables -t nat -X iptables -t nat -Z iptables -t nat -P PREROUTING ACCEPT iptables -t nat -P POSTROUTING ACCEPT iptables -t nat -P OUTPUT ACCEPT # part 2.2: 針對 Switch LAN 的 SNAT 設定: iptables -t nat -A POSTROUTING -s 192.168.20.0/24 -o enp1s0 -j MASQUERADE # part 2.3: 針對 Switch DMZ 的 SNAT/DNAT 設定 iptables -t nat -A POSTROUTING -s 192.168.30.0/24 -o enp1s0 -j MASQUERADE iptables -t nat -A PREROUTING -i enp1s0 -p tcp --dport 80 -j DNAT --to 192.168.30.1:80 # part 2.4: 針對本機的 redirection 設定 iptables -t nat -A PREROUTING -p tcp --dport 2121 -j REDIRECT --to 22 # part 2.5: 累加儲存規則 iptables-save -t nat >> /etc/sysconfig/iptables [root@master ~]# sh ~/iptables.sh [root@master ~]# iptables-save -t nat *nat :PREROUTING ACCEPT [5:926] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] -A PREROUTING -i enp1s0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.30.1:80 -A PREROUTING -p tcp -m tcp --dport 2121 -j REDIRECT --to-ports 22 -A POSTROUTING -s 192.168.20.0/24 -o enp1s0 -j MASQUERADE -A POSTROUTING -s 192.168.30.0/24 -o enp1s0 -j MASQUERADE
這樣就設定妥當了!接下來,請前往 server_raw 安裝 WWW 伺服器,並且啟動該服務 (無須開機啟動,目前測試而已), 然後從外部連線到 Master 的 public ip 位址處,應該就可以看到該資料了!
# 1. 先在 server_raw 處,進行底下的實做:(目的:啟動 WWW 並且修改首頁) [root@server001 ~]# yum install -y httpd [root@server001 ~]# systemctl start httpd [root@server001 ~]# firewall-cmd --add-service=http [root@server001 ~]# echo "I am server001" > /var/www/html/index.html # 2. 再到外部系統上,直接連線到 Master 的網頁測試看看即可: [user@outside ~]$ curl http://192.168.201.245 I am server001 <==你會看到首頁資料顯示的其實是 server_raw 那部主機的網頁資料喔!
透過 iptables 的 POSTROUTING 鏈,我們可以進行 MASQUERADE/SNAT 等任務,透過 PREROUTING 鏈則可以達成 REDIRECT/DNAT 等任務! 其他用途,就等著各位去開發啦!
大家要有個先入為主的觀念:『我提供給網際網路存取的系統,很可能會被攻擊攻破,而由於這部系統被攻破,同一區段的區域網路, 就很有可能同時被攻擊』這樣的概念!因此,我們提供的 server_raw 系統,就是那個很有可能被攻擊攻破的系統啦! 那,這樣的系統,就應該要放置在管制比較嚴格的區域吧!也就是說,這個區域應該要跟我員工系統放置的區網切開才行吧! 所以囉,將這樣對外公開服務的系統,放置到非軍事區 (Demilitarized Zone, DMZ) 就是個好主意!
簡單的說,所謂的 DMZ 就是只能讓網際網路或內網來存取的子網路位址,這個子網路位址不可以自己連網!畢竟這部主機『理論上』確實不該自己連網才對! 所以,我們可以從 WAN 主動連到 switch DMZ,可以從 switch LAN 主動連到 switch DMZ,switch DMZ 往外的回應封包 (ESTABLISHED, RELATED) 也可以放行! 但是主要來自 switch DMZ 的主動封包,就應該要拒絕這樣!這就是主要非軍事區 (DMZ) 的基礎想法。
那要怎麼達成 DMZ 的想法呢?用哪個 table 哪個 chain?其實,只要修改 FORWARD 這條鏈就可以了!看看上面 SNAT 與 DNAT, 我們預設的情況下,都沒有管制 FORWARD 對吧!那如果加以管制呢?就可以達成 DMZ 的作法了!只是實做上還得要留意的是, 丟進 DMZ 的 server 應該要先做好網路服務的安裝與其他軟體的整理,否則由於無法直接主動對外連線,所以恐怕連自我更新都會失敗! 那就糗了!所以需要特別注意啊!
設計上面要注意的是,因為 FORWARD 鏈是在 filter 表格內,因此你得要在 part 5 (儲存) 之前,將 FORWARD 規則寫好! 所以,鳥哥將這些 DMZ 的資訊紀錄成為 part 4.5 的部份:
[root@master ~]# vim ~/iptables.sh # part 4.5.1: DMZ 設定 (針對 internet) iptables -A FORWARD -i enp3s0 -o enp1s0 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -p udp --dport 53 -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -p tcp --dport 53 -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -p tcp --dport 443 -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp1s0 -j REJECT # part 4.5.2: DMZ 設定 (針對 intranet) iptables -A FORWARD -i enp3s0 -o enp2s0 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i enp3s0 -o enp2s0 -j REJECT # part 5: 不要忘記儲存規則 iptables-save > /etc/sysconfig/iptables [root@master ~]# sh ~/iptables.sh
如何測試呢?其實測試挺簡單的!大致上我們就來進行底下的動作即可:
請自行測試喔~這樣基本上就將 iptables 的大部分主要功能學全囉!真是不容易啊~結果...iptables 被放棄了!哈哈哈! 所以,準備來學其他的防火牆軟體吧~
firewalld 是一個防火牆軟體,目前這個軟體使用的後端其實還是 nftables 的機制,只是 firewalld 具有領域 (zone) 的概念, 每個領域都有其特定的功能,管理員只要選擇適當的領域,就可以直接搭配預設的防火牆規則。舉例來說,當使用 external 這個領域時, 只要透過這個領域的網卡送出去的封包,就直接具有偽裝的功能了!你不用手動去額外做設定!相當方便!鳥哥認為,這個 firewalld 對於單機環境的 Linux 伺服器來說,實在是太方便了!但是,要拿 firewalld 來當防火牆的學習軟體,則有點太過簡單, 同時,對於 DMZ 或不同的領域轉換間的狀況,還得要額外使用 policy 方式來處理~那就有點畫蛇添足~所以啦,用在單一服務 server 上, 或在單一用戶端環境,可以使用 firewalld 就好!簡單方便又輕鬆~如果是放在類似我們的 Master 骨幹系統,這個...其實反而有點傷腦筋! 要考慮的點實在太多了....所以,依據環境來選擇處理吧!
我們底下還是會用到 firewalld 來達成 Master 骨幹伺服器的所有防火牆任務,當然,某些任務處理後,會跟我們原本想像的情境結果不相同~ 無論如何,我們還是可以利用這個工具來聊聊防火牆的架設~
基本上,預設的 RHEL 9.x 衍生品原本預設的防火牆機制,就是 firewalld 啊!只是我們為了要介紹比較詳細的防火牆觀念, 所以前一小節使用了 iptables 的服務就是了。現在,就讓我們將 nftables, iptables 關閉,然後啟用 firewalld 防火牆, 再以 firewall-cmd 這個指令列管理程式來進行查閱吧!不過,由於 iptables 是 nftables 的模擬模組所支援, 如果只是關閉 iptables 似乎沒有辦法完整的移除其相關模組!建議修改過不同機制後,可能需要 reboot 比較好!
# 1. 僅啟用 firewalld 服務 [root@master ~]# systemctl disable --now nftables [root@master ~]# systemctl disable --now iptables [root@master ~]# systemctl enable --now firewalld [root@master ~]# reboot # 加上 --now 可以取代 stop/start 功能!可以達到立即關閉或啟動服務 # 另外,由於 iptables 的規則與 firewalld 會衝突,建議變更成為 firewalld 後,可以嘗試重新開機! # 2. 檢查目前的 firewalld 服務列表 [root@master ~]# firewall-cmd --list-all public (active) <==啟用的 zone 為 public target: default <==預設的政策為 default (相當於 REJECT) icmp-block-inversion: no <==是否拒絕 icmp 封包 interfaces: enp1s0 enp2s0 enp3s0 <==目前放在此 zone 的網路界面 sources: <==針對的來源位址 services: cockpit dhcpv6-client ssh <==提供放行的服務列表 ports: <==提供放行的埠口列表 protocols: <==提供放行的協定列表 forward: yes <==是否支援 forwarder 功能 masquerade: no <==是否支援 NAT 偽裝功能 forward-ports: <==轉遞的埠口 source-ports: <==來源埠口 icmp-blocks: <==拒絕連線的 icmp 類型 rich rules: <==更多詳細的自動規則 # 如果沒有啟動 firewalld 的話,這邊就會顯示錯誤了!
firewalld 使用了 firewall-cmd 這個指令來進行管理~在輸出的結果中,會有啟用的領域 (zone) 名稱,那個 target 就是我們在前面章節談到的『預設政策』的意思。firewalld 的預設政策主要有『ACCEPT / DROP / REJECT / default』。 至於 default 的功能,跟 REJECT 相似就是了!同樣是預設拒絕的意思~
services 有點類似公認的服務啟動的埠口,就是你要架設什麼服務,就得要放行什麼服務的埠口,那透過這個 services 來設定即可。 如果有非正規的埠口,例如我們想要放行 VNC 的 5900~5910 這幾個連續埠口,就需要用到 ports 那個設計!基本上, firewalld 針對單機環境下,就這幾個最重要!其他都可以暫時忽略的!
如前所述,firewalld 預先定義好許多規則,放在不同的領域 (zone) 內,只要呼叫該領域,你就可以直接套用預設的防火牆規則! 輕鬆又方便!那麼如何知道目前有多少的領域在 firewalld 裡面呢?使用 --get-zones 來查看即可:
[root@master ~]# firewall-cmd --get-zones
block dmz drop external home internal nm-shared public trusted work
要先說明的是,這些領域只對設定的本機有關,例如 block(阻擋/拒絕) 不是自我 block,而是加入這個 block 領域的網卡, 會針對進入本機 (input) 的封包做拒絕 (reject) 的動作之意!所以思考的時候,要想到『這是針對那一張網卡做的設計』喔! 好了,那麼上面這些領域的意思到底是什麼呢?大概說明如下:
從前一章節的內容,我們也會大概知道 dmz(非軍事區)、external(外部連線)、trusted(信任領域) 的意思~以我們前一小節的連線來看, enp1s0 (public ip) 網卡應該要丟進去 external 領域, enp2s0(switch LAN) 網卡應該要丟進去 trusted 或 internal 領域, 而 enp3s0 (switch DMZ) 網卡則應該丟進去 dmz 領域的概念!好了,那麼舉 internal 為例,這個領域裡面的防火牆規則是怎麼回事呢? 我們可以使用 --info-zone 來查看:
[root@master ~]# firewall-cmd --info-zone=internal
internal
target: default
....
services: cockpit dhcpv6-client mdns samba-client ssh
....
forward: yes
masquerade: no
....
上面的意思是,預設的政策 (target) 是 default (類似 REJECT),而放行的服務有 cockpit, dhcpv6-client, mdns, samba-client 與 ssh 等。 至於轉遞 (forword) 是放行的,不過並不處理偽裝問題,因為偽裝通常是對外 IP 位址 (public ip) 才會進行的。好像看得懂對吧! 問題是,我們怎麼知道 ssh 或 mdns 或 cockpit 這種服務的埠口是 TCP 或 UDP 以及其埠口號碼呢?
就跟許多已知服務 (known service) 一樣,firewalld 為了管理員設定方便,有指定許多的服務了!放行該服務就可以放行某些特定的埠口。 要查詢到底有哪些內建服務?可以這樣做:
[root@master ~]# firewall-cmd --get-services
RH-Satellite-6 .... ssh ...zabbix-server zerotier
資料真的是非常非常多!如同上面的說明,那麼類似 ssh 以及 cockpit 到底是放行什麼鬼呢?我們可以這樣看:
[root@master ~]# firewall-cmd --info-service=ssh ssh ports: 22/tcp <==預設放行的埠口號碼/協定 protocols: <==針對的協定 source-ports: <==來源埠口 modules: <==使用的模組 destination: <==目的地位址 includes: <==其他包含項目 helpers: <==其他協助模組 [root@master ~]# firewall-cmd --info-service=cockpit cockpit ports: 9090/tcp ...
很簡單看到 ssh 主要是放行 port 22/tcp 這個埠口而已!同理, cockpit 服務則是放行 port 9090/tcp。有沒有多重埠口的設定呢? 例如我們 iptables 章節談到的 DNS 似乎就得要有 udp 與 tcp!好啊!那就來瞧一瞧 dns 服務好了:
[root@master ~]# firewall-cmd --info-service=dns
dns
ports: 53/tcp 53/udp
...
果然是多重埠口的放行在 dns 服務上!這樣我們未來設定服務,就非常方便!不用去管埠口了!用預設的服務搭配標準埠口即可。 另外,我們可能會用到的還有一個 ftp 服務!這個服務挺有趣!我們先來看一看好了:
[root@master ~]# firewall-cmd --info-service=ftp
ftp
ports: 21/tcp
....
helpers: ftp
除了 ftp 預設的埠口為 21/tcp 之外,竟然還有個協助模組 (helpers) 的功能!同樣也是 ftp 這個名稱!那這個協助功能在幹麻? 我們來瞧一瞧:
[root@master ~]# firewall-cmd --info-helper=ftp
ftp
family:
module: nf_conntrack_ftp
ports: 21/tcp
重點在這個協助模組使用了 nf_conntrack_ftp 這個防火牆外掛模組的功能~FTP 服務是個很複雜的東西,連線成功之後會啟用兩條通道, 一條是指令通道,一條是資料通道,這個 nf_conntrack_ftp 則是在監聽指令通道,並告知防火牆應該要主動放行哪一條資料通道的意思! 有這個模組,架設 FTP 服務會方便許多!這就是 firewalld 的 helper 功能!
上述這些 zone 與 servcie 還有其他種種的資料,其實都放置到底下的兩個目錄:
大部分的資料都以 xml 樣式存在,有興趣的朋友可以自行查閱看看喔
實際管理 firewalld 防火牆,可以使用 firewall-cmd 來處理!從上面的說明來看,我們要管理的, 可能有 (1)預設領域的查看與切換功能、(2)黑/白名單的建立、(3)服務的增加與刪除、(4)非標準埠口的放行。大致上就這樣! 就讓我們來練習一下 firewall-cmd 吧!
firewalld 的預設值是在 /usr/lib/firewalld/ 目錄下,目前的實際設定檔則是在 /etc/firewalld/ 目錄內~但是, 記憶體還有 firewalld 的運作狀態!在當下的環境中,我們可以分別查詢/寫入紀錄檔,或者是直接執行!一般來說, 操作 firewall-cmd 有兩種常見的模式,說明如下:
以鳥哥的建議來說,使用第一種方式可能比較好,這是因為我們可能會進行很多測試,測試成功之後,那就成功了~結果就是忘記寫入設定~ 最終 reboot 後,規則就消失不見了...另外,如果後面還想要學習規則政策 (policy) 的建置,該方式僅能支援寫入設定檔後載入的模式, 所以,為了統一處理流程,還是使用先寫入設定檔後立刻載入的方式比較好喔!
取得目前活動中的領域,以及預設的領域,可以這樣處理:
# 1. 取得目前的預設領域 [root@master ~]# firewall-cmd --get-default-zone public # 2. 取得活動中的領域 [root@master ~]# firewall-cmd --get-active-zones public interfaces: enp1s0 enp2s0 enp3s0
如上所示,目前活動中的領域為 public,我們目前的三張網卡,通通包含在這個領域當中。現在,就依據之前的設計, 讓我們將不同的網卡放置到不同的領域去~基本的想法是這樣:
# 1. 開始切換不同的領域搭配的網卡 [root@master ~]# firewall-cmd --permanent --change-interface=enp1s0 --zone=external The interface is under control of NetworkManager, setting zone to 'external'. success # 由於 firewalld 的防火牆領域,針對網卡來說,還是跟 NetworkManager 有關!所以這裡會給說明! # 不用擔心!結果還是成功的! [root@master ~]# firewall-cmd --permanent --change-interface=enp2s0 --zone=internal [root@master ~]# firewall-cmd --permanent --change-interface=enp3s0 --zone=dmz [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --get-active-zones dmz interfaces: enp3s0 external interfaces: enp1s0 internal interfaces: enp2s0 # 最終三張網卡就放置到三個不同的領域去! # 2. 設定預設領域成為 external [root@master ~]# firewall-cmd --set-default-zone=external # 3. 檢查 NetworkManager 的設定檔,跟 zone 有關的設定: [root@master ~]# nmcli connection show enp1s0 | grep zone connection.zone: external [root@master ~]# nmcli connection show enp2s0 | grep zone connection.zone: internal [root@master ~]# nmcli connection show enp3s0 | grep zone connection.zone: dmz [root@master ~]# nmcli connection up enp1s0 [root@master ~]# nmcli connection up enp2s0 [root@master ~]# nmcli connection up enp3s0 # 可以看到使用 firewall-cmd 設定網卡對應 zone 時,NetworkManager 會同步寫入! # 為了準確性,我們還是重新載入一次各個網卡比較妥當!
由於我們使用的網路管理機制其實是 NetworkManager 這個服務,這個服務內容有個 zone 的設定,那就是跟 firewalld 連動的設定值! 這個設定值必須要寫入系統去~所以,改變了領域對應的網卡之後,一定要寫入設定值才行! 在目前的情況下,internal 以及 dmz 領域所在的系統,基本上還是不能連網的~等等我們談到最簡單 SNAT 時再來處理。 接下來我們先了解一下怎麼設定黑/白名單,透過 zone 的預先設定方式來處置!
每個 zone 裡面都有個名為 sources 的設定項目,我們也知道有兩個 zone 很有趣,分別是信任 (trusted) 與拒絕 (block) 領域! 那麼,能不能簡單的將信任的來源與可惡的來源分別放置到這兩個 zone 去?讚啊!這就是簡易的黑/白名單製作方式了! 處理的方式挺簡單:
# 將 192.168.201.254, 10.30.30.0/24 分別加入白/黑名單 [root@master ~]# firewall-cmd --permanent --add-source=192.168.201.254 --zone=trusted [root@master ~]# firewall-cmd --permanent--add-source=10.30.30.0/24 --zone=block [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --get-active-zones block sources: 10.30.30.0/24 <==黑名單 dmz interfaces: enp3s0 external interfaces: enp1s0 internal interfaces: enp2s0 trusted sources: 192.168.201.254 <==白名單
鳥哥覺得白名單的製作挺棒的!這是因為我們可能會從外部登入 master 骨幹系統來設計一些有的沒有的,此時, 我們外部的工作機 (範例中的 192.168.201.254) 就是一個可以加入白名單的來源!如此可以避免不小心的錯誤設定, 結果把自己擋死了的問題...如果設錯 source 的話,可以透過 --remove-source 來刪除規則!
內部網域 (internal) 所在的主機,應該可以使用我們骨幹伺服器比較多的服務才合理!假設我們的 Master 骨幹系統有 NTP 伺服器 (時間伺服器), 並且打算提供給這個網域來使用,這時可以透過這樣的方式來處理:
# 1. 增加提供服務的埠口給 internal 的系統 [root@master ~]# firewall-cmd --permanent --zone=internal --add-service=ntp [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=internal --list-services cockpit dhcpv6-client mdns ntp samba-client ssh # 2. 假設 http, https, dns 服務也提供吧! [root@master ~]# firewall-cmd --permanent --zone=internal --add-service={http,https,dns} [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=internal --list-services cockpit dhcpv6-client dns http https mdns ntp samba-client ssh
那如果要移除服務呢?舉例來說,將 dmz 的 ssh 服務移除,應該是個好主意!
# 列出 dmz 領域支援的服務,然後移除該服務! [root@master ~]# firewall-cmd --zone=dmz --list-services ssh <==還是提供了 DMZ 端的 ssh 連線!準備取消! [root@master ~]# firewall-cmd --permanent --zone=dmz --remove-service=ssh [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=dmz --list-services
你會發現最終指令輸出會是一片空白!因為已經有提供連線登入的服務囉!對於 DMZ 來說,還是這樣比較妥當!
某些時刻,我們某些埠口可能得要放行在非正規的埠口上!舉例來說,許多內部的伺服器,可能都會透過 8081, 3000 等埠口來提供某些特定的服務~那麼,firewalld 如何處理這個東西呢?很簡單,透過 port/tcp 或 port/udp 來規範即可! 例如上面兩個埠口同時加入,可以這樣做:
# 1. 加入獨立的埠口號碼 [root@master ~]# firewall-cmd --permanent --zone=internal --add-port={3000/tcp,8081/tcp} # 2. 加入連續的埠口號碼 [root@master ~]# firewall-cmd --permanent --zone=internal --add-port=5901-5910/tcp [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --zone=internal --list-ports 3000/tcp 5901-5910/tcp 8081/tcp
一般來說,針對單機伺服器而言,了解上述的功能就很棒了!其他不用再學習啦!現在,讓我們來想想:
你需要登入 client001 以及 server001 做底下的動作喔:
# 1. 在 client001 上面進行如下的動作: [root@client001 ~]# firewall-cmd --set-default-zone=work [root@client001 ~]# firewall-cmd --permanent --add-source=192.168.20.254 --zone=trusted [root@client001 ~]# firewall-cmd --reload [root@client001 ~]# firewall-cmd --get-active-zones trusted sources: 192.168.20.254 work interfaces: enp1s0 [root@client001 ~]# firewall-cmd --list-all work (active) target: default icmp-block-inversion: no interfaces: enp1s0 sources: services: cockpit dhcpv6-client ssh ... # 要注意 (1)白名單的 IP 位址是否正確? (2)services 的服務是否正確!這樣就搞定了! # 2. 在 server001 上面進行如下的動作: [root@server001 ~]# firewall-cmd --permanent --add-service={http,https,ftp} [root@server001 ~]# firewall-cmd --reload [root@server001 ~]# firewall-cmd --list-all public (active) target: default icmp-block-inversion: no interfaces: enp1s0 sources: services: cockpit dhcpv6-client ftp http https ssh ...
很簡單就處理完畢!不用在那邊撰寫 iptables 腳本!所以說,對於單機且服務相對單純的系統來說,使用 firewalld 就對了!
要達成真的比較好的 IP 偽裝、DNAT、DMZ 等功能,最好是要學習一下 rich rules 搭配 policy 的流程, 但是如果只是在自己家裡非常小的網路環境上,那就不需要這麼麻煩~有個超簡單的 SNAT 可以來進行偽裝! 只是無法達成 DMZ 的控制就是了。
在目前的情況下,基本上,client001 與 server001 在 internal 或 dmz 的領域中,都是無法對外的! 要講到對外,最簡單就是將需要偽裝的 IP 位址加入 external 領域中就搞定了!也就是說, 讓 internal 內的 192.168.20.0/24 以及 dmz 內的 192.168.30.0/24 加入 external 領域,成為該領域的 source 即可!
# 1. 將兩段子網路加入偽裝列表中 [root@master ~]# firewall-cmd --permanent --add-source={192.168.20.0/24,192.168.30.0/24} --zone=external [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --get-active-zones block sources: 10.30.30.0/24 dmz interfaces: enp3s0 external interfaces: enp1s0 sources: 192.168.20.0/24 192.168.30.0/24 internal interfaces: enp2s0 trusted sources: 192.168.201.254
現在,你的 server001 與 client001 兩部系統,應該都可以順利對外連線了!但是,dmz 領域也同樣可以連線到 internal 領域去, 彼此都沒有限制了喔!所以就幾乎沒有管制就是了!
還記得我們在 DNAT 的章節中,特別強調在 PREROUTING 有 DNAT 與 REDIRECT 的功能吧?那麼這個 firewalld 如何達到封包的轉遞呢? 基本語法有點像底下這樣:
firewall-cmd --add-forward-port=port=埠口號碼:proto={tcp|udp}:toport={埠口號碼}:toaddr={IP位址}
好像很簡單對吧!基本上,用在單機伺服器或用戶端,這個方式是沒問題的!但如果是用在比較複雜的 master 骨幹系統上呢? 來測試看看。假設我們讓 Master 本機的 port 2121 可以導向 port 22 時,應該是這樣處理:
[root@master ~]# firewall-cmd --permanent --add-forward-port=port=2121:proto=tcp:toport=22 [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --list-forward-ports port=2121:proto=tcp:toport=22:toaddr=
這樣就搞定了!你到 external 的外網部份嘗試『 ssh -p 2121 192.168.201.245 』時,就會連線到 port 22 了! 也算是非常簡單!那麼如果需要往後端的 DMZ 子網路的系統轉遞呢?舉例來說,從 external 領域來的封包, 遇到 port 80 就轉遞到 192.168.30.1:80 時,那可以這樣做:
[root@master ~]# firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toaddr=192.168.30.1 [root@master ~]# firewall-cmd --reload
好像沒什麼問題啊!很快就做好設定!輕鬆愉快!
其實上面的設定會出狀況喔~我們來測試一下你就知道問題在哪裡了:
# 1. 嘗試從 client001 連線到 masterlan 上面 [root@client001 ~]# ssh -p 2121 masterlan # 這時,好像很正常啊!沒啥問題!真的嘛?那麼來測試一下底下的狀況: # 2. 嘗試從 client001 連線到 10.0.0.1(這台主機並不存在) 上面 [root@client001 ~]# ssh -p 2121 10.0.0.1 The authenticity of host '[10.0.0.1]:2121 ([10.0.0.1]:2121)' can't be established. ED25519 key fingerprint is SHA256:Va9Z79fb2Kv16vT9QP46iRksvxDayLYKcMY64n3txv8. This host key is known by the following other names/addresses: ~/.ssh/known_hosts:3: [masterlan]:2121 Are you sure you want to continue connecting (yes/no/[fingerprint])? ^C
見鬼了!怎麼會連線到一個不存在的 IP 位址時,竟然出現也是連線到 master 骨幹系統上面的資訊? 只有 port 2121 才這樣嘛?來來~來測試看看 port 80 會變怎樣?
# 1. 正常情況下,連線到 masterlan 的 port 80 看網頁,應該是被導向 server001 [root@client001 ~]# curl http://masterlan/ I am server001 # 2. 那如果連線到鳥站的 port 80 呢? [root@client001 ~]# curl http://linux.vbird.org I am server001 # 同樣的,結果通通導向 server001,怎麼會這樣?
這個問題是這樣發生的,因為上面的埠口轉遞,只有分析到封包的『目標埠口』而已,並沒有分析其他特別的項目, 包括綁定來源 IP 或來源界面,所以當你從 lan 要外出時,卻也被該規則分析到符合項目,那就造成這個特別的困擾了! 怎麼辦呢?基本上,最簡單的方法,就是使用特別的不會被用到的埠口來做 port mapping!例如當使用 port 8080 時, 就傳到 192.168.30.1:80 上!如此一來,就『比較』可以避免這個困擾。
# 1. 刪除剛剛建立的 REDIRECT/DNAT 功能 [root@master ~]# firewall-cmd --permanent --remove-forward-port=port=2121:proto=tcp:toport=22 [root@master ~]# firewall-cmd --permanent --remove-forward-port=port=80:proto=tcp:toport=:toaddr=192.168.30.1 # 2. 讓 port 8080 可以連線到 192.168.30.1:80 上 [root@master ~]# firewall-cmd --permanent --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=192.168.30.1 [root@master ~]# firewall-cmd --reload # 3. 在不同的系統上 (看底下指令前的主機名稱) 測試 [root@cloud ~]# curl http://192.168.201.245 curl: (7) Failed to connect to 192.168.201.245 port 80: No route to host [root@cloud ~]# curl http://192.168.201.245:8080 I am server001 # 在外部系統上面測試,看起來整體效果是正確的!只有 port 8080 才會生效! [root@client001 ~]# curl http://masterlan curl: (7) Failed to connect to masterlan port 80: No route to host [root@client001 ~]# curl http://masterlan:8080 I am server001 [root@client001 ~]# curl http://192.168.30.1 I am server001 # 內部網段傳向 master 的 8080 埠口是成功導向,而主動前往 DMZ 也是沒問題! [root@client001 ~]# curl http://linux.vbird.org # 也確實可以看到鳥站的資料喔!
得要使用非正規埠口來分析,確實很有點困擾!那如何使用複合式的規則呢?可能就得要用到 rich rule 了! 畢竟是非正規~所以,還是將這個轉遞功能先取消:
[root@master ~]# firewall-cmd --permanent --remove-forward-port=port=8080:proto=tcp:toport=80:toaddr=192.168.30.1 [root@master ~]# firewall-cmd --reload
雖然 firewalld 還挺簡單的,但是如果要處理複雜的任務時,恐怕就得要額外設定了!例如要規範某個網域才能使用 ssh 時, 就不是簡單的加入來源位址而已~你可能需要使用 rich rule 的項目才行!詳細的用法可以參考『 man firewalld.richlanguage 』的說明, 該說明就只要看 example 的部份即可!相信大家都能看得懂才對!
現在,假設我們要讓 Master 系統的對外 (enp1s0) 只有 192.168.201.0/24 這個網域可以使用 ssh 服務, 並且將對外的 ssh 服務關閉!這時應該要這樣處理:
[root@master ~]# firewall-cmd --permanent --remove-service=ssh [root@master ~]# firewall-cmd --permanent --add-rich-rule='\ > rule family="ipv4" source address="192.168.201.0/24" service name="ssh" accept' [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --list-all external (active) target: default icmp-block-inversion: no interfaces: enp1s0 sources: 192.168.20.0/24 192.168.30.0/24 services: ... rich rules: rule family="ipv4" source address="192.168.201.0/24" service name="ssh" accept
我們可以看到服務的部份已經沒有提供 ssh 的連線,但是在 rich rule 的地方,則有提供單一網域開啟的 ssh 功能! 這樣是管理比較好的方式!不要讓 ssh 對全世界放行~也就是說,在外網的部份,ssh 是有針對性的~不是誰都可以連線過來的意思。
我們知道 DMZ 的主要功能其實是需要透過 external 來的封包進行處理~也就是說,從 external 來的封包,透過某些服務, 就可以轉到 dmz 內。使用簡易的 --add-forward-port 如果怪怪的,那使用 rich rules 可能不失為一個簡單有效的方法! 我們可以分析,當封包的『目標位址』是 external 所在的 IP 位址 (就是對外 IP),且服務為 port 80 時,就予以轉遞~ 處理的方式會有點像這樣:
[root@master ~]# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" destination address="192.168.201.245" \ > forward-port port="80" protocol="tcp" to-port="80" to-addr="192.168.30.1"' [root@master ~]# firewall-cmd --reload
那麼我們需要的本機 port 2121 轉到 port 22 應該也能透過這個方法來處理嘛?簡單的說,就是要連線到 192.168.201.245 這個我們 master 主要對外的 IP 位址上的 port 2121 時,才轉向本機的 port 22,那處理方法會變怎樣呢?
[root@master ~]# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" destination address="192.168.201.245" \ > forward-port port="2121" protocol="tcp" to-port="22" ' [root@master ~]# firewall-cmd --reload
使用上相當的方便!如果還有更多的需求,倒是可以參考 man firewalld.richlanguage 的內容說明!
到目前為止,我們設計的 firewalld 防火牆,大部分都還是針對單一領域來設計,也可以使用 rich rules 進行 forward port 等功能。 那如果需要使用類似 iptables 可以使用 -i 與 -o 宣告進/出界面的方式,而不是直接綁死 IP 位址的方式來處理時,該如何是好? 這個時候,可能就得要透過 policy 這個特殊的功能來實做了!
一般 policy 的語法會有點像這樣:
# 1. 一定要有的部份: # firewall-cmd --permanent --new-policy=政策名稱 # firewall-cmd --permanent --policy=政策名稱 --add-ingress-zone=來源領域 # firewall-cmd --permanent --policy=政策名稱 --add-egress-zone=目標領域 # firewall-cmd --permanent --policy=政策名稱 --set-target=[ACCEPT|DROP|REJECT|CONTINUE] # 2. 具有特殊規則的部份 # firewall-cmd --permanent --policy=政策名稱 --add-service=放行服務 # firewall-cmd --permanent --policy=政策名稱 --add-rich-rule=放行的特殊規則 # firewall-cmd --permanent --policy=政策名稱 --add-rich-rule=放行的特殊規則 # firewall-cmd --permanent --policy=政策名稱 --set-priority=[-32768~32768] # firewall-cmd --reload
上面的許多設定參數,你應該都看得懂才對~ingress-zone 為規範的封包規則之來源領域,同理, egress-zone 則是封包規則路由之後傳送出去的領域。除了正常的 external, public... 等預設的領域之外, 還有兩個特別的名詞要注意:
另外,set-target 的部份,基本上分為 4 種狀態:
除此之外,那個 set-priority 是什麼鬼?我們可能會有許多的 policy 存在,那麼哪一個 policy 會優先被執行呢? 不是考慮 policy 載入的順序,而是透過 priority 這個設定值!這個設定值越小就越優先!大概是這樣!
使用 policy 對 internal 傳向 external 的封包進行偽裝的功能,使用 policy 還算簡單!不過,如果想要制定的更嚴格, 例如只有來自 internal 且 IP 位址在 192.168.20.0/24 才能進行偽裝時,那就可以使用底下的方式囉!
# 1. 先移除原本加在 external 的兩個內部 source IP 位址~ [root@master ~]# firewall-cmd --get-active-zones .... external interfaces: enp1s0 sources: 192.168.20.0/24 192.168.30.0/24 .... [root@master ~]# firewall-cmd --permanent --remove-source={192.168.20.0/24,192.168.30.0/24} [root@master ~]# firewall-cmd --reload # 2. 建立名為 intranet 的新政策,針對 internal 來源與任何目標領域,同時預設流向為 REJECT [root@master ~]# firewall-cmd --permanent --new-policy=intranet [root@master ~]# firewall-cmd --permanent --policy=intranet --add-ingress-zone=internal [root@master ~]# firewall-cmd --permanent --policy=intranet --add-egress-zone=ANY [root@master ~]# firewall-cmd --permanent --policy=intranet --set-target=REJECT [root@master ~]# firewall-cmd --reload # 3. 放行 192.168.20.0/24 的使用權!且查閱 intranet 的政策內容 [root@master ~]# firewall-cmd --permanent --policy=intranet --add-rich-rule='rule family="ipv4" \ > source address="192.168.20.0/24" accept' [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --info-policy=intranet intranet (active) priority: -1 target: REJECT ingress-zones: internal egress-zones: ANY services: ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: rule family="ipv4" source address="192.168.20.0/24" accept
這個時候,你的 internal 領域內的任何主機 (相當於你的 LAN),就被放行了!要去哪邊都可以! 從 external 出去時,就偽裝 IP 位址外出~從 dmz 出去時,就保持原有的 IP 位址~看起來就相當簡單方便啊~
因為 DMZ 未來要放置 public server,所以看起來不應該提供太多的功能才對!因此,我們實做一個 dmz2wan 的政策, 這個政策裡面,只讓來自 dmz 的封包可以外出 external,且僅有 dns, http, https 可以主動對外,其他連線通通拒絕! 作法似乎也不怎麼難:
# 1. 建立名為 dmz2wan 的政策,且來源領域/目標領域分別為 dmz/external,預設流向為 REJECT [root@master ~]# firewall-cmd --permanent --new-policy=dmz2wan [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --add-ingress-zone=dmz [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --add-egress-zone=external [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --set-target=REJECT # 2. 僅放行 dns, http, https 的服務: [root@master ~]# firewall-cmd --permanent --policy=dmz2wan --add-service={dns,http,https} [root@master ~]# firewall-cmd --reload [root@master ~]# firewall-cmd --info-policy=dmz2wan dmz2wan (active) priority: -1 target: REJECT ingress-zones: dmz egress-zones: external services: dns http https ...
接下來你可以前往 192.168.30.1 這部 server001 測試一下,就能知道我們的設定是否成功!包括這樣:
所以說,在 master 骨幹伺服器上面實做 firewalld 時,得要熟悉 policy 的制定方式才好!而要制定 policy,對於網路與 iptables 要有一定程度的了解會比較好!加油加油!
如前所述,nftables 也是 netfilter 的機制之一,開發的目的是因為 iptables 的程式碼太過雜亂,為了統一格式與程式碼, 所以使用了 nftables 來進行管理~nftables 同樣使用了表格與鏈的方式來進行規則的設定,同樣保留了 nat, prerouting, postrouting 等特殊關鍵字,同樣將防火牆規則寫入到鏈 (chain) 裡面去。比較不同的是,nftables 的表格與鏈的名稱,都可以隨便你設定! 所以彈性很高!而跟 iptables 的啟動腳本都要去讀取 /etc/sysconfig/iptables 不同,你可以在 /etc/sysconfig/nftables.conf 去指定你要啟動的腳本名稱~甚至可以在其他腳本內呼叫額外的腳本~方便管理員進行不同的環境設定。
總之,目前的 Linux 系統,核心已經轉向 nftables 了,不論你喜歡不喜歡,都得要碰一碰 nftables 啦!閃不掉, 就好好學習吧!呵呵~
跟前面一樣,基本上,防火牆軟體,你就只能啟動其中一個!我們這邊要啟動的是 nftables,開始來處理一下這個新的防火牆機制囉。 由於 firewalld 的底層其實用的就是 nftables,所以,從 firewalld 切換到 nftables,就不需要重新開機啦!來測試看看:
# 1. 三種防火牆軟體,僅開啟 nftables 服務: [root@master ~]# systemctl disable --now iptables [root@master ~]# systemctl disable --now firewalld [root@master ~]# systemctl enable --now nftables # 2. 啟用預設的防火牆機制:先修改設定檔 /etc/sysconfig/nftables.conf [root@master ~]# vim /etc/sysconfig/nftables.conf include "/etc/nftables/main.nft" # 大概在第 4 行,將註解 # 拿掉即可! [root@master ~]# systemctl restart nftables [root@master ~]# nft list chains table inet nftables_svc { chain allow { } chain INPUT { type filter hook input priority 20; policy accept; } }
基本上,在 nftables 的環境下,所有的表格、鏈、規則等等,都需要管理員自己手動設定!雖然系統有預設給予一個規則, 但是卻預設沒有啟用...所以,我們先來設計一下,讓系統可以載入最陽春的防火牆設置囉!預設僅有針對本機的設置~ 最終使用 nftables 的管理工具,亦即是 nft 這個指令來觀察一下目前的鏈的數量,與鏈的預設政策!目前到這裡應該就 OK 啦! 準備讓我們來看看上述的表格、鏈、規則又是啥鬼~
跟 iptables 預設就有定義好需要的表格不同,nftables 所有的資料都要管理員自己指定~該如何制定表格?鏈? 讓我們從觀察預設的資料去學習吧!
讓我們來觀察一下在預設的 RHEL 9.x 衍生品當中,預設的防火牆表格是怎麼回事?
# 輸出預設的 nftables 的表格名稱與格式 [root@master ~]# nft list tables table inet nftables_svc table [IP位址格式] [表格名稱]
輸出的格式『table IP位址格式 表格名稱』,所以,預設的防火牆表格中,僅有一個名為 nftables_svc 的表格, 該表格支援的 IP 位址格式為 inet 格式的意思~那個 nftables_svc 的名稱是可變的,不一定要用該名稱! 然後, inet 又是符合什麼樣的 IP 位址格式?底下是常見的 IP 位址格式:
根據上面的說明,我們可以簡單的透過 nft add 來建立表格~假設我們要針對 ipv4 的位址進行封包分析, 假設表格名稱為 mytable,那麼建立這個表格的方法會是這樣:
[root@master ~]# nft add table ip mytable [root@master ~]# nft list tables table inet nftables_svc table ip mytable
這樣就簡單的建立好一個自訂的表格了!
跟 iptables 一樣,表格內就是鏈啦!列出鏈的名稱與預設政策如下:
[root@master ~]# nft list chains table inet nftables_svc { chain allow { } chain INPUT { type filter hook input priority 20; policy accept; } } table ip mytable { }
我們可以看到兩個表格,mytable 剛剛建立,內容是空的!至於 nftables_svc 則有兩個鏈,分別是 allow 跟 INPUT, allow 的內容很少,而 INPUT 則應該是針對連線到本機的封包所作的鏈的樣子!在 INPUT 底下那一行, 則是針對 INPUT 的規則描述!因為鏈的名稱是可以自訂的,所以我們還是得要規範這條鏈到底是針對輸入/輸出/路由等做的規範! 同時給予預設的政策資料。上面的鏈的規範基本語法如下:
type [鏈的類型] hook [主要針對的封包流向] priority [優先序數值]; policy [預設政策];
每一種類型 (type) 可以使用的封包流向 (hook, 鉤連) 並不相同!根據 Red Hat 手冊的說明,每種鏈的類型可適用的 IP 位址格式與封包流向方式, 簡單的說明如下:
類型(type) | 適用的IP位址格式 | 可用的封包流向(hook) | 應用說明 |
---|---|---|---|
filter | 適用所有格式 | 適用所有流向 | 主要的標準鏈格式 |
nat | ip, ip6, inet | prerouting, postrouting, input, output | 適用於進行 NAT 的功能! |
route | ip, ip6 | output | 當封包的表頭經過修改,經由路由過後,可透過這個鏈來進行後續分析 |
在鏈的類型以及封包流向 (type 與 hook) 之後,設定的是優先序的數值~在 nftables 當中,因為沒有預設的鏈, 所以每個鏈就沒有直接的關係,不像 iptables 是由預設的 prerouting-->forward-->postrouting 這樣。 既然每個鏈都沒有預設的關係,那麼如何知道那一個鏈要先運作?這就得要透過這個 priority 的參數來處理! 而為了方便管理員設定,事實上,還是有一些可以參考的文字與優先序相關性的預設值,列表如下:
優先序文字 | 代表的數值 | 適用的 IP 格式 | 封包流向功能 |
---|---|---|---|
raw | -300 | ip, ip6, inet | all |
mangle | -150 | ip, ip6, inet | all |
dstnat | -100 | ip, ip6, inet | prerouting |
-300 | bridge | prerouting | |
filter | 0 | ip, ip6, inet, arp, netdev | all |
-100 | bridge | all | |
security | 50 | ip, ip6, inet | all |
srcnat | 100 | ip, ip6, inet | postrouting |
300 | bridge | postrouting | |
out | 100 | bridge | output |
事實上,你也可以發現,dstnat (dnat) 比較優先,然後是 filter,最終才是 srcnat (snat),跟以前學的 iptables 也沒有差太多啦! 哈哈!所以我們才要先了解 iptables 啊!最後的政策 (policy) 就是預設的封包動作!主要大概就兩個:
所以預設的 INPUT 鏈,基本規範說的是:『針對所有 IP 位址的類型,針對封包流向為輸入的方式,優先序數值為 20,預設政策為接受』。 因為這條基本規範,我們也才能確認 INPUT 鏈就是針對流進本機的封包所制定的規則。
因為鏈是在表格內建立的,所以,要建立鏈的時候,當然要說明是在那一個表格內!所以,基礎的語法會有點像這樣:
[root@master ~]# nft add chain [IP格式] [表格名稱] [鏈的名稱] { \ > type [鏈的類型] hook [封包流向] priority [優先序] \; policy [預設政策] \; }
因為是指令模式,在指令模式內,分號 (;) 是有特別意義的,因此在這裡使用跳脫符號去處理他!你也可以使用單引號來將所有的指令放在一起, 這樣也不用加上跳脫符號!現在,讓我們建立一個新的鏈,這個鏈想要管理輸入的封包,那麼你應該需要的資料有:
所以整個新增鏈的指令就會變成這樣:
[root@master ~]# nft 'add chain ip mytable myinput { type filter hook input priority filter; policy accept; }' [root@master ~]# nft list chains [root@master ~]# nft list chain ip mytable myinput table ip mytable { chain myinput { type filter hook input priority filter; policy accept; } }
查看鏈的預設流向設定有兩種方式,一種直接使用『 nft list chains 』列出所有的鏈即可,一種則是列出某個表格的鏈名稱, 如上所示,最後一個指令主要是列出 mytable 內的 myinput 資料而已!檢查上面會比較單純!
上一小節談到 table 以及 chain 之後,現在要來討論的就是規則 (rule) 了!要學先要偷,所以,我們先來偷偷瞧一瞧預設的規則, 了解預設的規則之後,再來模仿與建立我們自己的規則~然後學習一下如何新增、移除規則~並且開始學習建立執行腳本的方式來建置整體規則囉!
我們先來瞧瞧全部 nftables 的規則,可以使用 list ruleset 指令來處理:
[root@master ~]# nft list ruleset
table inet nftables_svc {
set allowed_protocols {
type inet_proto
elements = { icmp, ipv6-icmp }
}
set allowed_interfaces {
type ifname
elements = { "lo" }
}
set allowed_tcp_dports {
type inet_service
elements = { 22, 9090 }
}
chain allow {
ct state established,related accept
meta l4proto @allowed_protocols accept
iifname @allowed_interfaces accept
tcp dport @allowed_tcp_dports accept
}
chain INPUT {
type filter hook input priority 20; policy accept;
jump allow
reject
}
}
table ip mytable {
chain myinput {
type filter hook input priority filter; policy accept;
}
}
我們可以看到兩個 chain,分別是 allow 以及 INPUT 兩個~因為 allow 並沒有 type 或 hook 的相關說明, 因此預設情況下,僅有 INPUT 鏈會被執行~分析 INPUT 鏈,我們只有看到 jump allow 以及 reject 而已! jump allow 意思是跳向 allow 鏈的意思~所以,會主動去分析 allow 那個鏈的內容!該內容有 4 條規則!
其實,預設的設定值寫的非常有結構,但是初學者很難看得懂~所以,我們還是拿當初的 iptables 三條最陽春的防火牆規則來說明! 還記得是那三條嗎?分別是 (1)放行回應封包 (2)放行 ICMP 封包 (3)放行 lo 裝置!所以,我們就透過上述的語法, 然後在我們的 myinput 鏈裡面增加這三條規則測試看看。不過,首先要先知道基本語法!相關語法有點像這樣:
基本語法: nft add rule [IP類型] [表格名稱] [鏈名稱] [規則項目]
基礎範例: nft add rule ip mytable myinput
規則項目與 iptables 互相比較,常見的項目有:
現在就讓我們來處理三條基礎防火牆規則吧!
# 1. 放行回應封包,所以需要用到 ct state 這個設定: [root@master ~]# nft add rule ip mytable myinput ct state established,related accept # 2. 放行 ICMP 這個檢測網路的封包協定,所以要用 meta l4proto 設定: [root@master ~]# nft add rule ip mytable myinput meta l4proto icmp accept # 3. 放行 lo 這個裝置成為信任裝置,所以需要 iifname 這個設定: [root@master ~]# nft add rule ip mytable myinput iifname lo accept # 4. 列出剛剛設定的規則 [root@master ~]# nft list ruleset [root@master ~]# nft list ruleset [ip|ip6|inet...] [root@master ~]# nft list ruleset ip table ip mytable { chain myinput { type filter hook input priority filter; policy accept; ct state established,related accept meta l4proto icmp accept iifname "lo" accept } }
這樣就搞定基礎防火牆設定~現在,繼續來放行其他的設定值!包括白名單/黑名單/特定網域放行的服務設計, 同時針對特定網卡的處理!來測試看看:
# 1. 放行 192.168.201.254 成為白名單: [root@master ~]# nft add rule ip mytable myinput ip saddr 192.168.201.254 accept # 2. 拒絕 10.30.30.0/24 整體網域成為黑名單: [root@master ~]# nft add rule ip mytable myinput ip saddr 10.30.30.0/24 reject # 3. 只有來自 enp1s0 的 192.168.201.0/24 要連線到 port 22 才放行 [root@master ~]# nft add rule ip mytable myinput iifname enp1s0 ip saddr 192.168.201.0/24 tcp dport 22 accept # 4. 來自 enp2s0 要連線到 port 22 就放行 [root@master ~]# nft add rule ip mytable myinput iifname enp2s0 tcp dport 22 accept # 5. 連線到本機的 port 80, 443 都予以放行 [root@master ~]# nft add rule ip mytable myinput tcp dport { 80, 443 } accept # 6. 全部拒絕 [root@master ~]# nft add rule ip mytable myinput reject # 7. 列表觀察 [root@master ~]# nft list ruleset ip table ip mytable { chain myinput { type filter hook input priority filter; policy accept; ct state established,related accept meta l4proto icmp accept iifname "lo" accept ip saddr 192.168.201.254 accept ip saddr 10.30.30.0/24 reject iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept iifname "enp2s0" tcp dport 22 accept tcp dport { 80, 443 } accept reject } }
基本上,跟 iptables 一樣,nft 也提供 add/insert/delete/replace 的規則處理方式~原則上,建議全部使用 add 來依序新增即可, 不過,如果有特殊需求,就可以透過 nft -a list.. 的方式,列出額外的參數後,就可以在某個位置 (handle) 增加規則了! 現在,讓我們來查看一下想要處理的『把手』點:
# 列出目前 ip 類型的規則的 handle 點 [root@master ~]# nft -a list ruleset ip table ip mytable { # handle 60 chain myinput { # handle 1 type filter hook input priority filter; policy accept; ct state established,related accept # handle 2 meta l4proto icmp accept # handle 3 iifname "lo" accept # handle 4 ip saddr 192.168.201.254 accept # handle 5 ip saddr 10.30.30.0/24 reject # handle 7 iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept # handle 8 iifname "enp2s0" tcp dport 22 accept # handle 9 tcp dport { 80, 443 } accept # handle 11 reject # handle 12 } }
上面的 handle 是 nftables 會自動記憶的 handle 點~即使你參考鳥哥的設定流程依序建立上述的規則,其 handle 點的數字位置, 應該也不會相同!所以,還是得要參考一下你自己的規則所對照的位置點才行!現在,假設我們要在 10.30.30.0/24 之前, 新增一個 10.30.30.120 的 IP 位址可以使用 port 22,那可以這樣處理:
# 使用 nft insert rule [IP類型] [table] [chain] handle [point] 規則: [root@master ~]# nft insert rule ip mytable myinput handle 7 ip saddr 10.30.30.120 tcp dport 22 accept [root@master ~]# nft -a list ruleset ip table ip mytable { # handle 60 chain myinput { # handle 1 type filter hook input priority filter; policy accept; ct state established,related accept # handle 2 meta l4proto icmp accept # handle 3 iifname "lo" accept # handle 4 ip saddr 192.168.201.254 accept # handle 5 ip saddr 10.30.30.120 tcp dport 22 accept # handle 13 ip saddr 10.30.30.0/24 reject # handle 7 iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept # handle 8 iifname "enp2s0" tcp dport 22 accept # handle 9 tcp dport { 80, 443 } accept # handle 11 reject # handle 12 } }
這樣就插入一個規則了~如果 IP 位址寫錯~應該是 10.30.30.210 不是 120 啦!那怎辦?可以使用 replace 的方式來處理!
# 使用 nft replace rule [IP類型] [table] [chain] handle [point] 規則: [root@master ~]# nft replace rule ip mytable myinput handle 13 ip saddr 10.30.30.210 tcp dport 22 accept [root@master ~]# nft -a list ruleset ip
那如果該規則是錯誤的,我們想要刪除呢?也很簡單~使用 delete 來處理!另外,不用寫完整的規則~ 只要找到 handle 點,直接刪除即可:
# 使用 nft delete rule [IP類型] [table] [chain] handle [point]: [root@master ~]# nft delete rule ip mytable myinput handle 13 [root@master ~]# nft -a list ruleset ip
從腳本去建立整體的規則,主要有兩種方式,一種是將目前的規則倒出來,成為相關的 nftables 設定檔,然後在裡面修改成需要的模樣, 之後就可以從 nft 載入了!另外一種則是跟我們之前建立的 iptables 相同,直接使用指令模式的方式建立好 shell script, 直接執行即可!我們先使用最簡單的方式來處理!就是透過 shell script 的方式來處理!另外,我們改用 inet 取代 ip 這種 IP 位址格式, 這是因為 inet 同時涵蓋 IPv4 與 IPv6,這樣未來要新增不同的 IP 格式比較容易!
為了方便設定,鳥哥這裡提供一個簡單的腳本,可以讓大家在腳本的前面設定好相關的變數之後,後續就可以讓腳本直接規劃好需要的表格、 鏈與規則~當然不是盡善盡美,至少可以讓大家比較方便一些。使用的方式如下所示果沒有相關的界面,直接讓該界面為空值,那就可以略過設定。 而在 allow_tcp 與 allow_udp 的部份,那個是針對內網的放行埠口,如果想要放行所有的埠口,直接寫下 0-65535 這樣也行! 至於白名單與黑名單,如果有多個,用逗號隔開就可以寫在一起!相當方便啊~同理,如果你的區網內有多個網段, 將各個網段寫入各自的 lan 裡面,用逗號隔開,就可以同時支援了!設計查詢上面很簡便!
# 1. 先建立 nftables.sh 這個腳本流程檔案: [root@master ~]# vim ~/nftables.sh #!/bin/bash # Part 0: 使用者輸入 wanif=enp1s0 wannet=192.168.201.0/24 apif=wlp7s0u1 apnet=192.168.10.0/24 lanif=enp2s0 lannet=192.168.20.0/24 dmzif=enp3s0 dmznet=192.168.30.0/24 white_list="192.168.201.254" black_list="10.30.30.0/24,10.40.40.0/24" allow_tcp="22,53,5901-5910" allow_udp="53,67,123" # Part 1: 針對本機的防火牆設定 # part 1.1: 清除所有規則 nft flush ruleset # part 1.2: 建立本機的表格與與輸入的鏈 nft add table inet mytable nft 'add chain inet mytable myinput { type filter hook input priority filter; policy drop; }' # part 1.2: 基礎防火牆規則,針對本機 nft add rule inet mytable myinput ct state established,related accept nft add rule inet mytable myinput meta l4proto icmp accept nft add rule inet mytable myinput iifname lo accept # part 1.3: 白名單/黑名單/特殊來源使用本機危險服務 # part 1.3.1: 針對 WAN 界面 (enp1s0) if [ "${white_list}" != "" ]; then nft add rule inet mytable myinput ip saddr { "${white_list}" } accept fi if [ "${black_list}" != "" ]; then nft add rule inet mytable myinput ip saddr { "${black_list}" } reject fi if [ "${wannet}" != "" ]; then nft add rule inet mytable myinput iifname ${wanif} ip saddr { "${wannet}" } tcp dport 22 accept fi # part 1.3.2: 針對 LAN 界面 (enp2s0),放行 ssh, dns, dhcp, ntp, 5901-5910 等埠口與服務 if [ "${lanif}" != "" ]; then nft add rule inet mytable myinput iifname ${lanif} tcp dport { ${allow_tcp} } accept nft add rule inet mytable myinput iifname ${lanif} udp dport { ${allow_udp} } accept fi # part 1.3.3: 針對 AP 界面 (wlp7s0u1),放行 ssh, dns, dhcp, ntp, 5901-5910 等埠口 if [ "${apif}" != "" ]; then nft add rule inet mytable myinput iifname ${apif} tcp dport { ${allow_tcp} } accept nft add rule inet mytable myinput iifname ${apif} udp dport { ${allow_udp} } accept fi # part final: 儲存規則 echo "flush ruleset" > /etc/nftables/mynftables.nft nft -a list ruleset >> /etc/nftables/mynftables.nft # 2. 修改設定檔,未來呼叫的是 /etc/nftables/mynftables.nft 載入檔才對! [root@master ~]# vim /etc/sysconfig/nftables.conf #include "/etc/nftables/main.nft" include "/etc/nftables/mynftables.nft" [root@master ~]# systemctl restart nftables [root@master ~]# nft list ruleset table inet mytable { chain myinput { type filter hook input priority filter; policy drop; ct state established,related accept meta l4proto icmp accept iifname "lo" accept ip saddr 192.168.201.254 accept ip saddr { 10.30.30.0/24, 10.40.40.0/24 } reject with icmp port-unreachable iifname "enp1s0" ip saddr 192.168.201.0/24 tcp dport 22 accept iifname "enp2s0" tcp dport { 22, 53, 5901-5910 } accept iifname "enp2s0" udp dport { 53, 67, 123 } accept iifname "wlp7s0u1" tcp dport { 22, 53, 5901-5910 } accept iifname "wlp7s0u1" udp dport { 53, 67, 123 } accept } }
要執行查看一下結果才對!從上面的執行結果,我們也能知道,基本上,我們也可以直接修改 /etc/nftables/mynftables.nft 這個我們自訂的規則紀錄檔~基本設定就跟指令設定差不多~而如果需要載入該設定檔的內容,亦即值些修改 /etc/nftables/mynftables.nft 之後,你也可以這樣做:
[root@master ~]# nft -f /etc/nftables/mynftables.nft
先來回想一下,我們的 NAT 有 SNAT 與 DNAT,而 SNAT 有『snat, masquerade』,DNAT 有『dnat, redirect』, SNAT 用的是 postrouting 相關封包流向,而 DNAT 則是 prerouting 相關的封包流向~所以,接下來就是需要這些 postrouting / prerouting 等等的鏈囉!
我們預計想要增加一個名為 mynat 的表格,這個表格適用於所有的 IP 格式 (inet),並且在建立之後,新增兩個鏈,分別是 mysnat 與 mydnat。 這兩個鏈的內部政策設定為:
實在來建立這幾樣資料:
# 1. 建立 mynat 表格 [root@master ~]# nft add table inet mynat # 2. 建立 mysnat 鏈 [root@master ~]# nft add chain inet mynat mysnat { type nat hook postrouting priority srcnat \; policy accept \; } # 3. 建立 mydnat 鏈 [root@master ~]# nft add chain inet mynat mydnat { type nat hook prerouting priority dstnat \; policy accept \; } [root@master ~]# nft list chains .... table inet mynat { chain mysnat { type nat hook postrouting priority srcnat; policy accept; } chain mydnat { type nat hook prerouting priority dstnat; policy accept; } }
回想一下 iptables 的表格與鏈,在 nat 表格裡面有個 POSTROUTING 作為 SNAT 的對吧!目前我們這個 mynat 就是類似 nat 表格, 而 mysnat 就是 POSTROUTING 的意思!表格與鏈的名稱都可以自己取名,不過,內部的類型 (type) 與封包流向,就得要確定為 postrouting! 這個是重要部份!建立好了表格與鏈之後,再來就是讓所有的內部三個介面對 internet 連線時,都可以進行 IP 位址的偽裝。
再次回到 7.1.4 小節查看一下,我們的內部網路分為三個區塊,這三個區塊的網卡與網段對外連線時,都得要透過偽裝才行! 偽裝的方法很簡單,我們可以簡易的這樣設計即可:
[root@master ~]# nft add rule inet mynat mysnat iifname enp2s0 oifname enp1s0 \ > ip saddr 192.168.20.0/24 masquerade [root@master ~]# nft add rule inet mynat mysnat iifname wlp7s0u1 oifname enp1s0 \ > ip saddr 192.168.10.0/24 masquerade [root@master ~]# nft add rule inet mynat mysnat iifname enp3s0 oifname enp1s0 \ > ip saddr 192.168.30.0/24 masquerade [root@master ~]# nft list chain inet mynat mysnat table inet mynat { chain mysnat { type nat hook postrouting priority srcnat; policy accept; iifname "enp2s0" oifname "enp1s0" ip saddr 192.168.20.0/24 masquerade iifname "wlp7s0u1" oifname "enp1s0" ip saddr 192.168.10.0/24 masquerade iifname "enp3s0" oifname "enp1s0" ip saddr 192.168.30.0/24 masquerade } }
你可以分別前往 server001 以及 client001 測試一下,現在兩部系統的網路應該都是通的!也可以互相 ping 的到才對! 這就完成了簡單的 SNAT 設定!
假設如同前面小節提到的,或許因為某些緣故,因此我們外網 WAN 界面的 port 22 不能直接使用,得要透過類似 2121 來轉遞! 這時就得要用到 redirection 了!redirection 方式很簡單啊!我們只有轉遞 WAN 界面而已喔~內部界面沒有變化!
[root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 2121 redirect to 22 [root@master ~]# nft list chain inet mynat mydnat table inet mynat { chain mydnat { type nat hook prerouting priority dstnat; policy accept; iifname "enp1s0" tcp dport 2121 redirect to :22 } }
你就可以發現到,只有從外部界面 (enp1s0) 來的連線可以連接到 port 2121 而已!從其他界面來的,就無法連接到 port 2121, 因為沒有設定轉遞的功能!這樣就處理好 redirection 囉!
由於我們的 Web 放置在 192.168.30.1 這部位於 DMZ 的伺服器上,在 LAN 的內部可以使用 http://192.168.30.1 來查詢到這部 Web 頁面。 同樣的,在外部系統基本上是無法連接到這部 Web 伺服器的,我們得要透過 prerouting 的 DNAT 功能!如同在 iptables 的章節, 要進行 DNAT 倒也是很簡單~從 WAN 界面來的封包,只要是 port 80 的,就轉到後端系統去!那就這樣處理:
# 1. 實際運作的指令如下: [root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 80 dnat ip to 192.168.30.1 # 2. 如果切換到不同的埠口,那就得要這樣處理 [root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 8080 dnat ip to 192.168.30.1:80 # 3. 如果 IP 位址不是 IPv4 而是 IPv6 的話,得要改成這樣: [root@master ~]# nft add rule inet mynat mydnat iifname enp1s0 tcp dport 8081 dnat ip6 to fe80::5054:ff:fe00:3001
這樣很快就處理好了外部的 DNAT 連線了!也算是相當的輕鬆愉快啊!
基本上,DMZ 就是需要做個限制才可以的!所以,我們得要模仿 iptables 當中的 DMZ 章節內容,使用 filter 表格內的 FORWARD 鏈來管理! 因此,讓我們回到 mytable 的表格當中,建立 myforward 鏈!要注意的是,我們已經有 myinput 鏈了!所以處理 myforward 鏈要注意一下, 不用重複建立表格~建立 myforward 這條鏈也不是太難,這樣來處理看看:
[root@master ~]# nft add chain inet mytable myforward { type filter hook forward priority filter \; policy accept \; }
我們當然可以對 LAN 做比較大範圍的偽裝放行,所以 forward 預設的政策就先設定為 accept ! 但是對於 DMZ 這個會被 internet 存取的區域而言,應該還是稍微避免這樣的情境比較妥當! 我們需要針對兩個部份來處理,分別是 (1) DMZ 對 internet 的連線與 (2) DMZ 對 LAN 的連線。
基本上,Internet 還是需要 DMZ 回傳的資訊,所以,我們會需要回應封包!而且,為了 DMZ 裡面 server 的安裝與管理, 應該還是要放行 DNS 以及 port 80, 443 的連線,這個部份我們先來處理看看:
[root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 ct state established,related accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 udp dport 53 accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 tcp dport { 53, 80, 443 } accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp1s0 reject
此時 DMZ 對於 Internet 的主動連線就只剩下查詢 DNS 主機名稱以及網頁伺服器查詢的功能~除非你還要放行 FTP, 否則,這樣的情況下,對於 DMZ 的主動連上 Internet 來說,應該是有相當限度的管制了。再來則是管理 DMZ 對於 LAN 的連線了! 這種連線方向中,應該是要放行狀態封包,其他通通都關掉才合理!
[root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp2s0 ct state established,related accept [root@master ~]# nft add rule inet mytable myforward iifname enp3s0 oifname enp2s0 reject [root@master ~]# nft list chain inet mytable myforward table inet mytable { chain myforward { type filter hook forward priority filter; policy accept; iifname "enp3s0" oifname "enp1s0" ct state established,related accept iifname "enp3s0" oifname "enp1s0" udp dport 53 accept iifname "enp3s0" oifname "enp1s0" tcp dport { 53, 80, 443 } accept iifname "enp3s0" oifname "enp1s0" reject iifname "enp3s0" oifname "enp2s0" ct state established,related accept iifname "enp3s0" oifname "enp2s0" reject } }
這樣處理下來,對於整體 LAN 跟 DMZ 的對外連網部份就大致搞定!
某些特殊的情況底下,你的 MTU 可能應該有點改變,例如在 PPPoE 這種撥接的環境中,我們的封包經過 PPPoE 的路由器環境時, 因為表頭還得要加上 PPPoE 的資訊,因此 MTU 不可能保持 1500,這時我們的 PPPoE 通常會偷偷的幫我們將 MTU 改成 1492 之類的情境。 不過需要注意的是,有時候我們的 router 會以為封包過大,因此會回傳 client 端封包過大的問題,而不予以拆解後重新傳送。 這時,這個封包的傳遞,可能就會失敗,因此整體連線就會斷斷續續的~很怪異就是了。這個問題就是所謂的『 TCP maximum segment size (MSS) 的 clamping 』情況。
在過去,用 Linux 核心處理的方法,通常會耗盡很多心力~但是,新的 nftables 機制裡面,藏了一個簡單的方法,通常在 forward 鏈裡面將 MSS 的最大值設定為系統能通過的最大 MTU 路徑!很快就可以搞定這個問題的發生!朋友們可以到後續的參考資料瞧一瞧原始資料, 這裡取快速的方式來設定:
[root@master ~]# nft add rule inet mytable myforward tcp flags syn tcp option maxseg size set rt mtu
未來在 forward 鏈裡面,記得都要加上這一段喔!可以排除很多問題!
透過腳本化處理,會比較容易管理 nftables 囉!同樣處理一下 /root/nftables.sh 腳本內容:
[root@master ~]# vim ~/nftables.sh .... # part 2: 開始處理 NAT 的部份 # part 2.1: 建立表格與鏈 nft add table inet mynat nft add chain inet mynat mysnat { type nat hook postrouting priority srcnat \; policy accept \; } nft add chain inet mynat mydnat { type nat hook prerouting priority dstnat \; policy accept \; } # part 2.2: 建立 SNAT 功能 if [ "${apif}" != "" ]; then nft add rule inet mynat mysnat iifname ${apif} oifname ${wanif} ip saddr ${apnet} masquerade fi if [ "${lanif}" != "" ]; then nft add rule inet mynat mysnat iifname ${lanif} oifname ${wanif} ip saddr ${lannet} masquerade fi if [ "${dmzif}" != "" ]; then nft add rule inet mynat mysnat iifname ${dmzif} oifname ${wanif} ip saddr ${dmznet} masquerade fi # part 2.3: 建立 redirection 功能 nft add rule inet mynat mydnat iifname ${wanif} tcp dport 2121 redirect to 22 # part 2.4: 建立 DNAT 功能 nft add rule inet mynat mydnat iifname ${wanif} tcp dport 80 dnat ip to 192.168.30.1 # part 3: 處理 DMZ 功能 nft add chain inet mytable myforward { type filter hook forward priority filter \; policy accept \; } nft add rule inet mytable myforward tcp flags syn tcp option maxseg size set rt mtu nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} ct state established,related accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} udp dport 53 accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} tcp dport { 53, 80, 443 } accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${wanif} reject nft add rule inet mytable myforward iifname ${dmzif} oifname ${lanif} ct state established,related accept nft add rule inet mytable myforward iifname ${dmzif} oifname ${lanif} reject # part final: 儲存規則 [root@master ~]# sh ~/nftables.sh