iptables防火墙调试,想打印个日志就这么难
背景
怎么会讲这个话题,这个说来真的长了。但是,长话短说,也是可以的。
我前面的文章提到,线上的服务用了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线程。

再后来,也试过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
默认规则
安装完成后,或者启动后,执行如下命令,会发现有一些默认规则。

所谓规则,在防火墙中,就是:针对某个满足条件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日志:

能看到对于某一条客户端报文(这里其实就是客户端发过来的第一次tcp握手,注意看,第二行的ID都是35701,所以都是针对同一条报文的日志),一共打印了三行:
raw:PREROUTING:policy:2
nat:PREROUTING:policy:1
filter:INPUT:rule:6
这里其实就是表示,一个顺序经过了三个规则。
首先是raw:PREROUTING,即raw表的PREROUTING链的默认规则(policy:2),看下图,raw表只有我们那个trace规则,除此之外,啥都没有,所以匹配了PREROUTING链的默认策略:

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

他么的,为啥说是网上的说法呢,因为,我本来想实验一下,把这块改成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规则链的第六条规则:

第六条规则,就是简单粗暴地拒绝,为啥会执行到第六条呢,这个是按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,重新执行下,就好了:

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

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
这个进程,主要会写如下文件(默认配置情况下):

比如我们上面的防火墙跟踪日志就在/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,发现正常的机器上,是可以看到有如下这些系统调用的,而异常的机器就没有:

所以,我开始怀疑是不是防火墙本身的问题,并没把日志写到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,然后比较差异,确实有些差异:

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

这个参数我在网上查了好久,甚至没查到枚举值有哪些,总的来说,信息非常少。
后来大概知道,如下这个参数值,会把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/

就是以后就两个模块,一个nfnetlink_log,一个nf_log_syslog(这个一看就是写到rsyslog的)。
再下来,是一个bug提交,和我的问题一模一样,最后解决办法也一样,是吐槽trace的文档写得太草了:
https://bugzilla.netfilter.org/show_bug.cgi?id=1076

问题是几年前提的,看起来还是没人改,bug也没关闭。
就这样吧,下篇再继续讲这个iptables的其他方面。
iptables防火墙调试,想打印个日志就这么难的更多相关文章
- 1分钟看懂log4j 配置自己想要的日志信息
在开发的时候我们会希望 只将 sql信息的日志,已经自定义输出的日志进行打印 ,而一些框架级的日志不需要输出 如下 首先 rootLogger 设置日志级别 log4j.rootLogg ...
- [调试日志]用php函数var_export把多维数组file_put_contents写入并打印到日志,以方便调试之多维数组,用php5中的var_export函数示例,顺带介绍http_build_query(转)
一行解决写入日志: file_put_contents("/tmp/jack.txt", var_export($layReturnArr,TRUE),FILE_APPEND); ...
- linux 的iptables防火墙
.a文件就是*.o文件的集合, 是object文件的归档文件, 所以, 用nm -A ???.a看到的 symbolic符合名称都是 相应的, 包含的 .o文件.... linux 2.4内核中 ...
- Netfilter/iptables防火墙
http://os.51cto.com/art/201107/273443.htm [51CTO独家特稿]Linux系统管理员们都接触过Netfilter/iptables,这是Linux系统自带的免 ...
- squid透明代理+iptables防火墙,多网卡详细配置
squid透明代理+iptables防火墙详细配置 看到很多人都在不停地问iptables+squid做透明代理的问题,鄙人不才,斗胆在此做统一解答.如有任何纰漏还望各位批评指教. ========= ...
- 在Ubuntu Server是配置iptables防火墙
iptables 是一个安装在Ubuntu Server上的默认防火墙.在正常的ubuntu安装过程中,iptables是被安装上了的,但是它默认允许所有的流量(不管防火墙是否是无效的) 关于ipta ...
- 2-10~2-11 配置iptables防火墙增强服务 selinux简单讲解
学习一个服务的过程: 1.此服务器的概述:名字,功能,特点,端口号 2.安装 3.配置文件的位置 4.服务启动关闭脚本,查看端口 5.此服务的使用方法 6.修改配置文件,实战举例 7.排错(从下到上, ...
- 9.Linux之iptables防火墙
Linux之iptables防火墙 目录 Linux之iptables防火墙 iptables防火墙概述 netfilter和iptables之间的关系 netfilter iptables ipta ...
- 编译内核实现iptables防火墙layer7应用层过滤 (三)
在前面的两篇文章中我们主要讲解了Linux防火墙iptables的原理及配置规则,想博友们也都知道iptables防火墙是工作在网络层,针对TCP/IP数据包实施过滤和限制,属于典型的包过滤防火墙.以 ...
- 20条IPTables防火墙规则用法!
导读 管理网络流量是系统管理员必需处理的最棘手工作之一,我们必需规定连接系统的用户满足防火墙的传入和传出要求,以最大限度保证系统免受攻击.很多用户把 Linux 中的 IPTables 当成一个防火墙 ...
随机推荐
- P4055 [JSOI2009] 游戏(博弈论 、最大匹配)
P4055 [JSOI2009] 游戏 目录 P4055 [JSOI2009] 游戏 题目传送门 题目大意 : 思路: code 题目传送门 题目大意 : 小AA和小YY玩游戏,在这个游戏中,同一个格 ...
- Vue的生命周期的详解
Vue的生命周期 Vue的生命周期是每个使用Vue框架的前端人员都需要掌握的知识,以此作为记录. Vue的生命周期就是vue实例从创建到销毁的全过程,也就是new Vue() 开始就是vue生 ...
- MySQL WorkBench更换界面成中文的方法
菜单页面更换 文章目录 菜单页面更换 汉化文件的xml文件我放在下面的网盘中了 1.找到MySQL的安装位置, 总结 汉化文件的xml文件我放在下面的网盘中了 1.找到MySQL的安装位置, 具体安装 ...
- Axure 手机页面拖动效果
1.设置好上下固定们,中间放一个动态面板,如下图所示,(刚开始创建是没有图片的) 2.再状态1下再创建一个动态面板 命名为D2,如下图所示 3.在D2的状态1下新建一个宽370(大概手机宽)高1100 ...
- BUG解决-Vscode/Sublime C++ 打印中文乱码问题
#include <iostream> using namespace std; #ifdef _WIN32 #include <windows.h> #endif int m ...
- 2020-10-16:CAS知道么?底层实现? 会引发什么问题?如何解决ABA问题?
福哥答案2020-10-16:#福大大架构师每日一题# 简单回答:cmpxchg原子指令.aba,循环开销大,一个共享变量. [知乎](https://www.zhihu.com/question/4 ...
- 2021-04-16:摆放着n堆石子。现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。求出将n堆石子合并成一堆的最小得分(或最大得分)合
2021-04-16:摆放着n堆石子.现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.求出将n堆石子合并成一堆的最小得分(或最大得分)合 ...
- Spring源码:Bean生命周期(五)
前言 在上一篇文章中,我们深入探讨了 Spring 框架中 Bean 的实例化过程,该过程包括从 Bean 定义中加载当前类.寻找所有实现了 InstantiationAwareBeanPostPro ...
- 使用 @GrpcClient 实现客户端
转载请注明出处: @GrpcClient 注解的作用是将 gRPC 客户端注入到 Spring 容器中,方便在应用程序中使用 gRPC 客户端调用 gRPC 服务提供的函数.使用 @GrpcClien ...
- AcWing 1022. 宠物小精灵之收服
宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事. 一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵. 小智也想收服其中的一些小精灵. 然而,野生的小精灵并不那么容易被收服. ...