原文请参阅www.netren.org jeffreyguo整理编辑

iptables DNAT功能就是重写包的目的IP地址。如果一个包被匹配了,那么它所有的包都会被自动转换,然后被路由到正确的主机或网络。



DNAT target是非常有用的一项功能。  如果我的Web服务器在LAN内部,而且没有可在Internet上使用的公网IP地址,那就可以使用DNAT让防火墙把所有到它自己:80(web)端口的包转发给LAN内部真正的Web服务器。

另外目的地址也可以是一个范围,这样的话DNAT会为每一次请求随机分配一个地址。所以,我们可以用这个DNAT做简单的负载平衡。

注意,DNAT 只能用在nat表的PREROUTING和OUTPUT链中,或者是被这两条链调用的链里。

DNAT 选项: --to-destination
范例:iptables -t nat -A PREROUTING -p tcp -d 202.123.45.67 --dport 80 -j DNAT --to-destination 192.168.10.8:80
范例说明:
-t nat 指定要在nat表中添加一条PREROUTING链  -p tcp -d 15.45.23.67 --dport 80 所有要送往202.123.45.67的80端口的数据包 -j DNAT --to-destination 192.168.10.8:80 被DNAT转向192.168.10.8:80端口(也就是LAN内的web服务器上)
这是一个典型的将内部web服务器通过防火墙202.123.45.67发布的案例。要注意:--dport的使用只有先用-p指定了TCP或 UDP协议才能使用。

因为DNAT的处理过程要做很多工作,所以我们再通过一个例子来大致理解一下它是如何工作的:
比如,我想通过Internet连接发布我们的网站,但是web server在我们的内网里,而且我们对外只有一个合法的IP,就是防火墙那个对外的IP(eth1=202.123.45.67)。防火墙还有一个内网的IP($AIP0=192.168.10.1),web server的IP也是内网的($BIP=192.168.10.8)为了完成我们发布的要求,要做的第一件事就是把下面的这个简单的规则加入到nat表的PREROUTING链中:

 iptables -t nat -A PREROUTING -d 202.123.45.67 -p tcp --dport 80 -j DNAT --to-destination 192.168.10.8:80

现在,所有从Internet到防火墙的80端口去的包都会被转发(被DNAT )到在内网的web服务器上。
注意哦:是所有internet来的包会被转发。如果你在Internet上试验一下,一切正常吧。OK 我们完成了web的对外发布。

这时我们再从内网里试验一下“C计算机”通过internet访问web服务器,肯定是不能用的。这是路由的问题,下面我们来好好分析这个问题。
为了容易阅读,我们把在外网上访问我们服务器的那台机子的IP地址记为“D计算机”。
 包从地址为D的机子出发——》去往地址为eth1=202.123.45.67(防火墙)——》包到达防火墙——》防火墙匹配此包为DNAT规则——》转发这个包到eth0—— 》包离开防火墙A向192.168.10.8 web server前进。 包到达web服务器——》80端口回复数据——》防火墙(经过反向DNAT处理)——这样就好像是防火墙自己回复了那个来自外网的请求包。——》发送给计算机D。(这里要求把防火墙作为web到达internet的网关)
 现在,我们来考虑和web服务器在同一个内网(这里是指所有机子不需要经过路由器而可以直接互相访问的网络,不是把服务器和客户机又分在不同子网的情况)的C客户机访问它时会发生什么。我们假设客户机的IP为192.168.10.7,其他设置同上。

由于A是C的网关。C发送请求去往A——》A接到请求后包被DNAT后送往B——》但是包没有经过SNAT 的处理,所以包还是使用它自己的源地址,就是192.168.10.7(译者注:这就是IP 传输包的特点,只根据目的地的不同改变目的地址,但不因传输过程中要经过很多路由器而随着路由器改变其源地址,除非你单独进行源地址的改变。其实这一步的处理和对外来包的处理是一样的,只不过内网包的问题就在于此,所以这里交待一下原因)。——》包离开防火墙,到达web服务器。——》web服务器试图回复这个包。因为来自同一个网络的一台机子,它会把回复包直接发送到请求包的源地址也就是192.168.10.7。——》回复包到达C客户机,但C它会很困惑,因为这个包不是来自它请求的那台防火墙。这样,它就会把这个包扔掉而去继续等待“真正”的回复包。 这就是问题所在~~

针对这个问题有两个解决办法:

第一、

因为这些包都要进入防火墙,而且它们都去往需要做DNAT才能到达的那个地址,所以我们只要对这些包做SNAT操作即可。
比如,我们来考虑上面的例子,如果对那些进入防火墙而且是去往地址为web IP、端口为80的包做SNAT操作,那么这些包就好象是从LAN_IP来的了,也就是说,这些包的源地址被改为LAN_IP了。
这样,web服务器就会把回复包发给防火墙,而防火墙会再对包做 Un-DNAT操作,并把包发送到客户机。
解决问题的规则如下:

 iptables -t nat -A POSTROUTING -p tcp -d 192.168.10.8 --dport 80 -j SNAT --to-source 192.168.10.1

要记住,按运行的顺序POSTROUTING链是所有链中最后一个,因此包到达这条链时,已经被做过DNAT操作了,所以我们在规则里要基于内网的地址web_IP(包的目的地)来匹配包。
 警告:我们刚才写的这条规则会对日志产生很大影响,这种影响应该说是很不好的。因为来自 Internet包在防火墙内先后经过了DNAT和SNAT处理,才能到达web服务器(上面的例子),所以web服务器就认为包是防火墙发来的,而不知道真正的源头是其他的IP。这样,当它记录服务情况时,所有访问记录的源地址都是防火墙的IP而不是真正的访问源。
我们如果想根据这些记录来了解访问情况就不可能了。因此上面提供的“简单办法”并不是一个明智的选择,但它确实可以解决“能够访问”的问题,只是没有考虑到日志而已。
其他的服务也有类似的问题。比如,你在LAN内建立了SMTP服务器,那你就要设置防火墙以便能转发SMTP的数据流。这样你就创建了一个开放的SMTP中继服务器,随之而来的就是日志的问题了。 一定要注意,这里所说的问题是针对没有建立DMZ或类似结构的网络,并且内网的用户访问的是服务器的外网地址而言的。
(译者注:因为如果建立了DMZ,或者服务器和客户机又被分在不同的子网里,那就不需要这么麻烦了。因为所有访问的源头都不在服务器所在的网里,所以就没必要做SNAT去改变包的源地址了,从而记录也就不是问题了。如果内网客户是直接访问服务器的内网地址那就更没事了)

第二、

较好的解决办法:
为你的LAN在内网建立一台单独的DNS服务器,并在DNS内添加好web服务器的IP(译者注:这样以来内网的客户使用网站名访问web服务器时,DNS就可以把它解析成内网地址。客户机就可以直接去访问web服务器的内网地址了,从而避免了通过防火墙的操作,而且包的源地址也可以被web服务器的日志使用,也就没有上面说的日志问题了。)
或者干脆建立DMZ(这是最好的办法,但是有可能需要花销,因为用的设备多呀)。

对上面的例子应该考虑再全面些,现在还有一个问题没解决,就是防火墙自己要访问web服务器时会发生什么,能正常访问吗?你觉得呢?很可惜,现在的配置还是不行,仔细想想就明白了:
我们这里讨论的基础都是假设机子访问的是web服务器的外网地址,但这个外网地址其实就是防火墙对外的地址,所以当防火墙访问这个外网地址时,就是访问它自己。防火墙上如果有web服务,那客户机就会看到页面内容,不过这不是它想看到的(它想要的在DNAT上了),如果没有web服务,客户就只能收到错误信息了。
前面给出的规则之所以不起作用是因为从防火墙发出的请求包不会经过那两条链。还记得防火墙自己发出的包经过哪些链吧:)我们要在nat表的OUTPUT链中添加下面的规则:
 iptables -t nat -A OUTPUT --dst 202.123.45.67-p tcp --dport 80 -j DNAT --to-destination 192.168.10.8
 有了最后这条规则,一切都正常了。

 我想大家应该能明白这些规则只说明了数据包是如何恰当地被DNAT和SNAT的。除此之外,在防火墙filter表中还需要其他的规则(在FORWARD链里),以允许特定的包也能经过前面写的(在POSTROUTING链和 OUTPUT链里的)规则。千万不要忘了,那些包在到达FORWARD链之前已经在PREROUTING链里被DNAT过了,也就是说它们的目的地址已被改写,所有必须添加相应的FORWARD规则,在写规则时要注意这一点。 以上数据包传递机制不仅仅限于web 80端口,对于smtp 25、 ssh 22 、mysql等服务同样适用。