Linux防火墙,Netfiler,iptables概念原理全解析(下)

前言:  

       不知道你有没有这样的困惑,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)包含如下的部分

  1. 一个"ipt_entry"结构体
  2. 0个或多个 "ipt_entry_match"结构体, 每个结构体中都附一个可变量的data
  3. 一个"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. 小结

 

posted @ 2021-10-22 16:09  水鬼子  阅读(731)  评论(0编辑  收藏  举报