本文目录
- 1、确认Linux kernel内核版本
- 2、netfilter的nat table简介
- 3、用iptables实现SNAT
- 3.1、 多对多(N:N)的SNAT
- 3.2、 将一个网段内的某个公网IP移除出SNAT可用的公有IP地址池
- 3.3、 设置目标地址为特定IP地址或者网段的报文不做NAT
- 3.4、通过端口号,设置允许或者禁止SNAT特定的协议
- 4、用iptables实现DNAT
- 4.1、如果你需要从外网全权访问私网里的某个主机(比如192.168.1.50)。
- 4.2、从特定外网IP访问私网内某个服务器的某个业务
- 5、透明代理
- 6、以上场景的配置脚本
在文章NAT网络地址转换技术入门到详解中,我们介绍了NAT的原理,NAT的分类,以及每种类型的NAT的功能。
netfilter/iptables是用来实现NAT的内核软件和用户配置工具软件。本文中我们会来详细描述如何使用iptables配置和实现NAT。
1、确认Linux kernel内核版本
通常的Linux发行版都已经自带了netfilter和iptables这二个软件和其它NAT需要用到的其它内核模块(比如连接跟踪,IP转发等内核功能模块),所以通常只要拿来直接使用就可以了,我们并不需要对Linux内核做出编译与修改。如果你需要自己编译Linux kernal,可以参照HowTo on compiling Linux 2.4 and 2.6 kernels 这本书(你可以在这个网页找到这本书http://www.digitalhermit.com/linux/Kernel-Build-HOWTO.html)。
2、netfilter的nat table简介
我们已经知道netfilter框架里,包含了所谓的4表5链,这里我们会需要使用4表里的nat table来实现NAT功能。nat table 包含了4个链—PREROUTING, INPUT, POSTROUTING和OUTPUT。每个链都包含多条规则rules,一个报文会被按照顺序进行匹配,直到命中某条规则。我们可以用命令sudo iptables –t nat –L查到nat表格里的这些链。
xxx@raspberrypi:~ $ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
本文中不使用OUTPUT chain和INPUT chain,PREROUTING和POSTROUTING chains的名字就表明了他们的用途。在报文进入Linux后,Linux内核根据路由表做出路由决定前,去检查 PREROUTING chain里的规则,因此所以我们可以使用PREROUTING chain去改变报文的目标IP地址,然后让下一步让的路由表去根据新的目标IP地址决定如何处理报文(DNAT)。在Linux内核对某个报文根据路由表做出转发决定后,下一步会根据POSTROUTING chain中规则进行处理,这意味着在这里,我们可以改变要发送出去的报文的源IP地址(SNAT)。
注意:这里和报文从哪个网口进,哪个网口出没有关系,所有进入Linux的的非上送给Linux本机而是需要被转发的报文都会经过这条路径进行处理。
上图表示了 DNAT和SNAT是如何工作的。
DNAT意味着修改报文的目标IP地址,所以Linux内核必须在根据报文的目标IP地址查找路由表并决定如何转发IP报文前完成目标IP地址的修改 (所以由PREROUTING chain完成)。例如:如果我们要为一个私网服务器192.168.1.3做DNAT,那么当Linux路由器的从公网收到IP报文时,它需要收到的IP报文的目标IP地址成192.168.1.3,然后再交给Linux内核做路由查找和转发决定:
router:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
Linux内核发现192.168.1.0/24网络是直接连接在Eth1网卡上的,所以它会直接将报文从Eth1发送给192.168.1.3。
SNAT 意味着修改了IP报文的源IP地址。 让我们考虑一下IP伪装Masquerade是怎么工作,这会有助于我们的理解。一个从192.168.1.3发出来的报文想要访问外网。
- Linux内核会查找路由表,如果这个报文需要多某个接口发出去,那个就会修改报文的源IP地址成这个接口的IP地址。
- 如果没有找到这个报文的命中的路由表项,就会路由到默认路由去,然后就会把报文的源IP地址修改成默认路由指向的那个接口。
在NAT table的末尾添加,插入,或者删除规则的命令的语法都非常类似:
- Append在末尾添加: iptables –t nat –A
- Insert插入到最前面: iptables –t nat –I
- Delete删除: iptables –t nat –D
- Delete all删除所有: iptables –t nat –F
这里 应该是PREROUTING, POSTROUTING, INPUT或者OUTPUT,替换成真正的规则。
注意:iptables –F 命令是删除了默认table, filter table里的所有chain的所有规则,如果你要删除nat table里的规则,需要用-t来指定table。
3、用iptables实现SNAT
SNAT是几种NAT技术里最常用的类型。以以下例子为例进行说明:
192.168.1.0/24是我们办公室的私网的网段。我们可以通过1.2.3.1/30这个IP访问外网,默认网关是1.2.3.2.
所有内网主机的默认网关是192.168.1.1。
我们可以看到Linux router有二个以太网口:
- Eth0, IP地址是192.168.1.1(掩码255.255.255.0),这个网口通过交换机连接到私网192.168.1.0/24网络上。
- Eth1, IP地址是1.2.3.1(掩码255.255.255.252),连接到外网上
我们可以用以下一条规则就为私网192.168.1.0/24里的所有设备都建立SNAT,并使得私网内的所有设备都可以访问外网:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.3.1
这条命令的作用和下面这条命令的作用是一样的,但使用下面这条IP伪装的命令,我们可以支持Eth1的动态的IP地址,而不需要像上面那条命令一样指定外网的IP地址(注意IP伪装Masquerade是用指定路由出口网卡的IP地址来替换源地址,所以它能支持动态的获取出口网卡的IP地址来替代):
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE
假如我们的网络供应商说只有1024以下的端口可以被使用,那么我们除了修改源IP地址网还需要修改源端口,这个SNAT的规则也是可以用于修改端口的,如下所示将报文的源端口修改成1-1024之间未使用的端口号:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.3.1:1-1024
注意,如果么网里的笔记本电脑用户想要上IRC或者FTP的话,那么还需要针对ip_conntrack打上ip_conntrack_
irc和ip_conntrack_ftp 模块才行,原因在于NAT只工作在IP层,但IPC和FTP这种协议在用户层也带有IP地址的信息,所有需要额外的功能模块来处理用户层的IP信息替换工作。
modprobe ip_conntrack_irc
modprobe ip_conntrack_ftp
#或者
insmod ip_conntrack_irc
insmod ip_conntrack_ftp
以下我们通过几个例子来学习一下SNAT的一些进阶的设置,以解决SNAT中常见的一些问题:
3.1、 多对多(N:N)的SNAT
IRC网络的服务端是限制来自同一个IP的客户端连接数量的,意味着当私网内的多个主机都使用SNAT去访问IRC服务器时,IRC服务器会检测到来自于同一个IP(1.2.3.1)的客户端连接数量超出了上限,那到超出后IRC服务器就会拒绝连接。此时我们可以通过增加公网IP的数量,并将私网IP地址SNAT到不同的公网IP地址的方式来减少以每个公网IP地址为源IP地址的IRC连接数量。当我们拿到额外的1.2.4.0/27子网里的所有公网IP地址后,我们将SNAT规则修改如下:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.32
这样就解决了问题,但这样的命令的话,就使1.2.3.1这个公网IP没有被加到SNAT的IP池里,没有利用起这个公网IP地址,所以修改上述命令如下:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
3.2、 将一个网段内的某个公网IP移除出SNAT可用的公有IP地址池
比如我们需要将1.2.4.15从可用的公网IP地址池中移除,那么我们可以使用以下命令
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.14 --to 1.2.4.16-1.2.4.32 --to 1.2.3.1
3.3、 设置目标地址为特定IP地址或者网段的报文不做NAT
这个通常可以用做二个用途:
- 防止私网用户修改网络配置后,将内网的访问报文转发到Linux router来后被Linux router误做SNAT而导致私网内的主机之间不可连接。
比如私网内的192.168.1.19主机,把自己的网络掩码从192.168.1.0/24改成了192.168.1.0/27意味着,192.168.1.19这个主机认为只有192.168.1.1-192.168.1.31这些IP地址和它是在同一个子网下的,所以当它去访问192.168.1.32这个IP地址时,它会先把报文发给它的默认网关192.168.1.1即Linux Router, 然后在没有配置SNAT时Linux Router会根据路由表将这个报文再转发给192.168.1.32这个主机,但此时设置了SNAT功能,所以Linux Router会把来自己192.168.1.19的所有报文的源地址都先修改成1.2.3.1或者其它公网IP后再把报文发给192.168.1.32,当192.168.1.32发回响应报文时就报影应报文发给了1.2.3.1,这个报文就回不到192.168.1.19了,所以导致192.168.1.19和192.168.1.32之间网络不通。我们用以下命令来解决这个问题,不对目标地址为192.168.1.0/24网段内的所有报文做SNAT功能。
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d ! 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
或者用-o eth1指定只对从Eth1发送出去的报文做SNAT:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
- 禁止私网内的用户访问某些公网的服务器(比如不对目标地址为1.3.3.0网段下的主机IP地址的报文做SNAT)
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d ! 1.3.3.0/24 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
- 局域网内二个不同网段的服务器之间的报文不做SNAT
假设我们的私网内有二个网段192.168.1.0/24和192.168.2.0/24,二个网段都被连接到了Linux Router,那么需要添加如下的规则,避免对二个网段之间互相访问的报文被SNAT
iptables -t nat -I POSTROUTING -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT
iptables -t nat -I POSTROUTING -s 192.168.2.0/24 -d 192.168.1.0/24 -j ACCEPT
这二个命令会在NAT规则前插入这二个规则(其实-I会把当前规则插入到所有已经存在的规则的前面),当192.168.2.0/24和192.168.1.0/24之间相互通信时,报文会先分析并命中这二条规则,命中后就不会再分析后面的规则了,所以就不会发生SNAT。
3.4、通过端口号,设置允许或者禁止SNAT特定的协议
- 黑名单制度
假设领导不希望让特定员工访问IRC,我们知道IRC的服务器是工作在6666-6669端口的,所以我们只要把从这个员工的电脑的IP地址发出来去访问目标主机的6666-6669端口的报文全部丢弃则可,则可以插入如下规则实现:
iptables -t nat -I POSTROUTING -s 192.168.1.31 -p tcp --dport 6666:6669 -j DROP
- 半白名单制度
或者是只允许访问特定端口,比如某员工只允许他访问网页,其它协议服务都不可用,则我们可以只允许他访问TCP的80端口,其它数据一律丢弃:
iptables -t nat -I POSTROUTING -s 192.168.1.31 -p tcp --dport ! 80 -j DROP
注意,这个不等同于白名单制度,这里丢弃的是非80端口的TCP协议,但UDP协议的通信依然是可以进行。
- 白名单制度
如果要做成白名单制度,需要在一条或者多条白名单规则后,添加一条丢弃所有报文的规则(配置实例待更新)。
4、用iptables实现DNAT
DNAT的功能与原理在NAT网络地址转换技术入门到详解中已经介绍过了,我们假设本文的读者已经明天了DNAT的功能与原理了,所以这里只简单如何配置与使用。
4.1、如果你需要从外网全权访问私网里的某个主机(比如192.168.1.50)。
将一个公网IP地址(比如1.2.4.1))绑定给192.168.1.50这个主机,这时我们就可以通过DNAT来实现这个功能:
iptables -t nat -A PREROUTING -d 1.2.4.1 -j DNAT --to 192.168.1.50
配置完后,我们对所有192.168.1.50的访问都可以通过1.2.4.1来完成。
4.2、从特定外网IP访问私网内某个服务器的某个业务
这是出于安全性考虑的最小授权原则出发,严格限制访问者(通过访问者的IP地址)和严格限制可以访问的服务。如需要从1.2.5.17访问192.168.1.100的内网web服务,那么在我们设置了以下规则后,他就可以通过1.2.4.2来访问192.168.1.100的内网web服务了。
iptables -t nat -A PREROUTING -s 1.2.5.17 -d 1.2.4.2 -p tcp --dport 80 -j DNAT --to 192.168.1.100
同样出于安全原因考虑,我们DNAT可以帮助我们做信息隐藏,增加安全性。比如,我们希望从外网的任意主机通过SSH访问内网web服务器,因为内网web服务器里往往有非常重要的信息,所以通过DNAT直接映射一个公网IP地址到内网web服务器的私网IP地址,并不是一个非常安全的方案,这会让我们的内网web服务器的SSH服务完全暴露在外,会增加受到网络攻击的风险,更安全的方式是把端口也做一个映射,而攻击者不知道我们把SSH端口映射到了哪个端口,这样会使得SSH端口对外隐藏起来,从而增加安全性(这本质上已经是PAT或者称为NAPT了)。如下用65521端口去映射22端口。
iptables -t nat -A PREROUTING -d 1.2.4.2 -p tcp --dport 65521 -j DNAT --to 192.168.1.100:22
这样的配置下,如果我们从外网想要ssh到内网web服务器的话,我们需要ssh 1.2.4.2:65521.
我们把公司的外网web服务器部署在192.168.1.200上,通过DNS服务将域名www.mycompany.com映射到1.2.4.5,为了使外网web服务器从外网可以访问,我们需要设置如下规则:
iptables -t nat -A PREROUTING -d 1.2.4.5 -p tcp --dport 80 -j DNAT --to 192.168.1.200
5、透明代理
待更新)
透明代理是一种强制使用用户使用代理服务器的方法,使用透明代理不需要用户在他们的终端或者他们的浏览器上配置代理。使用代理服务器有好多好处,比如通过代理服务器上的网页cache来节省流量,访问控制(如:禁止危险文件的下载).
使用透明代理还有一个好外,是可以防止用户自行绕过代理服务器。这个可以防止用户访问黄赌毒等有害网站。如果你的代理服务器工作在Linux Router的3128端口,那么我们可以使用下面命令来实现透明代理:
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 -j REDIRECT --to-port 3128
将192.168.1.0/24网段过来的访问网页的IP报文都转发到3128端口。
如果你要让某些私网里的主机可以不需要通过代理而是直接访问外网,那么可以通过以下命令给它加一个例外:
iptables -t nat -I PREROUTING -s 192.168.1.50 -p tcp --dport 80 –j ACCEPT
在PRE-ROUTING里会命中这条规则,然后在POSTROUTING里通过SNAT功能访问外网。
6、以上场景的配置脚本
#!/bin/bash
IP=/sbin/iptables
#... some packet filtering rules
### NAT SECTION
#first of all, we want to flush the NAT table
$IP -t nat -F
############ SNAT PART
#特定电脑只能访问web和DNS.
#Don't SNAT any TCP connections from her computer except www and all
#udp connections except DNS
$IP -t nat -A POSTROUTING -s 192.168.1.31 -p tcp --dport ! 80 -j DROP
$IP -t nat -A POSTROUTING -s 192.168.1.31 -p udp --dport ! 53 -j DROP
#公司内部的二个子网间的通信不要做NAT
#Don't SNAT anything from 192.168.1.0/24 to 192.168.2.0/24
$IP -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT
#映射192.168.1.50成1.2.4.1使得192.168.1.50的数据访问固定从1.2.4.1发出
$IP -t nat -A POSTROUTING -s 192.168.1.50 -j SNAT --to 1.2.4.1
#从eth1出去的报文都要做SNAT
$IP -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
############ DNAT PART
#Dnat映射192.168.1.50成1.2.4.1使得192.168.1.50可以通过1.2.4.1自由访问
$IP -t nat -A PREROUTING -d 1.2.4.1 -j DNAT --to 192.168.1.50
#DNAT 特定的外部主机1.2.5.17可以访问内网web服务器
$IP -t nat -A PREROUTING -s 1.2.5.17 -d 1.2.4.2 -p tcp --dport 80 -j DNAT --to 192.168.1.100
#DNAT转换SSH登录的端口
$IP -t nat -A PREROUTING -d 1.2.4.2 -p tcp -dport 65521 -j DNAT --to 192.168.1.100:22
#DNAT 将外网web服务器部署到内部主机上
$IP -t nat -A PREROUTING -d 1.2.4.5 -p tcp --dport 80 -j DNAT --to 192.168.1.200
############ 透明代理
#允许192.168.1.50绕过代理
$IP -t nat -A PREROUTING -s 192.168.1.50 -p tcp --dport 80 -j ACCEPT
#给所有其它人设置透明代理
$IP -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 -j REDIRECT --to-port 3128
### End of NAT section