Skip to content

Instantly share code, notes, and snippets.

@BruceChen7
Last active February 23, 2024 09:21
Show Gist options
  • Save BruceChen7/6b0b6c36c0445c6234374df7cab4a528 to your computer and use it in GitHub Desktop.
Save BruceChen7/6b0b6c36c0445c6234374df7cab4a528 to your computer and use it in GitHub Desktop.
[#NAT#iptables]#nat#iptables#netfilter

绪论

网络地址转换通常涉及在IP包经过路由器或防火墙的时候,修改其源和/或目的地址

资料来源

netfilter

Linux 内核中有一个数据包过滤框架(packet filter framework),叫做 netfilter,这个框架使得 Linux 机器可以像路由器一样工作。接下来我们将使用一个命令行工具 iptables 来创建复杂的规则,用于修改和过滤 数据包。毫无意外,和 NAT 相关的最重要的规则,都在 nat 这个(iptables)table 里。这个表有三个预置的 chain:PRETOUTING, OUTPUT 和 POSTROUTING

PREROUTING 和 POSTROUTING 是最重要的 chain。如名字所示,PRETOUTING chain 负责处理刚刚到达网络接口的数据包,这时还没有做路由判断,因此还不知道这个包是发往本机(local),还是网络内的其他主机。包经过 PRETOUTING chain 之后,将进行路由判 断。如果目的是本机,那接下来的过程将不涉及NAT;如果目的是网络内的其他机器,那包 将会被转发到那台机器,前提是这台机器配置了允许转发。

私有网络通过NAT连接到互联网

  • 子网内的所有主机(客户端)通过socket将数据包发送到一个特定路由器(通过将路由器地址设置为所有机器的网关来实现,数据会通过以太网 或其他底层协议传输)
  • 路由器将发送方的socket 替换为自己的一个(还未使用的)socket
  • 这个socket收到的数据,路由器将socket地址修改为对应的客户端socket,并转发给它 // 收到数据的响应,这个是反向规则

我们假设客户端的网关设置是正确的,那剩下的就是如何配置路由器了幸运的是,netfilter 框架会对设置的每条一条(出向或入向)规则,自动设置它的反向规则,理解这句话的意思是,比如,我们的期望的结果出规则是,从本地网络发出的、目的是因特网的包,将发送方地址修改为路由器的地址**,反规则是从因特网接收的本地网络包,目的地址修改为本地地址。

因此我们只需要设置一个方向的规则即可。选择哪个方向来设置规则?通常是选择不确定性小的一个方向。 例如,“替换所有从本地网络发出的数据包的地址”比“如果客户端发送过一些东西给服务端 ,那将服务端发送的数据进行某种方式的修改”要简单的多。

如何通过iptables来设置规则

# Connect a LAN to the internet
$> iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
  • iptables - 配置内核的工具
  • -t nat - 指定对名为 nat 的 iptables table 配置 NAT 规则
  • -A POSTROUTING - 追加(A: Append)规则到 iptables 的 POSTROUTING chain
  • -o eth1 - 指定只对从 eth1 发出的数据包做操作(o: output)
  • -j MASQUERADE - 规则匹配成功后的动作是 masquerade (伪装)数据包,例如将源地址修改为路由器地址

另外需要说明的是,除了客户端过来的包,路由器自己的包也会涉及以上处理逻辑,因为它们也经过 POSTROUTING chain(见下面的图)。然而,因为路由器为客户端做 socket (IP+Port) 转换的时候,会从它的未使用端口中挑选,因此它自身的包所使用的 port 与做 NAT 的 port 肯定是不同的。因此,虽然它自身的包也会经过以上规则,但并不会被修改。

缺点

本地计算机可以访问因特网,但反过来,我们看看因特网上的机器访问本地机器会是什么情况

因特网上的机器要和本地机器建立连接的话,它唯一能利用的信息就是用路由器的IP地址加一个端口号。大部分情况下,这个端口都是没有被使用的,因此过来的包会被拒绝。即使运气比较好,这个端口是路由器做NAT的一个端口,包仍然很可能会被拒绝,因为这个端口已经和因特网上的其他主机建立连接了。

对于常规服务,可以静态地将路由器的端口映射到本地服务,例如,将路由器的80端口收到的包转发到本地机器的 HTTP 服务器。

解析iptable

iptables [-t table] command [match pattern] [action]

对于nat -t 的参数value必须是nat, 默认的是 table是filter table

几个重要的命令

$> iptables -t nat -A chain [...]

# list rules:
$> iptables -t nat -L

# remove user-defined chain with index 'myindex':
$> iptables -t nat -D chain myindex

# Remove all rules in chain 'chain':
$> iptables -t nat -F chain

选择匹配模式

# actions to be taken on matched packets
# will be abbreviated by '[...]'.
# Depending on the match pattern the appropriate chain is selected.

# TCP packets from 192.168.1.2:
$> iptables -t nat -A POSTROUTING -p tcp -s 192.168.1.2 [...]

# UDP packets to 192.168.1.2:
$> iptables -t nat -A POSTROUTING -p udp -d 192.168.1.2 [...]

# all packets from 192.168.x.x arriving at eth0:
$> iptables -t nat -A PREROUTING -s 192.168.0.0/16 -i eth0 [...]

# all packets except TCP packets and except packets from 192.168.1.2:
$> iptables -t nat -A PREROUTING -p ! tcp -s ! 192.168.1.2 [...]

# packets leaving at eth1:
$> iptables -t nat -A POSTROUTING -o eth1 [...] // [...]是命令

# TCP packets from 192.168.1.2, port 12345 to 12356
# to 123.123.123.123, Port 22
# (a backslash indicates contination at the next line)
$> iptables -t nat -A POSTROUTING -p tcp -s 192.168.1.2 \
   --sport 12345:12356 -d 123.123.123.123 --dport 22 [...]

匹配后的动作

# In the following the table selection, the command and the match pattern
# will be abbreviated using [...]

# Source-NAT: Change sender to 123.123.123.123
$> iptables [...] -j SNAT --to-source 123.123.123.123

# Mask: Change sender to outgoing network interface
$> iptables [...] -j MASQUERADE

# Destination-NAT: Change receipient to 123.123.123.123, port 22
$> iptables [...] -j DNAT --to-destination 123.123.123.123:22

# Redirect to local port 8080
$> iptables [...] -j REDIRECT --to-ports 8080

SNAT - 修改源IP为固定新IP (静态)

前面的将本地私有网络连接到因特网的例子中,我们已经使用了Source NAT(SNAT )。如名字所暗示,发送方的地址会被静态地修改

在例子中我们选择MASQUERADE的原因在于:对于 SNAT,必须显式指定转换后的 IP。 如果路由器配置的是静态 IP 地址,那 SNAT 是最合适的选择,因为它比 MASQUERADE 更 快,后者对每个包都需要检查指定的输出端口上配置的 IP 地址。因为SNAT只对离开路由器的包有意义,因此它只用在 POSTROUTING chain 中。

# Options for SNAT (abstract of manual page)
--to-source <ipaddr>[-<ipaddr>][:port-port]

MASQUERADE - 修改源 IP为动态新 IP(动态获取网络接口 IP)

和 SNAT 类似,但是对每个包都会动态获取指定输出接口(网卡)的IP,因此如果接口的IP地址发送了变化,MASQUERADE规则不受影响,可以正常工作;而对于 SNAT 就必须重新调整规则。

和 SNAT 一样,MASQUERADE 只对POSTROUTING chain有意义。但和 SNAT 不同, MASQUERADE 不支持更详细的配置项了。

DNAT - 修改目的 IP 如果想修改包的目的 IP 地址,那需要使用 Destination NAT(DNAT)。DNAT 可以用于运行在防火墙后面的服务器

显然,接收端修改必须在做路由决策之前,因此 DNAT 适用于PRETOUTINGOUTPUT (本地生成的包)chain

# Options for DNAT (abstract of manual page)
--to-destination <ipaddr>[-<ipaddr>][:port-port] 

REDIRECT - 将包重定向到本机另一个端口

REDIRECT 是 DNAT 的一个特殊场景。包被重定向到路由器的另一个本地端口,可以实现, 例如透明代理的功能。和DNAT 一样,REDIRECT 适用于PRETOUTING 和 OUTPUT chain

# Options for REDIRECT (abstract of manual page)
--to-ports <port>[-<port>] 
@BruceChen7
Copy link
Author

BruceChen7 commented Oct 17, 2019

NAT chain

image

image

几个hook的点

  • forward: 网络包从一个gateway computer的一个网络接口,到另一个接口
  • input: 在网络包发送到当前的进程
  • output: 网络包离开本地进程
  • postrouting: 准备离开网络接口
  • preroutine

从上图可以看出,如果我们要让某台Linux主机充当路由和负载均衡角色的话,我们显然应该在该主机的nat表的prerouting链中对数据包做DNAT操作。

什么是链和表

防火墙的作用就在于对经过的报文匹配规则,然后执行对应的动作,所以,当报文经过这些关卡的时候,则必须匹配这个关卡上的规则,但是,这个关卡上可能不止有一条规则,而是有很多条规则,当我们把这些规则串到一个链条上的时候,就形成了,所以,我们把每一个"关卡"想象成如下图中的模样 ,这样来说,把他们称为"链"更为合适。
image

我们把具有相同功能的规则的集合叫做"表",所以说,不同功能的规则,我们可以放置在不同的表中进行管理,而iptables已经为我们定义了4种表,每种表对应了不同的功能,而我们定义的规则也都逃脱不了这4种功能的范围

  • filter表:负责过滤功能,防火墙;内核模块:iptables_filter,set policies for the type of traffic allowed into, through and out of the computer

  • nat表:network address translation,网络地址转换功能;内核模块:iptable_nat,used with connection tracking to redirect connections for networking addresss translation。

    • 属于一个流的包只会经过这个表一次。如果第一个包被允许做NAT或Masqueraded,那么余下的包都会自动地被做相同的操作。也就是说,余下的包不会再通过这个表,一个一个的被NAT,而是自动地完成。
    • PREROUTING 链的作用是在包刚刚到达防火墙时改变它的目的地址(DNAT),如果需要的话。OUTPUT链改变本地产生的包的目的地址(DNAT)。POSTROUTING链在包就要离开防火墙之前改变其源地址
  • mangle表:拆解报文,做出修改,并重新封装 的功能;iptable_mangle,特殊用途,比如strip off ip options。

    • 可以改变不同的包及包头的内容,比如TTL,TOS或MARK。 注意MARK并没有真正地改动数据包,它只是在内核空间为包设了一个标记。防火墙内的其他的规则或程序可以使用这种标记对包进行过滤或高级路由
  • raw表:关闭nat表上启用的连接追踪机制;iptable_raw

表的概念意味着它们的规则类似,是一类统称。

并不是所有的表,都可以在某个链中使用

  • raw 表中的规则可以被哪些链使用:PREROUTING,OUTPUT
  • mangle 表中的规则可以被哪些链使用:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING
  • nat 表中的规则可以被哪些链使用:PREROUTING,OUTPUT,POSTROUTING(centos7中还有INPUT,centos6中没有)
  • filter 表中的规则可以被哪些链使用:INPUT,FORWARD,OUTPUT

iptables为我们定义了4张"表",当他们处于同一条"链"时,执行的优先级如下。优先级次序(由高而低):

raw --> mangle --> nat --> filter

匹配规则

基本匹配条件

源地址Source IP,目标地址 Destination IP

上述内容都可以作为基本匹配条件。

扩展匹配条件:

除了上述的条件可以用于匹配,还有很多其他的条件可以用于匹配,这些条件泛称为扩展条件,这些扩展条件其实也是netfilter中的一部分,只是以模块的形式存在,如果想要使用这些条件,则需要依赖对应的扩展模块。

源端口Source Port, 目标端口Destination Port

上述内容都可以作为扩展匹配条件

处理动作

理动作在iptables中被称为target(这样说并不准确,我们暂且这样称呼),动作也可以分为基本动作和扩展动作。

此处列出一些常用的动作,之后的文章会对它们进行详细的示例与总结:

  • ACCEPT:允许数据包通过。

  • DROP:直接丢弃数据包,不给任何回应信息,这时候客户端会感觉自己的请求泥牛入海了,过了超时时间才会有反应。

  • REJECT:拒绝数据包通过,必要时会给数据发送端一个响应的信息,客户端刚请求就会收到拒绝的信息。

  • SNAT:源地址转换,解决内网用户用同一个公网地址上网的问题。

  • MASQUERADE:是SNAT的一种特殊形式,适用于动态的、临时会变的ip上。

  • DNAT:目标地址转换。

  • REDIRECT:在本机做端口映射。

LOG:在/var/log/messages文件中记录日志信息,然后将数据包传递给下一条规则,也就是说除了记录以外不对数据包做任何其他操作,仍然让下一条规则去匹配。

@BruceChen7
Copy link
Author

BruceChen7 commented Jun 2, 2020

常见的命令

iptables -t 表名 -L

查看对应表的所有规则,-t选项指定要操作的表,省略"-t 表名"时,默认表示操作filter表,-L表示列出规则,即查看规则。

iptables -t 表名 -L 链名

查看指定表的指定链中的规则。

iptables -t 表名 -v -L

查看指定表的所有规则,并且显示更详细的信息(更多字段),-v表示verbose,表示详细的,冗长的,当使用-v选项时,会显示出"计数器"的信息,由于上例中使用的选项都是短选项,所以一般简写为iptables -t 表名 -vL

iptables -t 表名 -n -L

表示查看表的所有规则,并且在显示规则时,不对规则中的IP或者端口进行名称反解,-n选项表示不解析IP地址。

iptables --line-numbers -t 表名 -L

查看表的所有规则,并且显示规则的序号

例子

 sudo iptables -vL INPUT

Chain INPUT (policy ACCEPT 193K packets, 53M bytes)
 pkts bytes target     prot opt in     out     source               destination         
 187K 8453K KUBE-SERVICES  all  --  any    any     anywhere             anywhere             ctstate NEW /* kubernetes service portals */
 187K 8453K KUBE-EXTERNAL-SERVICES  all  --  any    any     anywhere             anywhere             ctstate NEW /* kubernetes externally-visible service portals */
  24M 4917M KUBE-FIREWALL  all  --  any    any     anywhere             anywhere  
  • pkts:对应规则匹配到的报文的个数。
  • bytes: 对应匹配到的报文包的大小总和。
  • target:规则对应的target,往往表示规则对应的"动作",即规则匹配成功后需要采取的措施。
  • prot:表示规则对应的协议,是否只针对某些协议应用此规则。
  • opt:表示规则对应的选项。
  • in:表示数据包由哪个接口(网卡)流入,我们可以设置通过哪块网卡流入的报文需要匹配当前规则。
  • out:表示数据包由哪个接口(网卡)流出,我们可以设置通过哪块网卡流出的报文需要匹配当前规则。
  • source:表示规则对应的源头地址,可以是一个IP,也可以是一个网段。
  • destination:表示规则对应的目标地址。可以是一个IP,也可以是一个网段。

@BruceChen7
Copy link
Author

BruceChen7 commented Jul 29, 2020

增删改查操作

追加操作

命令语法:iptables -t 表名 -A 链名 匹配条件 -j 动作
示例:iptables -t filter -A INPUT -s 192.168.1.146 -j DROP

首部添加

在指定表的指定链的首部添加一条规则,-I选型表示在对应链的开头添加规则

命令语法:iptables -t 表名 -I 链名 匹配条件 -j 动作
示例:iptables -t filter -I INPUT -s 192.168.1.146 -j ACCEPT

指定位置添加

命令语法:iptables -t 表名 -I 链名 规则序号 匹配条件 -j 动作
示例:iptables -t filter -I INPUT 5 -s 192.168.1.146 -j REJECT

设置指定表的指定链的默认策略(默认动作),并非添加规则

命令语法:iptables -t 表名 -P 链名 动作
示例:iptables -t filter -P FORWARD ACCEPT

上例表示将filter表中FORWARD链的默认策略设置为ACCEPT

删除规则

命令语法:iptables -t 表名 -D 链名 规则序号
示例:iptables -t filter -D INPUT 3

上述示例表示删除filter表中INPUT链中序号为3的规则。

按照具体的匹配条件与动作删除规则,删除指定表的指定链的指定规则

命令语法:iptables -t 表名 -D 链名 匹配条件 -j 动作
示例:iptables -t filter -D INPUT -s 192.168.1.146 -j DROP

上述示例表示删除filter表中INPUT链中源地址为192.168.1.146并且动作为DROP的规则。

删除指定表的指定链中的所有规则,-F选项表示清空对应链中的规则,执行时需三思

命令语法:iptables -t 表名 -F 链名
示例:iptables -t filter -F INPUT

删除指定表中的所有规则,执行时需三思

命令语法:iptables -t 表名 -F
示例:iptables -t filter -F

修改操作

如果如使用-R选项修改规则中的动作,那么必须指明原规则中的原匹配条件,例如源IP,目标IP等。
修改指定表中指定链的指定规则,-R选项表示修改对应链中的规则,使用-R选项时要同时指定对应的链以及规则对应的序号,并且规则中原本的匹配条件不可省略。

命令语法:iptables -t 表名 -R 链名 规则序号 规则原本的匹配条件 -j 动作
示例:iptables -t filter -R INPUT 3 -s 192.168.1.146 -j ACCEPT

保存规则

保存规则命令如下,表示将iptables规则保存至/etc/sysconfig/iptables文件中

service iptables save

@BruceChen7
Copy link
Author

BruceChen7 commented Jul 30, 2020

自定义链的出现

想象一下,如果INPUT链中存放了200条规则,这200条规则有针对httpd服务的,有针对sshd服务的,有针对私网IP的,有针对公网IP的,假如,我们突然想要修改针对httpd服务的相关规则,难道我们还要从头看一遍这200条规则,找出哪些规则是针对httpd的吗?这显然不合理。

所以,iptables中,可以自定义链,通过自定义链即可解决上述问题。

操作

创建自定义链

#示例:在filter表中创建IN_WEB自定义链
iptables -t filter -N IN_WEB

引用自定义链

#示例:在INPUT链中引用刚才创建的自定义链
iptables -t filter -I INPUT -p tcp --dport 80 -j IN_WEB

重命名自定义链

#示例:将IN_WEB自定义链重命名为WEB
iptables -E IN_WEB WEB

删除自定义链
删除自定义链需要满足两个条件

  • 自定义链没有被引用
  • 自定义链中没有任何规则
#示例:删除引用计数为0并且不包含任何规则的WEB链
iptables -X WEB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment