前言:
不知道你有沒有這樣的困惑,iptables會用,可總是知其然不知其所以然,然后常常江里面的概念搞混,尤其是類似的操作,卻常常是以不同的稱謂出現:netfilter,iptables, firewalld....
所以,我們有必要了解一下其真正的內核實現,這樣有助於我們記憶iptables的用法。
本文主要是基於官網文檔進行的翻譯以及自己的理解: https://netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO.html#toc3, 還包括一些其他的連接都會在相應的位置注明
大綱:
1.netfilter框架
1.1 netfilter原理概述
1.2 netfilter的鈎子們
1.3 netfilter的核心基礎
1.4 代碼摘要
1.5 小小結
2.基於netfilter框架的實現
2.1 數據包篩選: IP Tables
2.2 連接跟蹤(Connection Tracking)
2.3 其他待補充
2.4 小小結
附: IP Tables的代碼實現舉例&實操
3.IP Tables的內核空間與用戶空間
3.1 ip_tables的數據結構
3.2 用戶空間的ip_tables
3.3 ip_tables的使用和遍歷
3.4 用戶空間工具
3.5 共享庫: libiptc
3.6 小小結
附:實操
4. iptables, iptables.service, firewalld.service,firewall-cmd辨析
4.1 iptables.service 和 iptables cmd
4.2 由firewalld實現的動態防火牆
4.3 firewalld.service和iptables.service
4.4 小小結
注:一,二章見:Linux防火牆,Netfiler,iptables概念原理全解析(上) - 水鬼子 - 博客園 (cnblogs.com)
三. IP Tables的內核空間與用戶空間
原文:
iptables simply provides a named array of rules in memory (hence the name `iptables'), and such information as where packets from each hook should begin traversal. After a table is registered, userspace can read and replace its contents using getsockopt() and setsockopt().
iptables does not register with any netfilter hooks: it relies on other modules to do that and feed it the packets as appropriate; a module must register the netfilter hooks and ip_tables separately, and provide the mechanism to call ip_tables when the hook is reached.
解析:
iptables 只是在內存中提供了一個命名的規則數組(因此命名為"iptables"),以及一些諸如來自某個hook的數據包應該從哪里開始遍歷的信息。
一旦這個表被注冊了,用戶空間可以使用getsockopt() 和setsockopt() 對他的內容進行讀取和替換。
iptables 本身不注冊任何 netfilter hook:它依賴其他模塊來做到這一點,並根據需要向它提供數據包;模塊必須分別注冊 netfilter hooks和 ip_tables,並需要提供相應的機制在到達hooks時調用ip_tables。
wxy:
在上一個章節中,可以看到IP Tables系統中命名有注冊hook的函數,為什么這里卻說"does not register with any netfilter hooks"
我的理解是這樣的:IP Tables系統只是一種內置的實現,只是存在而已,
如果想要將該系統置入到netfiler框架,則需要加載這個模塊(IP Tables系統是以一個個模塊的形式呈現的,這些模塊比如:iptable_filter,iptable_mangle,iptable_nat....)
2. ip_tables的數據結構
為了方便起見,內核態和用戶態使用的是相同的數據結構, 在用戶態用來表示一條rule,有些字段僅在內核態有用。每條規則(rule)包含如下的部分
- 一個"ipt_entry"結構體
- 0個或多個 "ipt_entry_match"結構體, 每個結構體中都附一個可變量的data
- 一個"ipt_entry_target"結構體, 每個結構體中都附一個可變量的data
ipt_entry_match和ipt_entry_target結構體類似,都包含一個代表總長的字段(分別是match_size和target_size), 一個union holding match名稱或target名稱(用於用戶態), 以及一個指針(用於內核態)
3. 用戶空間的ip_tables
支持用戶空間的四種操作:可以讀取當前的表,讀取信息(hook的位置以及table的大小), 替換表(以及抓取舊的計數器), 和添加新的計數器.
利用 libiptc庫,可以在用戶空間模擬任何原子操作,該庫提供了方便的"add/delete/replace"編程原語。
因為這些表會被轉移到內核空間,所以對於具有不同用戶空間和內核空間類型規則的機器(例如具有 32 位用戶空間的 Sparc64), 對齊則成為了一個重要的問題。這種情況下,通過覆蓋掉"libiptc.h"中IPT_ALIGN的定義來解決。
4. ip_tables的使用和遍歷
內核從特定的hook指向的位置開始遍歷。依次檢查每個"struct ipt_entry_match"(調用與該匹配項關聯的匹配函數), 如果"struct ipt_ip"元素匹配,則檢查該規則。如果 match 函數返回 0,則迭代在該規則上停止。如果它設置`hotdrop'參數為1,數據包也將被立即丟棄(這用於一些可疑數據包,例如在tcp匹配功能中)。
如果迭代繼續到最后,計數器遞增, 'struct ipt_entry_target'將會被檢查:如果它是標准的target,則讀取'verdict' 字段(負表示數據包判定,正表示跳轉到的偏移量)。如果答案是正的並且偏移量並不是對應下一條規則,則設置一下"back"變量,並且將之前的"back"的值放置在該規則的"comefrom"字段中。
對於非標准target,調用目標函數:它返回一個判斷(非標准目標不能跳轉,因為這會破壞靜態循環檢測代碼)。判決可以是 IPT_CONTINUE,以繼續執行下一條規則。
5. 用戶空間工具
現在您已經編寫了漂亮的內核模塊,您可能希望從用戶空間控制其選項。我沒有為iptables的每個擴展設置一個分支版本,而是使用最新的90 年代技術:furbies。抱歉,我的意思是共享庫。
現在創建新表通常不需要對iptables做任何擴展:用戶只需使用`-t' 選項使其使用新表。
共享庫應該有一個 '_init()' 函數,它會在被加載時自動調用:這類似於內核模塊的'init_module()'函數。該函數應該調用'register_match()'或'register_target()',具體取決於您的共享庫是提供新的match還是新的target。
你需要提供一個共享庫:他可以用來初始化部分結構體,或者提供一些額外的選項。
'iptables.h'頭文件中描述了一些有用的函數,尤其是:
- check_inverse():
- ...
6. 共享庫: libiptc
libiptc是 iptables 控制庫,用於列出和操作 iptables 內核模塊中的規則。雖然它目前是用於iptables應用程序(wxy: 指iptables cmd),但它使用它編寫其他工具也相當容易。不過您需要是 root 用戶才能使用這些功能。
內核表本身只是一個規則表和一組代表入口點(entry points)的數字。鏈名稱(例如 "INPUT"等)其實是庫幫我們抽象出來的。用戶自定義鏈通過在用戶定義鏈的頭部之前插入一個錯誤節點來標記,該錯誤節點包含了位於target的額外數據段中的鏈名稱(內置鏈的位置由三個表的入口點定義)。
支持的標准targets有:ACCEPT、DROP、QUEUE(分別轉換為 NF_ACCEPT、NF_DROP 和 NF_QUEUE)、RETURN(轉換為由 ip_tables 處理的特殊 IPT_RETURN 值)和 JUMP(從鏈名稱到表中的實際偏移量)。
當'iptc_init()'被調用時,包括計數器在內的表被讀取。該表可以用'iptc_insert_entry()'、'iptc_replace_entry()'、'iptc_append_entry()'、'iptc_delete_entry()'、'iptc_delete_num_entry()'、'iptc_flush_entries()'、'iptc_flush_entries()'、'iptc_zero_entries()', `iptc_create_chain()' `iptc_delete_chain()', and `iptc_set_policy()' 這些函數操作。
但是,在調用"iptc_commit()"函數之前,不會將表更改寫回到內核。這意味着在同一條鏈上運行的兩個庫用戶可以相互競爭;所以需要使用鎖來防止這種情況發生,不過目前還沒有這樣做。
然而,沒有計數器的競爭。因為計數器以這樣一種方式被重新加回到內核中,即表的讀取和寫入之間的計數器增量仍然顯示在新表中。
7. 小結
首先,netfiler定制了框架,
然后,內置系統 IP Tables(iptable_filter, nat, raw....)根據這個框架實現了使用表的方式維護規,
最后,如果有模塊需要 IP Tables系統功能,則加載系統中各個模塊進行注冊
但是,IP Tables表中的規則來自哪里呢?所以一個很重要的問題就是如何定制規則?由於一切都是服務於用戶層的應用,於是就有了內核空間和用戶空間的交互!總結如下:
0. 首先,內置系統 IP Tables提供了socket服務用於操作表,支持的操作包括:IPT_SO_SET_REPLACE,IPT_SO_GET_INFO....
1. 然后,提供了一個庫:libiptc,會通過getsockopt()和setsockopt()與內核打交道,一些操作比如:
TC_INIT/iptc_init() :getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) //其中,#define SO_GET_INFO IPT_SO_GET_INFO
TC_COMMIT/iptc_commit: ret = setsockopt(handle->sockfd, TC_IPPROTO, SO_SET_REPLACE, repl...);
2. 最后,在用戶態的應用中,比如/usr/sbin/iptables應用,就會通過調用庫完成對內核中的ip_tables進行操作。
附:實操
1. 查看該應用都需要哪些庫
# ldd /usr/sbin/iptables linux-vdso.so.1 => (0x00007ffdb07c3000) libip4tc.so.0 => /lib64/libip4tc.so.0 (0x00007f71eb776000) libip6tc.so.0 => /lib64/libip6tc.so.0 (0x00007f71eb56e000) libxtables.so.10 => /lib64/libxtables.so.10 (0x00007f71eb361000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f71eb15d000) libm.so.6 => /lib64/libm.so.6 (0x00007f71eae5b000) libc.so.6 => /lib64/libc.so.6 (0x00007f71eaa8d000) /lib64/ld-linux-x86-64.so.2 (0x00007f71eb97e000)
1. 跟蹤iptables cmd的調用鏈
# ltrace /usr/sbin/iptables -I INPUT -p tcp --dport 8080 -j ACCEPT __libc_start_main(0x403170, 9, 0x7ffca02bd328, 0x40f150 <unfinished ...> __xpg_basename(0x7ffca02be6ea, 0x7ffca02bd328, 0x40f280, 0x40f150) = 0x7ffca02be6f4 strcmp("iptables", "iptables") = 0 xtables_init_all(0x615500, 2, 8, 0) = 0 ... iptc_init(0x40ff20, 6, 384, 0) = 0x1a365e0 iptc_is_chain(0x7ffca02be71d, 0x1a365e0, 1, 6) = 0 xtables_malloc(200, 0, 48, 0) = 0x1a376c0 memcpy(0x1a37730, "0\0tcp\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 48) = 0x1a37730 memcpy(0x1a37760, "(\0ACCEPT\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 40) = 0x1a37760 free(0x1a36530) = <void> iptc_insert_entry(0x7ffca02be700, 0x1a376c0, 0, 0x1a365e0) = 1 xtables_rule_matches_free(0x7ffca02bd198, 0xfffffffe, 0, 0) = 0x1a36520
四. iptables, iptables.service, firewalld.service,firewall-cmd辨析
先簡單認識一下
參考鏈接:https://www.cnblogs.com/hftian/p/8280841.html
https://akm111.wordpress.com/2017/04/29/red-hat-centos-linux-firewalld/
注:這個截圖的原出處嗎,搜索了很多但是沒有找到
1. iptables.service 和 iptables cmd
【參考鏈接1】:
https://forums.centos.org/viewtopic.php?t=69433
解析:
【參考鏈接2】:
iptables詳解(3):iptables規則管理-朱雙印博客 (zsythink.net)
【參考鏈接3】:
https://docs.openshift.com/container-platform/3.7/admin_guide/iptables.html
The iptables service supports a local network firewall. It assumes total control of the iptables configuration. When it starts, it flushes and restores the complete iptables configuration. The restored rules are from its configuration file, /etc/sysconfig/iptables. The configuration file is not kept up to date during operation, so the dynamically added rules are lost during every restart.
The iptables.service configuration is loaded from: /etc/sysconfig/iptables
To make permanent rules changes, edit the changes into this file. Do not include Docker or OpenShift Container Platform rules.
After iptables.service is started or restarted on a node, the Docker service and atomic-openshift-node.service must be restarted to reconstruct the needed iptables configuration.
解析:
iptables 服務支持一個本地的網絡防火牆。它假設完全控制了iptables 配置。當它啟動時, 它會flush並restores完整的iptables 配置。restores的規則來自其配置文件, /etc/sysconfig/iptables。在運行過程中配置文件並不會保持最新,因此每次重啟時動態添加的規則都會丟失。
所述iptables.service配置從加載:/etc/sysconfig/iptables
要進行永久性規則更改,請將更改編輯到此文件中。不包括 Docker 或 OpenShift Container Platform 規則。
在節點上啟動或重啟iptables.service后,必須重啟Docker 服務和atomic-openshift-node.service以重構所需的 iptables 配置。
【小小結】:
首先,可以認為iptables cmd是用戶層面最接近內核操作的,一切對IP Tables(內核空間的iptables)操作都是由這個應用完成的, 包括用戶空間的其他應用.
但是,前面也說了,內核的IP Tables只是在內存中維護所有的規則,也就是說: 操作系統重啟,模塊重新加載等這些操作,都會使得之前用戶空間的所有操作都沒有了,所以,為了解決這個問題,iptables.service誕生了。
iptables.service,保存內核中的IP Tables表中的規則到指定文件: /etc/sysconfig/iptables中,每次服務的啟動都會將文件中的配置刷入內核。
所以,我們常常看到這樣的操作:iptables操作之后,還要執行iptables-save, 其實即使將所有的規則本地化...
2. 由firewalld實現的動態防火牆
原文鏈接: https://fedoraproject.org/wiki/Firewalld?rd=FirewallD
firewalld 提供了一個動態管理的防火牆,支持通過划分網絡/防火牆的區域來定義網絡連接或接口的信任級別。它支持 IPv4、IPv6 防火牆設置和以太網橋,並且將運行時配置和永久配置做了分離。它還支持服務或應用程序直接添加防火牆規則的接口。
以前的帶有 system-config-firewall/lokkit 的防火牆模型是靜態的,每次更改都需要完全重啟防火牆。這還包括卸載防火牆 netfilter 內核模塊和加載新配置所需的模塊。模塊的卸載破壞了狀態防火牆以及已經建立的連接。
另一方面,防火牆守護進程可以動態管理防火牆並只應用變更的那部分而無需重新啟動整個防火牆。因此無需重新加載所有防火牆內核模塊。但是使用防火牆守護進程要求所有對防火牆的修改都使用該守護進程完成,以確保守護進程中的狀態和內核中的防火牆同步。防火牆守護進程無法解析 ip*tables 和 ebtables 命令行工具添加的防火牆規則。
該守護進程通過 D-BUS 提供有關當前活動防火牆設置的信息,並使用 PolicyKit 身份驗證方法通過 D-BUS 接受更改。
- 守護進程
應用程序、守護進程和用戶可以通過請求 D-BUS 來啟用防火牆功能。這個功能可以是預定義(predefined)的一種防火牆功能,例如服務、端口和協議組合、端口/數據包轉發、偽裝或 icmp 阻止。該功能可以啟用一段時間,也可以再次禁用。
所謂的其他服務(例如 libvirt)可以使用直接接口添加自己的規則則是指使用 iptables 指令(arguments)和參數(parameters)。
一些用於 amanda、ftp、samba 和 tftp 等服務的netfilter 防火牆助手(helpers),只要它們屬於預定義服務(predefined service)的一部分,則也是由守護進程來處理。加載額外的helpers不是當前接口的一部分。對於某些helpers程序,只有在模塊處理的所有連接都關閉后才能卸載。因此,跟蹤連接的信息就變得很重要,需要加以考慮。
- 靜態防火牆 (system-config-firewall/lokkit)
實際上帶有 system-config-firewall 和 lokkit 的靜態防火牆模型仍然可用,但不會與守護進程同時運行。用戶或管理員可以通過啟用相應的服務來決定使用哪種防火牆解決方案。
需要在安裝時或首次啟動時計划好為防火牆解決方案添加一個選擇器。其他解決方案的配置將保持不變,只需切換到其他模型即可啟用。
防火牆守護進程獨立於 system-config-firewall,但不應該同時使用。
- 由iptables/ip6tables服務實現的靜態防火牆
如果想通過iptables 和 ip6tables 服務來使用自己的靜態防火牆規則,這需要安裝 iptables-services 並禁用 firewalld 並啟用 iptables 和 ip6tables:
# yum install iptables-services
# systemctl mask firewalld.service
# systemctl enable iptables.service
# systemctl enable ip6tables.service
將 /etc/sysconfig/iptables 和 /etc/sysconfig/ip6tables 用於您的靜態防火牆規則。
注意:iptables 和 iptables-services 包並不提供和服務一起使用的防火牆規則。該服務可用於兼容性或想要使用自己的防火牆規則的人。不過,您可以安裝和使用 system-config-firewall 來為服務創建規則。為了能夠使用 system-config-firewall,您必須停止 firewalld。
創建與服務一起使用的規則后,停止 firewalld 並啟動 iptables 和 ip6tables 服務:
# systemctl stop firewalld.service # systemctl start iptables.service # systemctl startip6tables.service ... 10月 19 14:51:27 wxy-see-159 systemd[1]: Starting IPv4 firewall with iptables... 10月 19 14:51:28 wxy-see-159 iptables.init[66838]: iptables: Applying firewall rules: [ 確定 ]
其他對比信息:
# systemctl status firewalld.service ● firewalld.service - firewalld - dynamic firewall daemon... # systemctl status iptables.service ● iptables.service - IPv4 firewall with iptables...
- firwalled中的zone
網絡區域(zone)定義了網絡連接的信任級別。這是一個一對多的關系,也就是說一個連接只能屬於一個區域,但一個區域可以用於多個網絡連接。
🔗 Predefined services
A service is a combination of port and/or protocol entries. Optionally netfilter helper modules can be added and also a IPv4 and IPv6 destination address.
🔗 Ports and protocols
tcp 或 udp 端口的定義,其中端口可以是單個端口或端口范圍。
🔗 ICMP blocks
選定的 Internet 控制消息協議 (ICMP) 消息。這些消息要么是信息請求,要么是作為對信息請求的答復或在錯誤情況下創建的。
🔗 Masquerading
專用網絡的地址映射到並隱藏在公共 IP 地址之后。這是地址轉換的一種形式。
🔗 Forward ports
一個端口映射到另一個端口和/或另一個主機。
3. firewalld.service和iptables.service
【firewall和firewalld】
firewall:
wiki:https://en.wikipedia.org/wiki/Firewall_(computing)
表示一種通用的功能:防火牆這個功能,所謂防火牆的功能就是用於安全的一種功能....
firewalld: 表示防火牆功能的一種具體的實現,該應用被稱為: firewalld.
【參考鏈接1】: https://oracle-base.com/articles/linux/linux-firewall-firewalld#reverting-to-iptables
原文:
You need to distinguish between the iptables service and the iptables command. Although firewalld is a replacement for the firewall management provided by iptables service, it still uses the iptables command for dynamic communication with the kernel packet filter (netfilter). So it is only the iptables service that is replaced, not the iptables command. That can be a confusing distinction at first.
解析:
注意區分iptables服務和iptables命令。盡管firewalld取代了iptables服務來提供防火牆管理,但它仍然使用iptables命令與內核包過濾模塊(netfilter)進行動態通信。所以,只有iptables服務被替換,而不是iptables命令。
【參考鏈接2】:https://jfearn.fedorapeople.org/fdocs/en-US/Fedora/20/html/Security_Guide/sec-Comparison_of_Firewalld_to_system-config-firewall.html
The essential differences between firewalld and the iptables service are:
The iptables service stores configuration in /etc/sysconfig/iptables while firewalld stores it in various XML files in /usr/lib/firewalld/ and /etc/firewalld/. Note that the /etc/sysconfig/iptables file does not exist as firewalld is installed be default on Fedora.
With the iptables service, every single change means flushing all the old rules and reading all the new rules from /etc/sysconfig/iptables while with firewalld there is no re-creating of all the rules; only the differences are applied. Consequently, firewalld can change the settings during run time without existing connections being lost.
Both use iptables tool to talk to the kernel packet filter.
解析:
firewalld和iptables服務之間的本質區別是:
iptables服務將配置存儲在/etc/sysconfig/iptables中,而firewalld將配置存儲在/usr/lib/firewalld/和/etc/firewalld/中的各種XML文件中。請注意,缺省情況下/etc/sysconfig/iptables文件不存在,因為默認情況下在Fedora上安裝的是firewalld。
對於iptables服務,每次更改都意味着會flush掉所有舊規則以及從/etc/sysconfig/iptables讀取所有新規則,而對於firewalld,則不會重新創建所有規則;僅將差異部分apply。因此,firewalld可以在運行時更改設置而不會丟失現有連接。
兩者都使用iptables工具與內核包過濾器通信
【參考鏈接3】https://blog.karthisoftek.com/a?ID=00900-80613201-b66a-4660-bc4c-2ff2483cd3d1
解析:
為了更好的理解什么是動態防火牆,我們需要對比一下iptables(靜態防火牆)和firewalld(動態防火牆)。就iptables而言,用戶添加了新的防火牆策略后,需要重新加載才能生效。在這個過程中,iptables service 會先清除舊的防火牆策略,然后完全重新加載所有新的防火牆規則;對於 firewalld防火牆,任何規則的變更都不需要重新加載整個防火牆規則列表,只需將更改的部分保存並更新到正在運行的 iptables 中即可。
iptables和firewalld不是相對獨立的。兩者之間存在一定的聯系。firewalld 提供了一個守護進程和服務,以及命令行和圖形界面配置工具。它只是替代了iptables的服務部分,底層仍然使用iptables作為防火牆規則管理的入口點。為了更直觀的看到兩者的關系,看一下
【小小結】
結合本章節最開始的那張圖,我們可以確定:二者都是基於IP Tabels系統實現的防火牆功能,核心思想其實都是維護內核中的那些表
共同點:都可以稱之為防火牆:firewall
都是調用iptables工具操作內核的IP Tables,當然也就都是基於Netfiler框架
不同點:iptables.service更直接,即直接放映了內核的表規則, 由於使用加載文件的方式,所以稱之為靜態.
firewalld.service更人性化,增加了更多的維度去定制規則,比如以應用的角度去定制規則等....
【附. 實操】
# systemctl status firewalld ● firewalld.service - firewalld - dynamic firewall daemon Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled) Active: active (running) since 二 2021-10-12 15:43:45 CST; 6 days ago # systemctl status iptables.service ● iptables.service - IPv4 firewall with iptables Loaded: loaded (/usr/lib/systemd/system/iptables.service; enabled; vendor preset: disabled) Active: active (exited) since 三 2021-10-20 16:51:46 CST; 1 day 17h ago Process: 40171 ExecStop=/usr/libexec/iptables/iptables.init stop (code=exited, status=0/SUCCESS) Process: 40194 ExecStart=/usr/libexec/iptables/iptables.init start (code=exited, status=0/SUCCESS) Main PID: 40194 (code=exited, status=0/SUCCESS) CGroup: /system.slice/iptables.service 10月 20 16:51:46 wxy-see-159 systemd[1]: Starting IPv4 firewall with iptables... 10月 20 16:51:46 wxy-see-159 iptables.init[40194]: iptables: Applying firewall rules: [ 確定 ] # iptables -h iptables v1.4.21 Usage: iptables -[ACD] chain rule-specification [options]
4. 小結