本文只是为了记录下一直没太理清的iptables的SNAT和DNAT,其实就是"地址翻译"很容易。

1. iptable规则链

先摆上 无Mangle表 的iptables 规则链:(来自《鸟哥私房菜》)无Mangle的表更加常用分析起来逻辑更加清晰。

iptables 实现NAT上网_IP

完整的包含mangle表的:

iptables 实现NAT上网_网络_02

2. SNAT例子:IP分享器

局域网的人多人上网,出去的时候都是通过路由器的public ip地址出去,对方服务器也只能看到其来自于public ip。

iptables -A INPUT -i $INIF -j ACCEPT
#这一行为非必要的,主要的目的是让内网 LAN 能够完全的使用 NAT 服务器资源。
# 其中 $INIF 在本例中为 eth1 接口

echo "1" > /proc/sys/net/ipv4/ip_forward
# 上头这一行则是在让你的 Linux 具有 router 的能力

iptables -t nat -A POSTROUTING -s $innet -o $EXTIF -j MASQUERADE
# 这一行最关键!就是加入 nat table 封包伪装!本例中 $innet 是 192.168.100.0/24
# 而 $EXTIF 则是对外界面,本例中为 eth0 如果没有写的话则使用gateway所属的NIC

工作原理:

  1. 局域网内部192.168.100.110访问 www.baidu.com
  2. 封包到达NAT主机(例如:家用路由器),则先进行路由:选择www.baidu.com 出口的的路由
  3. 在NAT POSTROUTING时候对来自于$innet的SOURCE进行伪装,伪装成$EXTIF的NIC的IP地址,为61.32.52.78(若家用路由器分配到的IP地址是这个的话) ,并且保存这个翻译的映射关系到内存
  4. 封包经由$EXTIF的NIC出去
  5. www.baidu.com 的response返回到 61.32.52.78 家用路由器,NAT PRETROUTING的时候修改DESTINATION为192.168.100.110(这不是依赖于iptables的规则,而是依赖于之前内存保存的翻译的映射关系,反过来使用)
  6. 进行路由,www.baidu.com 的response到达192.168.100.110

直接修改指定IP的来源地址

# 只对192.168.1.210进行SNAT转换,修改其来源IP为eth0的IP
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 192.168.1.210

# 对192.168.1.210-192.168.1.220进行SNAT转换,修改其来源IP为eth0的IP
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 192.168.1.210-192.168.1.220

3. DNAT例子:DMZ主机

DMZ主机,局域网内部提供对外可被访问。
相信很多痴迷于花生壳解析的童鞋一定知道我在说什么:局域网搭建一台web服务器,然后在路由器的界面上设置DMZ主机端口映射下,然后配上花生壳,别人就能访问到你局域网的网站啦。

外界请求eth0的tcp 80端口,则在路由前修改DESTINATION为内网的192.168.100.10:80端口

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 \
> -j DNAT --to-destination 192.168.100.10:80

如果destination是本机的另一个网段的话,则可以 redirect

-j REDIRECT --to-ports <port number>
# 这个也挺常见的,基本上,就是进行本机上面 port 的转换就是了!
# 不过,特别留意的是,这个动作仅能够在 nat table 的 PREROUTING 以及 OUTPUT 链上面实行而已喔!

范例:将要求与 80 联机的封包转递到 8080 这个 port

[root@www ~]# iptables -t nat -A PREROUTING -p tcp  --dport 80 \
> -j REDIRECT --to-ports 8080
# 这玩意最容易在你使用了非正规的 port 来进行某些 well known 的协议,
# 例如使用 8080 这个 port 来启动 WWW ,但是别人都以 port 80 来联机,
# 所以,你就可以使用上面的方式来将对方对你主机的联机传递到 8080 啰!

著名的的Kubernetes的kube-proxy组件就是这样做的。可见机器内部有53端口有个DNS服务器,443端口有个kubernetes的apiserver服务器。

Chain KUBE-PORTALS-CONTAINER (1 references)
target     prot opt source               destination         
REDIRECT   udp  --  0.0.0.0/0            10.254.254.10        /* kube-system/kube-dns:dns */ udp dpt:53 redir ports 40665
REDIRECT   tcp  --  0.0.0.0/0            10.254.254.10        /* kube-system/kube-dns:dns-tcp */ tcp dpt:53 redir ports 51202
REDIRECT   tcp  --  0.0.0.0/0            10.254.0.1           /* default/kubernetes: */ tcp dpt:443 redir ports 39488

Chain KUBE-PORTALS-HOST (1 references)
target     prot opt source               destination         
DNAT       udp  --  0.0.0.0/0            10.254.254.10        /* kube-system/kube-dns:dns */ udp dpt:53 to:192.168.2.66:40665
DNAT       tcp  --  0.0.0.0/0            10.254.254.10        /* kube-system/kube-dns:dns-tcp */ tcp dpt:53 to:192.168.2.66:51202
DNAT       tcp  --  0.0.0.0/0            10.254.0.1           /* default/kubernetes: */ tcp dpt:443 to:192.168.2.66:39488

4. 总结

就记住两条规则即可:

  1. SNAT修改的是Source的地址,也就是要在路由后修改Source,故而配置POSTROUTING规则
  2. DNAT修改的是Destination的地址,也就是在路由前修改Destination,故而配置PREROUTING规则

今天在配置kubernetes的服务发现之DNS的时候,遇到了一直无法从container访问service的问题,也就是无法访问上述的 10.254.254.10 的53端口(DNS服务)。
后来参考上述的 iptables 规则链,发现是路由的问题,对于这个问题改天会开一篇帖子专门分析这个问题,以及Kubernetes的服务之网络、iptables分析。

参考

1 第九章、防火墙与 NAT 服务器