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 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