iptables的文章多如牛毛,但是,我读了一些,发现虽然成体系,但是不便理解,今天就结合自己的理解,好好讲解下,另外,我们也会使用iptables来实验一个nat地址转换的demo,nat转换,通俗地讲,一般是为了解决ipv4公网地址不够用的问题,因此在学校、公司等机构的有公网ip的服务器上,部署nat软件进行地址转换,如内网机器访问互联网时,将源地址转换为服务器的公网ip;在收到响应时,此时目的地址是公网ip,此时需要修改为内网机器的地址。
这个东西,教科书里学了很多年了,没想到还能走进现实,也是有点意思。
iptables运行在用户态,netfilter运行在linux内核中。iptables相当于一套界面,可以将防火墙规则写入内核中的一片内存区域,而netfilter会去读取这片内存区域中的规则,根据规则进行对应的网络包处理,如丢弃、放行等。
正因为如此,我们需要手动安装iptables,如yum install -y iptables-services
,但是,netfilter是linux内核本身自带的,无需安装。
简单来说,netfilter才是核心,你要觉得iptables不好用,可以换其他的命令行、图形界面工具,甚至自己写也可以。当然,netfilter和iptables都是同一个团队弄的,因为它不可能让大家直接写代码去操作netfilter,肯定还是得搞个好用的命令行工具给大家用吧,所以有了iptables。
而且,现在团队觉得iptables还是不够好,目前新搞了个nftables来取代iptables。
项目官网:https://www.netfilter.org/index.html
大概有这么几种网络包:
如果让你来设计,你应该也会引入几个时间点,在这个时间点,主动去查询用户定义的规则,看看规则中是要丢弃还是放行数据包。
时间点的考虑,也很重要。
比如,针对网卡收到的数据包的处理,第一个时间点,应该是在检查路由表之前(即检查目的地址是否是本机之前),此时,给用户提供一个介入的时机,用户甚至可以修改这个数据包,比如,本来是发给当前机器的,我可以将目的地址改为其他机器,这就是DNAT(nat分为修改源地址和目的地址,这里说的这种,就是修改目的地址)适用的场景;
第二个时间点,应该是在路由决定做出之后,在交给进程处理之前;
第三个时间点,应该是进程处理完成后,假如需要回应,即进程将数据包交给我们后,此时也可以进行处理
另外,针对那种路由转发的数据,在转发出去之前,应该也可以进行一些处理。
这些时间点呢,一般就是扩展点,就像spring里面一样,bean的创建过程中有很多时间点会回调用户的函数,用户就可以注册自己的回调函数,进行一些特殊的逻辑处理。
Netfifilter一共在内核的网络协议栈中,插入了5个扩展点(官网叫hook)。我参考了如下文章:
https://www.usenix.org/system/files/login/articles/892-neira.pdf
PREROUTING:
All the packets, with no exceptions, hit this hook, which is reached before the routing decision。
Port Address Translation (NAPT) and Redirections, that is, Destination Network Translation (DNAT), are implemented in this hook.
这个时间点,就是所有的网络包在做出路由决定前,就会调用本hook。一般来说,端口转换和重定向、DNAT(目标地址转换)就是在这里实现
LOCAL INPUT:
All the packets going to the local machine reach this hook. This is the last hook in the incoming path for the local machine traffific.
在检查目的地址后,发现包是发给本机的,那么这些包在交给进程之前,就会调用本hook。
FORWARD:
Packets not going to the local machine (e.g., packets going through the fifirewall) reach this hook.
所有不是发往本机的包,即本机充当路由转发功能时(linux主机实现为路由器或防火墙等),会调用本hook
LOCAL OUTPUT
This is the fifirst hook in the outgoing packet path. Packets leaving the local machine always hit this hook.
本机对外部请求的回复包或者对外发起的请求包,就会触发这个hook
POSTROUTING
This hook is implemented after the routing decision. Source Network Address Translation (SNAT) is registered to this hook. All the packets that leave the local machine reach this hook.
在做出路由决定后,调用本hook。源地址转换,基本就在这个hook阶段完成。所有要离开本机的数据包,就会触发本hook。
具体可以看下图:
按照这篇文章的提法,主要有下面几种网络包流向:
Therefore we can model three kind of traffific flflows, depending on the
destination:
■ Traffific going through the fifirewall, in other words, traffific not going to
the local machine. Such traffific follows the path: PREROUTING 、
FORWARD、 POSTROUTING.
仅仅穿越本机的流量包,即,目的地址不是本机,这样的网络包,会依次经历:
PREROUTING 、FORWARD、POSTROUTING
■ Incoming traffific to the fifirewall, for example, traffific for the local
machine. Such traffific follows the path: PREROUTING INPUT.
发给本机的流量包,路径为:
PREROUTING 、LOCAL INPUT
■ Outgoing traffific from the fifirewall: OUTPUT POSTROUTING.
本机生成的,要出去的流量包(别管是回复别人的那种还是自己发起的,对机器来说都一样,都是要出去的),经历:
OUTPUT 、POSTROUTING
几个时间点说清楚了,那么,这些时间点,回调我们的时候,是查询我们设置的防火墙规则对吧,那么,规则大概长啥样呢?
iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
比如,上面这就是一个规则,其中,-p tcp --dport 8080
就是指定要筛选出哪些数据包来处理,如目标端口8080的,那么,怎么处理呢,这个-j ACCEPT
就表示放行。
另外,我们也不可能就一个规则吧,这个规则处理8080的,那我还有其他处理8081端口的规则,所以,最终是一堆规则,一个规则列表。
此时,我们就可以往每个hook点,注册上一个规则集合,即一个规则链,按链中顺序,依次处理。
那么,iptables是这样搞的吗,我们来看看。
此时,可以看下图:
在每个hook点,如左下角的PREROUTING,按理说只需要有一个规则集合就够了,为啥有三个黄色方块(它们唯一指向了一堆PREROUTING阶段的规则集合),也就是说,怎么有三个规则集合呢?
其实,就是因为不同规则大相径庭:
Type of Service
字段然后,官方就大体根据这些规则,进行了分类,把一个大集合,分成了好几个集合,比如nat集合负责nat相关规则、mangle集合负责改报文字段的规则,如Type of Service
、raw集合呢,目前我接触到的有trace、log等规则、像filter呢,就负责对报文检查后进行筛选过滤。
另外呢,像一个报文的生命周期,可能会依次经过:
PREROUTING 、LOCAL INPUT
那么,在这两个阶段,我们都可以执行规则,比如,filter集合里的规则,在这两个hook点都可以执行,所以,filter表中,包含了多个hook点的规则集合;像nat集合里的规则呢,一般就在PREROUTING 、POSTROUTING这两个阶段发挥作用,所以呢,nat表,包含了PREROUTING 、POSTROUTING两个hook点的规则集合,两者互不影响,井水不犯河水。
我个人喜欢上面那张图,下面这张也可以参考:
网上还有一张图,我觉得也很棒(https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg):
下图是只针对转发这种场景,也不错,图都是来源网络,侵删:
比如,上图中,我想操作左下角的prerouting阶段的raw表:
// -t 指定表名,如raw,-I 表示插入,chain表示在哪个链插入,如PREROUTING,rulenum呢,是在PREROUTING链中插入到第几个,后面的rule-spe就是规则定义:筛选包+对包的操作
iptables [-t table] -I chain [rulenum] rule-specification
比如,我想trace一下发往8080端口的包:
iptables -t raw -I PREROUTING -p tcp -m tcp --dport 8080 -j TRACE
其他操作:
// -R,替换,即修改
iptables [-t table] -R chain rulenum rule-specification
// -D 删除
iptables [-t table] -D chain rulenum
// -L 查询,一般组合使用iptables -nvL,默认查看filter表
iptables [-t table] -L, --list [chain]
其他命令参考man iptables
、man iptables-extensions
(包含了各种目标操作的讲解)
看我上面的图,server1没有实际监听8080端口,server2有监听,假设我客户端只能直接访问server1,那么,我想做的是,在server1上靠iptables的dnat,修改目的ip为server2,达成我访问server2的8080服务的目的。
// 没有持久化,临时修改
echo 1 >/proc/sys/net/ipv4/ip_forward
// 查看
[root@hx168-access ~]# sysctl -p
net.ipv4.ip_forward = 1
因为server1没有监听8080端口,那肯定不能自己处理,所以必须转发给server2,所以需要开启这个功能
iptables -t raw -I PREROUTING -p tcp -m tcp --dport 8080 -j TRACE
iptables -t raw -I PREROUTING -p tcp -m tcp --sport 8080 -j TRACE
目标端口为8080的,表示客户端发出来的;源端口为8080的,表示server2回复的。都加上trace
有问题就去看/var/log/messages日志
iptables -t nat -I PREROUTING 1 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 10.80.121.115
即,在网络包刚进来时,因为目标ip是server1,此时如果不进行目标ip修改,就会交给本机处理,所以,我们不能让这事发生,得改成server2.
iptables -t filter -I FORWARD 1 -p tcp -m tcp --dport 8080 -j ACCEPT
iptables -t filter -I FORWARD 1 -p tcp -m tcp --sport 8080 -j ACCEPT
正向的和反向的,各加一个,免得一会还要再加
iptables -t nat -I PREROUTING 1 -p tcp -m tcp --dport 8080 -j SNAT --to-source 10.80.121.114
因为此时发往server2的报文里,源ip还是客户端的ip,如果就这么发过去,server2就会返回报文给客户端,而不是server1,所以得改源ip为server1.
可以看到,完全没问题。
在执行上述命令测试时,我在server1上抓包了:
tcpdump -i any tcp port 8080 -w 114.pcap
然后分析114.pcap时,发现就是如下这样,一个简单的三次握手也不简单:
我一开始有疑问,当server2给server1返回时,目标ip为server1,我们也没有配置什么NAT规则,把目标ip改成客户端的ip,那么,服务器为啥不是把报文交给server1的进程处理,而是原路forward转发呢
这个其实还是因为NAT依赖了netfilter的会话跟踪功能,简单来说,netfilter是有状态的,以tcp举例,tcp连接的建立是因为客户端ip:客户端端口和服务端ip:服务端端口,这个四元组是有来有回的,就是我给你发了消息,你也回我了,此时,netfilter就认为这是一个会话。
所以,在server2给server1返回时,server1拿着server2和server1之间的四元组去查,查到有会话,因此,就按照之前的路径,原路回来。
另外,nat这个table里的链,只在检测到之前不存在会话时,才会进,后续就不会再进了;也就是只有首次报文的时候进nat的链
这个可以参考:
https://serverfault.com/questions/741104/iptables-redirect-works-only-for-first-packet/741108#741108
像上面这个案例,只要执行如下命令,关闭会话跟踪,就执行不成功了:
iptables -t raw -I PREROUTING -p tcp -m tcp -dport 8080 -j NOTRACK
[root@hx168-access ~]# cat /proc/net/nf_conntrack
ipv4 2 tcp 6 118 TIME_WAIT src=10.0.240.103 dst=10.80.121.114 sport=8922 dport=8080 src=10.80.121.115 dst=10.80.121.114 sport=8080 dport=8922 [ASSURED] mark=0 zone=0 use=2
yum install conntrack-tools
[root@hx168-access ~]# conntrack -L
tcp 6 118 TIME_WAIT src=10.0.240.103 dst=10.80.121.114 sport=9072 dport=8080 src=10.80.121.115 dst=10.80.121.114 sport=8080 dport=9072 [ASSURED] mark=0 use=1
https://www.jianshu.com/p/0fe6aeec8d79,图不错
https://www.linux.org/docs/man8/iptables-extensions.html
https://gist.github.com/tomasinouk/eec152019311b09905cd 关于nat
https://www.netfilter.org/documentation/
https://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html#ss3.1
https://www.usenix.org/system/files/login/articles/892-neira.pdf