背景

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

我前面的文章提到,线上的服务用了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. 1分钟看懂log4j 配置自己想要的日志信息

    在开发的时候我们会希望 只将  sql信息的日志,已经自定义输出的日志进行打印 ,而一些框架级的日志不需要输出 如下    首先 rootLogger 设置日志级别    log4j.rootLogg ...

  2. [调试日志]用php函数var_export把多维数组file_put_contents写入并打印到日志,以方便调试之多维数组,用php5中的var_export函数示例,顺带介绍http_build_query(转)

    一行解决写入日志: file_put_contents("/tmp/jack.txt", var_export($layReturnArr,TRUE),FILE_APPEND); ...

  3. linux 的iptables防火墙

    .a文件就是*.o文件的集合, 是object文件的归档文件, 所以, 用nm -A  ???.a看到的 symbolic符合名称都是 相应的,  包含的  .o文件.... linux 2.4内核中 ...

  4. Netfilter/iptables防火墙

    http://os.51cto.com/art/201107/273443.htm [51CTO独家特稿]Linux系统管理员们都接触过Netfilter/iptables,这是Linux系统自带的免 ...

  5. squid透明代理+iptables防火墙,多网卡详细配置

    squid透明代理+iptables防火墙详细配置 看到很多人都在不停地问iptables+squid做透明代理的问题,鄙人不才,斗胆在此做统一解答.如有任何纰漏还望各位批评指教. ========= ...

  6. 在Ubuntu Server是配置iptables防火墙

    iptables 是一个安装在Ubuntu Server上的默认防火墙.在正常的ubuntu安装过程中,iptables是被安装上了的,但是它默认允许所有的流量(不管防火墙是否是无效的) 关于ipta ...

  7. 2-10~2-11 配置iptables防火墙增强服务 selinux简单讲解

    学习一个服务的过程: 1.此服务器的概述:名字,功能,特点,端口号 2.安装 3.配置文件的位置 4.服务启动关闭脚本,查看端口 5.此服务的使用方法 6.修改配置文件,实战举例 7.排错(从下到上, ...

  8. 9.Linux之iptables防火墙

    Linux之iptables防火墙 目录 Linux之iptables防火墙 iptables防火墙概述 netfilter和iptables之间的关系 netfilter iptables ipta ...

  9. 编译内核实现iptables防火墙layer7应用层过滤 (三)

    在前面的两篇文章中我们主要讲解了Linux防火墙iptables的原理及配置规则,想博友们也都知道iptables防火墙是工作在网络层,针对TCP/IP数据包实施过滤和限制,属于典型的包过滤防火墙.以 ...

  10. 20条IPTables防火墙规则用法!

    导读 管理网络流量是系统管理员必需处理的最棘手工作之一,我们必需规定连接系统的用户满足防火墙的传入和传出要求,以最大限度保证系统免受攻击.很多用户把 Linux 中的 IPTables 当成一个防火墙 ...

随机推荐

  1. odoo wizard界面显示带复选框列表及勾选数据获取

    实践环境 Odoo 14.0-20221212 (Community Edition) 需求描述 如下图(非实际项目界面截图,仅用于介绍本文主题),打开记录详情页(form视图),点击某个按钮(图中的 ...

  2. 深度学习-05(tensorflow模型保存与加载、文件读取、图像分类:手写体识别、服饰识别)

    文章目录 深度学习-05 模型保存于加载 什么是模型保存与加载 模型保存于加载API 案例1:模型保存/加载 读取数据 文件读取机制 文件读取API 案例2:CSV文件读取 图片文件读取API 案例3 ...

  3. Prism Sample 8 ViewModelLocator

    这一例只是说明ViewModelLocator的使用,没有难度,跳过.

  4. 2020-10-13:hash与B+tree的区别?

    福哥答案2020-10-13: [答案来自知乎:](https://www.zhihu.com/question/425378511/answer/1522000015) 这里我从数据库索引的层面回答 ...

  5. 2021-06-23:给定一个数组arr,代表每个人的能力值。再给定一个非负数k,如果两个人能力差值正好为k,那么可以凑在一起比赛。一局比赛只有两个人,返回最多可以同时有多少场比赛。

    2021-06-23:给定一个数组arr,代表每个人的能力值.再给定一个非负数k,如果两个人能力差值正好为k,那么可以凑在一起比赛.一局比赛只有两个人,返回最多可以同时有多少场比赛. 福大大 答案20 ...

  6. Redis基础命令汇总,看这篇就够了

    本文首发于公众号:Hunter后端 原文链:Redis基础命令汇总,看这篇就够了 本篇笔记将汇总 Redis 基础命令,包括几个常用的通用命令,和各个类型的数据的操作,包括字符串.哈希.列表.集合.有 ...

  7. vue全家桶进阶之路28:项目仓库Gitee

    Gitee(之前称为GitCafe)是一种基于web的Git仓库托管服务,在中国很受欢迎.它为开发人员提供了一个平台,可以托管他们的Git仓库,与其他开发人员协作,并管理他们的代码. https:// ...

  8. vue全家桶进阶之路27:Vue.js 3.0的下载和安装

    使用脚手架vue-cli创建vue3项目,创建前需要准备以下: 1.node.js环境 见:https://www.cnblogs.com/beichengshiqiao/p/17251233.htm ...

  9. 防抖节流utils

    /** * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数 * * @param {Function} func 要执行的回调函数 * @param {Number} wait ...

  10. Ubuntu 18.04 (Bionic) 简单快速的安装mongodb

    按步骤走,不带脑子式安装(注意4.0版本mongodb官方已经不再支持,以下代码中可以修改mongodb版本号安装,目前最新版为6.0,如果懒得改直接用也可以,文章后边第三章第一条代码会直接升级为最新 ...