shuiguizi

前言:  

       不知道你有没有这样的困惑,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)定义了网络连接的信任级别。这是一个一对多的关系,也就是说一个连接只能属于一个区域,但一个区域可以用于多个网络连接。

分类:

技术点:

相关文章:

  • 2021-07-19
  • 2021-08-26
  • 2021-06-06
  • 2021-10-23
  • 2022-02-02
猜你喜欢
  • 2022-12-23
  • 2021-06-02
  • 2022-01-22
  • 2021-11-17
  • 2021-06-04
  • 2021-12-16
  • 2021-09-18
相关资源
相似解决方案