前言
很有必要记录下来,因为一直对 iptables 了解不够透彻,导致对 nat 一直是敬而远之。
直到昨天晚上看到了数据进入服务器,iptables 的转发图后。
一切都变得豁然开朗。
虽然话是这么说,但是开朗和彻底理解还是有些距离的。
也导致我一整个晚上都彻夜难眠。
环境介绍
主机名 | 内网 ip | 外网 ip | 程序名称 |
主机 A | 192.168.0.238/24 | 140.210.202.195 | 无 |
主机 B | 192.168.0.178/24 | 140.210.197.113 | nginx(port:80) |
本次打算访问主机 A 外网 ip(140.210.202.195:80),通过转发,转发到主机 B 的 nginx 上
第一次尝试
主机 A 上执行下面代码
# 临时开启内核的转发功能
echo 1 > /proc/sys/net/ipv4/ip_forward
#设置通过和返回两个方向的 nat
iptables -t nat -I PREROUTING -p tcp --dst 192.168.0.238 --dport 80 -j DNAT --to-destination 192.168.0.178:80
iptables -t nat -I POSTROUTING -p tcp -s 192.168.0.178 --dport 80 -j SNAT --to-source 192.168.0.238
#上面这一行可以改进为下面这行(删掉了 --dport 80)iptables -t nat -I POSTROUTING -p tcp -s 192.168.0.178 -j SNAT --to-source 192.168.0.238
这时候,以我的理解,网络就应该已经通了
因为正反向的转发都已经配置好了
但是我在浏览器中访问:主机 A 的公网 ip 时 (140.210.202.195),没有任何相应
在主机 A 上访问主机 A 的内网 ip:curl 192.168.0.238,没有任何响应
我一度十分沮丧,直到我想起,iptables 的模型中,数据包会因为 ip 是否为当前 ip 而分成两个路线
我开始在主机 B 上访问主机 A 的内网 ip:curl 192.168.0.238,一下子就通了,显得那么流畅
第二次尝试
首先解决在主机 A 上访问本机内网 ip 无相应的问题
按照 iptables 模型图来看,当服务器意识到 ip 指向自己时,nat 表下的 output 就开始起作用了
于是我敲下命令
# 在 nat 表下的 OUTPUT 链上设置 DNAT
iptables -t nat -I OUTPUT -p tcp --dst 192.168.0.238 --dport 80 -j DNAT --to-destination 192.168.0.178:80
当我再次在主机 A 上访问自身内网 ip 时:curl 192.168.0.238,通了!!!
按捺住激动的感觉,开始了第三次尝试
第三次尝试
接下来就是解决公网 ip 访问,无法访问到主机 B 的 nginx
没有头绪的时候,我开始抓包了
首先是抓主机 A 内网访问主机 B,这个没问题,是通的
[root@master ~]# tcpdump -i eth0 tcp port 80 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:25:19.388160 IP 192.168.0.178.40416 > 192.168.0.238.http: Flags [S], seq 4138848512, win 29200, options [mss 1460,sackOK,TS val 140020 ecr 0,nop,wscale 7], length 0
10:25:19.388185 IP 192.168.0.238.40416 > 192.168.0.178.http: Flags [S], seq 4138848512, win 29200, options [mss 1460,sackOK,TS val 140020 ecr 0,nop,wscale 7], length 0
10:25:19.388360 IP 192.168.0.178.http > 192.168.0.238.40416: Flags [S.], seq 2892093041, ack 4138848513, win 28960, options [mss 1460,sackOK,TS val 140020 ecr 140020,nop,wscale 7], length 0
10:25:19.388375 IP 192.168.0.238.http > 192.168.0.178.40416: Flags [S.], seq 2892093041, ack 4138848513, win 28960, options [mss 1460,sackOK,TS val 140020 ecr
接着就是浏览器访问主机 A 的公网 ip 了:
[root@master ~]# tcpdump -i eth0 tcp port 80 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:24:20.610393 IP 140.210.197.113.mbus > 192.168.0.238.http: Flags [S], seq 1934685210, win 29200, options [mss 1460,sackOK,TS val 81242 ecr 0,nop,wscale 7], length 0
10:24:20.610407 IP 140.210.197.113.mbus > 192.168.0.178.http: Flags [S], seq 1934685210, win 29200, options [mss 1460,sackOK,TS val 81242 ecr 0,nop,wscale 7], length 0
10:24:21.611655 IP 140.210.197.113.mbus > 192.168.0.238.http: Flags [S], seq 1934685210, win 29200, options [mss 1460,sackOK,TS val 82244 ecr 0,nop,wscale 7], length 0
10:24:21.611664 IP 140.210.197.113.mbus > 192.168.0.178.http: Flags [S], seq 1934685210, win 29200, options [mss 1460,sackOK,TS val 82244 ecr 0,nop,wscale 7], length 0
结果是,怎么看都是一个公网 ip 去试图访问一个内网 ip
没在一个网段肯定是不通的
没有条件,就创造条件,我开始给公网 ip 映射一个内网 ip:
# 公网 ip 访问内网时,映射一个内网 ip 给公网
iptables -t nat -I POSTROUTING -p tcp --dst 192.168.0.178 -j SNAT --to-source 192.168.0.238
试了下,通了,哈哈哈
又简单改进了一下最后的命令
iptables -t nat -I POSTROUTING -p tcp --dst 192.168.0.0/24 -o eth0 -j MASQUERADE
已经可以完美使用了
最后
贴几个有助于 iptables 学习的文章