iptables防火墙调试,想打印个日志就这么难

iptables,防火墙,调试,打印,日志,这么 · 浏览次数 : 269

小编点评

## iptables 的其他方面 ### trace trace 是 iptables 的核心功能之一,它允许我们跟踪iptables规则是如何执行的,包括规则是如何匹配的、规则是如何执行的等等。 在 /etc/rsyslog.conf 中,我们可以使用 trace 来记录 iptables 的规则匹配过程,例如: ``` trace rule 10 ``` 这会记录 iptables 对 rule 10 的规则匹配过程,包括 rule 的匹配规则、规则的执行方式等等。 trace 可以使用不同的级别记录规则匹配过程,例如: * trace level 1:记录所有规则匹配过程 * trace level 2:记录规则匹配过程,但不记录规则执行过程 * trace level 3:记录规则匹配过程,但不记录规则执行过程,也不记录规则匹配过程 ### ulogd ulogd 是 iptables 的一个用户空间 logging daemon,它可以用于收集 iptables 的规则匹配过程,并将其写入到 ulogd 的文件中。 ulogd 可以使用不同的级别记录规则匹配过程,例如: * ulogd level 1:记录所有规则匹配过程 * ulogd level 2:记录规则匹配过程,但不记录规则执行过程 * ulogd level 3:记录规则匹配过程,但不记录规则执行过程,也不记录规则匹配过程 ### 其他 iptables 还有其他一些功能,例如: * iptables 可以使用 filter 来对 iptables 的规则进行过滤 * iptables 可以使用 iptable_filter 来对 iptables 的规则进行过滤 * iptables 可以使用 iptables_con 来对 iptables 的规则进行过滤 ###总结 iptables 是一个非常强大的工具,它可以用于收集 iptables 的规则匹配过程,并将其写入到不同的文件中。 除了 trace、ulogd 和其他功能,iptables 还还有其他一些功能,例如:filter、iptables_filter 和iptables_con。

正文

背景

怎么会讲这个话题,这个说来真的长了。但是,长话短说,也是可以的。

我前面的文章提到,线上的服务用了c3p0数据库连接池,会偶发连接泄露问题,而分析到最后,又怀疑是db侧主动关闭连接,或者是服务所在机器和db之间有防火墙,防火墙主动关闭了连接。导致我们这边socket看着还健康,实际在对端已经失效了,然后我们在这个socket发消息过去,对方一直不回复,我方没设置超时时间,导致长时间阻塞在read方法上(等待对方响应)。

然后,为了模拟这种场景,我是想了一些办法,我搜到的传统一点的办法是,我们是oracle,支持sleep命令,执行后,数据库就像死了一样,这样应该可以模拟出read timeout。但是吧,开发环境就那一个数据库,很多人用,我去sleep它,不太好,我自己搭吧,有点懒,而且,感觉这个方法不够通用,换个别的数据库超时,或者是什么服务超时,岂不是又要另寻他法?

个人偏爱通用的东西,比如这种超时模拟的场景,不只是可以模拟db,也可以模拟其他远端服务。

一开始,我想的办法是nginx四层代理(使用stream模块),比如客户端连接nginx,然后又nginx转发流量到后面的db啥的,然后当后端db返回响应时,我在nginx上sleep个n秒再返回,就能达成客户端超时的场景,但是,经过我后面多方研究,找到了一个在nginx或者openresty上支持sleep的模块(https://github.com/openresty/stream-echo-nginx-module),但是,这个模块明确写了,不能和proxy模块一起用:

  • The commands of this module cannot be mixed with other response-generating modules like the standard ngx_stream_proxy module in the same server {} block, for obvious reasons.

另外,它也说了,它虽然sleep,但是不会阻塞nginx的eventloop线程。

image-20230722164116965

再后来,也试过stream-lua-nginx-module模块,打算用lua代码来sleep,发现依然不行,应该怎么说呢,我的理解是,nginx本身就是一个非阻塞的模型,所以,想要靠sleep这些去阻塞它,反而做不到。

所以,后面我放弃了这条路。

再后来,我是偶然间来了灵感,认为可以靠机器上的防火墙,将db返回的数据包给丢掉,这样,就可以模拟出读超时的场景了,事实上,这条路是正确的,但是,主要是网上资料比较少,所以也踩了好多坑才折腾出来。

最近就打算好好介绍下这个防火墙,但是,我发现想要比较系统地介绍它,还是比较困难,我自己又现学了好一阵,每次想写的时候,发现还是有太多不了解的,于是搁笔。

今天是打算先介绍下iptables的nat功能,结果实验的时候,死活有问题,然后想着怎么打日志,结果打日志又出问题,折腾了好久(搜索引擎上关于这个的资料较少),这里就记录下来。

iptables简单介绍

在centos 6时代,我记得机器上默认的防火墙就是iptables;centos 7,基本换成了firewalld。我这次最先折腾的是firewalld,后来一直没成功,后来换到iptables结果就好了,因此最近都是在折腾iptables,这里就先介绍iptables,后续再去研究firewalld。

安装、启动

一般来说,先停止firewalld,避免互相影响。

systemctl status firewalld
systemctl stop firewalld

安装iptables并启动:

yum install -y iptables-services
// 启动
systemctl start iptables
// 查看状态
systemctl status iptables
如果提示绿色的“active (exited)”,则iptables已经启动成功。
// 停止
systemctl stop iptables

默认规则

安装完成后,或者启动后,执行如下命令,会发现有一些默认规则。

image-20230722172302338

所谓规则,在防火墙中,就是:针对某个满足条件1、条件2、条件n的包,需要采取什么动作,而我们可能有很多这样的规则,形成一个或多个list。

而上面看到的默认规则,存放在文件 /etc/sysconfig/iptables

vim /etc/sysconfig/iptables

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
// 1 state如果为ESTABLISHED,可以粗略理解为tcp中三次握手后的数据包,对这种包,放行。当然,防火墙不止处理tcp,所以这个概念还是略有差异
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
// 2 对于ping包,放行
-A INPUT -p icmp -j ACCEPT
// 3 本机的loopback网卡的包,放行
-A INPUT -i lo -j ACCEPT
// 4 对于本机的ssh连接请求包,放行
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
// 5 其他的包,都拒绝
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

使用trace定位规则问题

下面的一些iptables的东西,我们还没介绍,所以可能导致看不太懂,可以简单看下,我们本篇就是介绍,如何保证debug的日志能看到。

一般来说,对于开发同学来说,场景也就是比较简单,如禁止/允许访问某端口,或者只禁止允许某些ip等,比如,假设我们希望放行8080端口,要怎么设置呢?

我们得设想一下,客户端访问8080端口的话,传过来的packet一般长啥样,是不是tcp协议这一层的目标端口是8080呢,对吧?那我们只要设置一个规则,将这种包筛选出来,放行即可。

我先随便启动一个8080的监听:

python -m SimpleHTTPServer 8080

然后我们新建一个rule,准备放行8080端口:

iptables -i filter -I INPUT 1 -p tcp -m tcp --dport=8080 -j ACCEPT

然后本机执行telnet,结果发现连不上。。。

为啥呢,因为我命令写错了,假设我不知道我命令写错了,感觉没匹配上我的这个规则呢,那我怎么知道,iptables到底最终匹配到哪个rule了呢,会不会还没到这个rule就出问题了呢?

不要着急,我们加上下面这个命令:

iptables -t raw -I PREROUTING -p tcp -m tcp --dport 8080 -j TRACE

这个命令,分段来看:

-t raw : 指定要操作的table,其实就是传说中的三表五链中的三表之一,table这里可以先不管
-I PREROUTING: -I表示插入规则,后面指定要插入的链(链就是规则的集合)
上面两个选项,暂时不理解可以先不管,其实就是指定了一个时机,这个时机,发生在防火墙处理网络报文的最前面。
此时,我们指定一个rule:
-p tcp -m tcp --dport 8080 -j TRACE
即,对于目标端口为8080的报文,-j TRACE ,表示进行跟踪,这样的话,防火墙会在日志打印出最终匹配上了哪些rule

增加了上述命令后,我们重试一次telnet,然后打开/var/log/messages日志:

image-20230722175600395

能看到对于某一条客户端报文(这里其实就是客户端发过来的第一次tcp握手,注意看,第二行的ID都是35701,所以都是针对同一条报文的日志),一共打印了三行:

raw:PREROUTING:policy:2
nat:PREROUTING:policy:1
filter:INPUT:rule:6

这里其实就是表示,一个顺序经过了三个规则。

首先是raw:PREROUTING,即raw表的PREROUTING链的默认规则(policy:2),看下图,raw表只有我们那个trace规则,除此之外,啥都没有,所以匹配了PREROUTING链的默认策略:

image-20230722180021766

按照网上的说法,下图红框处就表示默认策略:

image-20230722181715290

他么的,为啥说是网上的说法呢,因为,我本来想实验一下,把这块改成DROP再看看效果:

 // 不要执行!!!这个命令就是设置默认策略为drop
 iptables -P PREROUTING DROP -t raw 

结果,他么的,马上shell就断了,然后这是工作服务器,我都以为必须找运维才能解决了,后来好歹想起来有个平台,可以重启服务器,重启后,iptables我是没设置开机启动的,这条rule也是没有持久化的,所以我才能连上shell,在这里继续做实验。

扯远了,执行完第一条链的默认策略ACCEPT后,进入表nat的PREROUTING链:

[root@hx168-access ~]# iptables -nvL -t nat
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination    

这个也是空的,没有rule,所以最终是执行了PREROUTING:policy:1默认策略:ACCEPT,然后进入到下面的:

filter:INPUT:rule:6。

这就是filter表的INPUT规则链的第六条规则:

image-20230722182526725

第六条规则,就是简单粗暴地拒绝,为啥会执行到第六条呢,这个是按1-6顺序执行下来的,说明前面的5条,都没匹配上条件,那肯定就是条件估计写得有问题。

经过我认真检查,发现自己手写还是容易出错,正确的应该是下面这样:

// 正确版本
iptables -I INPUT  -p tcp --dport 8080 -j ACCEPT
// 错误版本
iptables  -i filter -I INPUT 1 -p tcp -m tcp --dport=8080 -j ACCEPT

错误的地方就是 -i filter,我本来是准备显式指定下要操作的表(其实正确的版本里就没指定,默认就是filter),结果语法写错了,应该是-t filter,重新执行下,就好了:

image-20230722183047050

可以看到,虽然错误的规则还存在,但是我们把正确的规则放到了num=1,它会先执行,它匹配上了,后面就不会执行了。

我们重新看trace日志,这次就是匹配上rule1了:

image-20230722183252336

trace规则如何设置才能正确输出

rsyslog进程

今天其实花了很多时间在trace规则上,我在网上查到有这种规则,但是,加了后,死活在log日志里看不到内容,查了半天,到处改改改也没搞出来。

这个/var/log/messages文件,一般是什么服务在往里面写呢?是rsyslogd,它是一个后台进程,

简单一句话的介绍是,它是一个支持将消息记录到日志的系统工具,消息可以通过网络或者本机unix socket的方式发送给它。

Rsyslogd is a system utility providing support for message logging. Support of both internet and unix domain sockets enables this utility to support both local and remote logging.

一般来说,都是通过本机unix socket方式。

根据man rsyslogd的说法,这个进程主要涉及如下几个重要文件:

主配置文件:
/etc/rsyslog.conf
Configuration file for rsyslogd.  See rsyslog.conf(5) for exact information.

unix socket的位置,本进程就从这里获取要写日志的消息
/dev/log
The Unix domain socket to from where local syslog messages are read.

进程pid:
/var/run/syslogd.pid
The file containing the process id of rsyslogd.

进程id这个是可以看到,是匹配的:4773:

[root@hx168-access ~]# cat /var/run/syslogd.pid
4773
[root@hx168-access ~]# ps -ef|grep rsys
root       4773      1  0 18:44 ?        00:00:00 /usr/sbin/rsyslogd -n

这个进程,主要会写如下文件(默认配置情况下):

image-20230722192650305

比如我们上面的防火墙跟踪日志就在/var/log/messages

日志输出遇阻

今天我遇到的问题就是,看到网上说,要改配置,将内核日志给输出才行:

vim /etc/rsyslog.conf
增加如下一行:
kern.*     /var/log/iptables.log(这里文件名可以自取)

结果弄了后还是没效果。

后面我换了台机器,结果发现可以输出,而且也没像上面这样配置,我后面就在想,到底是哪里的问题。

strace排查日志daemon是否收到日志

 # xxx为rsyslogd的pid
 strace -p xxx -q -f -s 200

strace命令可以attach到一个进程上,监控其系统调用。

我当时两台机器,一台可以,一台不行,于是分别在两台机器上开启该命令,然后执行ping,发现正常的机器上,是可以看到有如下这些系统调用的,而异常的机器就没有:

image-20230722230348064

所以,我开始怀疑是不是防火墙本身的问题,并没把日志写到rsyslogd。

比较两台机器加载的mod

man iptables-extensions文档,讲解了TRACE这个target操作的信息。

TRACE
This target marks packets so that the kernel will log every rule which match the packets as those traverse the tables, chains, rules.

A logging backend, such as nf_log_ipv4(6) or nfnetlink_log, must be loaded for this to be visible. The packets are logged with the string prefix: "TRACE:
tablename:chainname:type:rulenum " where type can be "rule" for plain rule, "return" for implicit rule at the end of a user defined chain and "policy" for
the policy of the built in chains.
It can only be used in the raw table.

这里面提到:

A logging backend, such as nf_log_ipv4(6) or nfnetlink_log, must be loaded for this to be visible.

必须先在机器上加载nf_log_ipv4或者nfnetlink_log这两个模块之一,其他细节就没有了。

我在网上当时也查了不少文章,就是看看大家是怎么配置这个TRACE规则的,然后跟着做了些操作,比如,加载模块的命令是modprobe,我搜索shell history,发现执行了如下这么多次:

  678  modprobe ipt_LOG
  688  modprobe ipt_LOG ip6t_LOG nfnetlink_log
  741  modprobe nfnetlink_log
  748  modprobe nf_log_ipv4

模块相关的命令有这几个:

加载或删除模块
modprobe - Add and remove modules from the Linux Kernel

显示已加载的模块
lsmod - Show the status of modules in the Linux Kernel
lsmod is a trivial program which nicely formats the contents of the /proc/modules, showing what kernel modules are currently loaded.

查看模块信息
modinfo - Show information about a Linux Kernel module

我分别在两台机器执行了lsmod | sort,然后比较差异,确实有些差异:

image-20230722231603033

然后在网上看到一些文章,说和内核参数有关(sysctl -a),于是也比较了一下,发现其中一个参数可能有影响:

image-20230722231715861

这个参数我在网上查了好久,甚至没查到枚举值有哪些,总的来说,信息非常少。

后来大概知道,如下这个参数值,会把log不是发往rsyslogd,而是发往一个叫ulogd的后台程序。

net.netfilter.nf_log.2 = nfnetlink_log

这个ulogd大概介绍下,https://www.netfilter.org/projects/ulogd/:

ulogd is a userspace logging daemon for netfilter/iptables related logging.

它是一个用户态的日志后台,供netfilter/iptables相关的日志使用。不管怎么说,反正不会发往rsyslog这边就对了。

这个为啥会变成这样呢,大概是因为我执行了modprobe nfnetlink_log吧。正确的模块其实应该是nf_log_ipv4.

所以我这边修改了下值:

echo nf_log_ipv4 >  /proc/sys/net/netfilter/nf_log/2
然后再查看:
sysctl -a
发现就已经变成nf_log_ipv4了

这里额外补充下,在如下目录下,有12个文件:

[root@hx168-access ~]# ll /proc/sys/net/netfilter/nf_log
-rw-r--r-- 1 root root 0 Jul 22 23:25 2
...
-rw-r--r-- 1 root root 0 Jul 22 23:25 12

其中,2就是表示IP协议族,表示遇到IP协议族时,日志打印使用的模块,其他的协议族都不用特别关心。

网上关于这个问题的讨论

首先是一个feature,貌似是这块net.netfilter.nf_log.2可以取的值太多了,要合并掉几个:

https://patchwork.kernel.org/project/netdevbpf/patch/20210406122133.1644-2-pablo@netfilter.org/

image-20230722233203106

就是以后就两个模块,一个nfnetlink_log,一个nf_log_syslog(这个一看就是写到rsyslog的)。

再下来,是一个bug提交,和我的问题一模一样,最后解决办法也一样,是吐槽trace的文档写得太草了:

https://bugzilla.netfilter.org/show_bug.cgi?id=1076

image-20230722233439568

问题是几年前提的,看起来还是没人改,bug也没关闭。

就这样吧,下篇再继续讲这个iptables的其他方面。

与iptables防火墙调试,想打印个日志就这么难相似的内容:

iptables防火墙调试,想打印个日志就这么难

# 背景 怎么会讲这个话题,这个说来真的长了。但是,长话短说,也是可以的。 我前面的文章提到,线上的服务用了c3p0数据库连接池,会偶发连接泄露问题,而分析到最后,又怀疑是db侧主动关闭连接,或者是服务所在机器和db之间有防火墙,防火墙主动关闭了连接。导致我们这边socket看着还健康,实际在对端已

如何使用iptables防火墙模拟远程服务超时

# 前言 超时,应该是程序员很不爱处理的一种状态。当我们调用某服务、某个中间件、db时,希望对方能快速回复,正确就正常,错误就错误,而不是一直不回复。目前在后端领域来说,如java领域,调用服务时以同步阻塞调用为主,此时一般会阻塞当前线程,等待结果。如果我们设置了超时时间还好,一段时间等不到就报错了

[转帖]12.计算机网络---iptables防火墙管理工具

文章目录 一.防火墙基础知识1.1 防火墙是什么?1.2 iptables基础知识1.3 netfilter和iptables的关系:1.4 新型防火墙工具:firewalld 二.iptables的四表五链2.1 规则表2.2 规则链2.3 规则表和规则链之间的关系2.4 数据报的过滤匹配流程:2

[转帖]安全技术和iptables防火墙

目录 安全技术Netfilter防火墙工具介绍iptablesfirewalldnftables iptables的组成概述netfilter与iptables关系 iptables的四表五链结构介绍iptables安装iptables的命令格式数据包的常见控制类型iptables 命令常用管理选项

【转帖】【奇技淫巧】Linux | 安全保障防火墙-iptables

虽然说Linux在安全方面确实相当于windows要更加可靠一些,但一般使用其作为服务器的我们,也不能大意,也是需要严格限制网络传输过程中的出入规则。上篇文章我们有聊到统计网络的信息,这篇文章来学习一下比较著名的防火墙iptables,它已经有十几年的历史了,算是不折不扣的Linux系统的功臣。 一

[转帖]Linux 防火墙开放特定端口 (iptables)

查看状态: iptables -L -n 下面添加对特定端口开放的方法: 使用iptables开放如下端口 /sbin/iptables -I INPUT -p tcp --dport 8000 -j ACCEPT 保存 /etc/rc.d/init.d/iptables save 重启服务 ser

[转帖]Linux 防火墙开放特定端口 (iptables)

查看状态: iptables -L -n 下面添加对特定端口开放的方法: 使用iptables开放如下端口 /sbin/iptables -I INPUT -p tcp --dport 8000 -j ACCEPT 保存 /etc/rc.d/init.d/iptables save 重启服务 ser

[转帖]iptables命令详解和举例(完整版)

1、防火墙概述 防火墙,其实说白了讲,就是用于实现Linux下访问控制的功能的,它分为硬件的或者软件的防火墙两种。无论是在哪个网络中,防火墙工作的地方一定是在网络的边缘。而我们的任务就是需要去定义到底防火墙如何工作,这就是防火墙的策略,规则,以达到让它对出入网络的IP、数据进行检测。 目前市面上比较

[转帖]iptables 执行清除命令 iptables -F 要非常小心的

使用 /sbin/iptables -F 要小心,搞不好,你就马上同服务器断开连接了 以下是来自 http://wiki.ubuntu.org.cn/IptablesHowTo 上的说明 可以通过/sbin/iptables -F清除所有规则来暂时停止防火墙: (警告:这只适合在没有配置防火墙的环境

[转帖]深入理解 netfilter 和 iptables

Netfilter (配合 iptables)使得用户空间应用程序可以注册内核网络栈在处理数据包时应用的处理规则,实现高效的网络转发和过滤。很多常见的主机防火墙程序以及 Kubernetes 的 Service 转发都是通过 iptables 来实现的。 关于 netfilter 的介绍文章大部分只