文章声明:此文基于木子实操撰写 生产环境:MikroTik RouterOS 6.44.5问题关键字:ROS路由器获取访客真实公网IP及内网回流解决方法
目录
起因
今天开发的同事反馈,在测试环境无法获取客户端IP信息,但在预发布环境可以,这样就造成业务端无法统计访客来源,这对业务造成了诸多的不便。第一时间想到haproxy的问题,于是上服务器查看了一下,发现日志信息如下:
Apr 20 18:29:04 localhost haproxy[25679]: 192.168.8.254:1180 [22/Apr/2021:18:29:04.517] https-in~ bk_git.oubayun.com/git.oubayun.com 0/0/1/38/39 403 391 - - ---- 10/10/1/1/0 0/0 "GET /api/xxx HTTP/1.1" Apr 20 18:29:04 localhost haproxy[25679]: 192.168.8.254:1178 [22/Apr/2021:18:29:04.509] https-in~ bk_git.oubayun.com/git.oubayun.com 0/0/1/52/53 200 1006 - - ---- 9/9/0/0/0 0/0 "GET /api/xxx HTTP/1.1"从日志来看,返回的地址是ROS的网关地址192.168.8.254,问题一定出在ROS上,其实原因很简单,因为ROS也是一个Linux操作系统,如果有使用过Linux iptables做防火墙的同学,相信大概都知道是什么原因。为了使整个内网的服务器可以共享上网,在设置ROS时,我们一般都会设置一条srcnat规则(源地址转换),Action设置为"masquerade",masquerade是snat的一个特例,主要用在无固定IP网关的情况下,比如:adsl拨号等,masquerade比snat效率低的原因是snat直接指出需要伪装的源地址,而masquerade永远以默认网关地址为IP伪装源地址,所以效率会慢一些,其次只能用在外网IP只有一个的情况,如果有多个wan接口就不能用masquerade,而只能用snat,因为snat可以手工指定多个需要伪装的源IP地址,而masquerade却只能找到一个地址,就是默认网关的地址。
SNAT与DNAT
这是涉及到一个DNAT与SNAT的概念,学过计算机网络的同学应该比较清楚,这里简单科普一下: SNAT 即source network address translation的缩写即源地址目标转换 比如,多个PC机使用ADSL路由器共享上网,每个PC机都配置了内网IP。PC机访问外部网络的时候,路由器将数据包的报头中的源地址替换成路由器的IP(网关IP)。当外部网络的服务器比如网站WEB服务器接到访问请求的时候,它的日志记录下来的是路由器的IP地址,而不是PC机的内网IP。这是因为,这个服务器收到的数据包的报头里边的"源地址",已经被替换了。
DNAT 即destination network address translation的缩写即目标网络地址转换 典型的应用是,有个WEB服务器放在内网,配置内网IP,前端有个防火墙,配置公网IP,互联网上的访问者使用公网IP来访问这个网站,当访问的时候,客户端发出一个数据包,这个数据包的报头里边,目标地址写的是防火墙的公网IP,防火墙会把这个数据包的报头改写一次,将目标地址改写成WEB服务器的内网IP,然后再把这个数据包发送到内网的WEB服务器上这样,数据包就穿透了防火墙,并从公网IP变成了一个对内网地址的访问了。
MASQUERADE 地址伪装,不管是ROS、pfSense还是iptables等等防火墙设备,理论上来说,都可以实现masquerade功能。 SNAT,DNAT,MASQUERADE都是NAT,MASQUERADE是SNAT的一个特例。SNAT是指在数据包从网卡发送出去的时候,把数据包中的源地址部分替换为指定的IP,这样接收方就认为数据包的来源是被替换的那个IP的主机。MASQUERADE是用发送数据的网卡上的IP(网关IP)来替换源IP(转换以前),因此,对于那些IP不固定的场合,比如拨号网络或者通过dhcp分配IP的情况下,就得用MASQUERADE。
这里面涉及一个PREROUTING与POSTROUTING的概念,NAT是在PREROUTING链上来进行的,而SNAT是在数据包发送出去的时候才进行,因此是在POSTROUTING链上进行的。每次木子都想通过一篇文章交代清楚一切,结果发现是不可能的,在这里放两张图,供各位同学去领悟。@-@
解决方法
还是直接说解决方法吧,解决的方法有两种:
如果您是带公网固定IP,可以直接改NAT上网方式解决此问题,非固定公网IP其实也可以,就是需要自己单独写一个脚本,来实现公网固定IP自动更新。调整masquerade的设置来完成显示外网IP。木子选用了每二种方法,因为之前木子已经配置了这种方式上网,虽然masquerade效率会慢一些。具体操作如下,进入winbox,点击IP -- Firewall -- NAT,打开NAT配置界面,双击原来建立的srcnat规则masquerade(用于共享上网),在"General"选项卡的 "Out. Interface"选中内网网卡(内网网卡视自己情况而定,因为木子这里划了多个VLAN,所以这里直接使用VLAN接口。),单击将前面的小框,在框中出现"!",单击"OK"完成此设置。
这样设置完成后,我们再查看对应haproxy的日志,外网访客可以正常访问,而且IP显示也是正确的了。
Apr 22 20:44:03 localhost haproxy[25679]: 113.88.22.44:4628 [22/Apr/2021:20:44:03.263] https-in~ bk_git.oubayun.com/git.oubayun.com 0/0/1/42/44 403 391 - - ---- 10/10/1/1/0 0/0 "GET /api/xxx HTTP/1.1" Apr 22 20:44:03 localhost haproxy[25679]: 113.88.22.33:4627 [22/Apr/2021:20:44:03.267] https-in~ bk_git.oubayun.com/git.oubayun.com 0/0/0/62/62 200 1006 - - ---- 9/9/0/0/0 0/0 "GET /api/xxx HTTP/1.1"但同时导致了别一个问题,就是内网用户不能用外部IP访问映射的内部服务器(即DNAT的服务器公网端口,怎么测试了?比如您的公网IP是123.77.35.32,在内网的服务器上telnet 123.77.35.32 22,22为映射的SSH端口,如果不通,就需要进行后续设置了。 ),再增加一条对内网的规则,点击IP -- Firewall -- NAT,打开NAT配置界面,点击"+"(加号),在"General"选项卡中,设置Chain为 "srcnat",Src. Address为"192.168.0.0/16"(由于木子的内网为192.168.0.0/16 C类网段),所以在此将子网设置为16,也就是255.255.0.0,Action设置为"masquerade",设置好后如下图所示,这时候就可以正常访问了。
[root@haproxy ~]# telnet 123.77.35.32 22 Trying 123.77.35.32... Connected to 123.77.35.32. Escape character is ^]. SSH-2.0-OpenSSH_7.4 ^] telnet> quit Connection closed.至此,内外网都能正常访问了且外网访客IP也是正确的,此时内网用外网IP访问WEB时,显示的IP是内网网关地址,用这种方法可以实现内外网用户都用网关外部IP访问映射的内部服务,并解决了外部用户显示的IP不正确问题,当然这种方法内网用户显示的IP还是不正确的,要解决内网显示IP问题,可以在ROS中设DNS服务器,用域名来访问,内网访问内网IP,外网访问外网IP就能完美解决。因为木子主要是要知道外网访客IP,内网IP并不重要,所以至此也算解决了此问题。
haproxy、Nginx、Traefik配置
需要注意的一点是,如果您的后端还有使用haproxy、Nginx、Traefik等反向代理,可能还需要进行一些其它配置,比如haproxy、Nginx需要添加一条规则option forwardfor(这是7层透传),方能够保证后端服务器可以获取到公网IP地址信息。
三平台同步更新:博客: https://www.oubayun.com知乎: 欧巴云: 欧巴云