获取客户端真实IP备忘

news/2024/11/19 16:46:24/文章来源:https://www.cnblogs.com/uncleguo/p/18347117

  出于安全考虑,近期在处理一个记录用户真实IP的需求。本来以为很简单,后来发现没有本来以为的简单。这里主要备忘下,如果服务器处于端口回流(hairpin NAT),keepalived,nginx之后,如何取得客户端的外网IP。

  来自客户端PC的流量路径如上,在这样的拓扑中,在应用服务中取得,客户端PC的外网ip,可能会遇到哪些问题呢?(ip 编的随意,为便于说明,不考虑合理)。

  • 编程实现

  Java 为例,这个我会。

    public static String getClientIP(HttpServletRequest request) {String remoteAddr = request.getRemoteAddr();return remoteAddr;}

  运行一下,输出呢是 3.3.3.3 。 这是因为这个API所取得的是IP数据包的源地址。Nginx的反向代理时工作在应用层的,当他收到一个http请求时,会对应生成一个新的请求,发送给应用服务,这个请求的IP包的源地址是Nginx服务器的IP即3.3.3.3。

  • Nginx头部注入

  因为是应用层,那这个请求ip包的源地址肯定就是3.3.3.3了,但是在应用层我们可以附加一点信息,以便后面的应用服务,可以通过这个附加信息,了解这个请求对应的原始源地址。这个我也会。

  在Nginx 中配置。

server {...proxy_set_header X-Real-IP $remote_addr;

  在应用层http协议中,加一个http header。X-Real-IP:$remote_addr. $remote_addr 是一个预设变量,代表所代理转发请求的原始源ip地址。

  在Java 程序中,读取对应的附加信息

    public String getRealIp(HttpServletRequest request) {String realIp = request.getHeader("X-Real-IP");if (realIp != null && !realIp.isEmpty()) {return "Client's Real IP: " + realIp;} return "";}    

  运行一下,此时输出2.2.2.2。 显然我们向前推进了一步。

  • Keepalived负载均衡模式

  印象里这里keepalived的主要作用应该是解决nginx 代理服务器的单点问题的,似乎也被配置为负载均衡了?翻了下配置文件,实际的情况如下。

  运维大壮说他配置keepalived 时候多考虑了一步,如果机器活着,nginx 挂了怎么办,于是又做了一层负载均衡(这种情况虚拟IP不会漂移到右边的备机)。他说的也确实不是没有道理。keepalived 的负载均衡貌似是工作在第三层的,那肯定在负载均衡的时候,又对ip包的源地址进行了修改。这是网络层,向nginx 这样附加信息肯定是不行了。于是,翻了翻手册发现,keepalived 的负载均衡支持三种路由模式,NAT,Direct Routing 和 Tunneling。

  NAT 模式,会修改源IP,出入流量都会经过负载均衡器。而DR模式,会直接修改MAC地址,那回程流量就不再经过负载均衡器了,也就意味这种模式,源地址不会被修改,回程流量会直接发送给源ip地址。

  DR模式有个要求,就是负载均衡器需要能知道后端服务的MAC地址,这是依赖于ARP实现的,也就是,要求负载均衡器和后端服务器在同一广播域。恰好我门可以满足。于是。

virtual_server 192.168.11.242 80 {
……
lb_kind DR
……

  将负载均衡路由模式切换为DR模式。重新看一下这次,取得客户端地址变成了 1.1.1.1, 这一步一坑。为什么到达keepalived的ip包的源地址会变成,出口路由器的外网地址呢?

  • 路由器端口回流(Hairpin NAT)

  离胜利是不远了,此时见多识广的大壮说,这应该是跟端口回流有关,之前有个系统也是类似问题, 你的web端口配置了端口回流,如果关掉端口回流就可以取得外网地址了。什么是端口回流?

 

   首先,路由器做了端口映射,1.1.1.1:80->192.168.0.2:80

  服务器A,由于某些原因,不方便使用内网地址192.168.0.2访问B,而要通过外网IP或者域名访问服务器B,即访问1.1.1.1:80, 按端口转发规则,路由器会将这个来自于内网接口的流量再次转发回内网服务器B,形成了一个180度的急弯——发卡弯,这也就是Haripin NAT的名字由来,十分形象。

  如果不做设置,服务器A通过访问1.1.1.1:80 是无法正常访问服务器B的。原因是,hairpin会影响Tcp连接建立的握手过程。

  1. A发送握手请求给入口路由器,路由器修改目的ip为192.68.0.2 ,发送到服务器B。

  2.B收到握手请求后,回复握手确认应答给这个握手请求的源IP地址,此处是A的地址192.168.0.1

  3.因为A,B同一网络,握手确认会直接到达A。

  4.A发现这个握手确认回复的源ip(192.168.0.2)并不是我期望与之建立连接的握手请求目的地址(1.1.1.1),A并不认识B,只认识路由器,导致TCP连接无法建立。

  解决以上问题的关键,就是让握手确认应当同样经过路由器,发送给A。因此,需要在之前将握手请求转发给B时,同时修改源ip地址为(1.1.1.1),如此,B服务器作出确认回复时,自然也会发送给1.1.1.1。

  但是这个源地址转化(SNAT)的过程,实际上只对于来自内网的流量是有必要的。对于外网流量,其源IP本身就处于网络外部,必然会经过再次经过路由器返回。

  于是联系管路由器的小明,请他不要偷懒,规则配置的细致一点,不要做无差别的源地址转换。即

  1.对内网接口流量进行源地址和目标地址转换

  2.对外网流量只进行目标地址转化。

  重新测试。 终于输出实际了客户PC实际ip地址0.0.0.0

    

  OK,一波三折,跌宕起伏,写到这里~

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/779343.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[OI] 欢夏!邪龙?马拉车!

标题来自原神 算法概述 Maracher 算法 用途:寻找回文串,最板子的情况下用于字符串的回文子串计数给定一个字符串 \(S\),求出它全部的回文子串容易想到一种暴力的 \(n^{2}\) 做法,即枚举全部中心点,开双指针向两边扩展,每扩展一次就提供 \(1\) 的贡献. 事实上,对于这样的…

一行命令搞定内网穿透

一行命令搞定内网穿透 一款开源免费的内网穿透工具:localtunnel ,基于 nodejs 实现,无需修改 DNS 和防火墙设置,方便快捷的将内网服务暴露到外网,为开发人员、测试人员以及需要分享本地项目的人提供实时的公网访问方式,以便于测试和共享!功能特性一行命令启动,无需复杂…

中电信翼康济世数据中台基于Apache SeaTunnel构建数据集成平台经验分享

Apache SeaTunnel作为一个高效、灵活的数据集成平台,在数据中台战略中扮演着重要角色。通过本文的介绍,读者可以了解如何基于SeaTunnel快速搭建数据集成平台,并在实际应用中灵活运用。未来,随着技术的不断发展,SeaTunnel将继续在数据集成领域发挥重要作用,助力企业实现数…

数据集可以自己找开源

数据集可以自己找开源,比如GitHub上面的等等。 比如这个:https://github.com/bstabler/TransportationNetworks/tree/master其他相关项目 TRB网络建模委员会 InverseVIsTraffic是一个开源存储库,它实现了一些针对单类和多类交通网络提出的逆变分不等式 (VI) 公式。该软件包还…

js实战小项目随笔

获取事件源 var img = document.getElementById("imgs"); setAttribute为事件源赋值 img.setAttribute("src", "./image/" + index + ".jpg"); 图片切换

泛微OA移动端在公网IP变更和无法打开Install.do界面下修改插件库地址(122,128报错)

事情起因 因为公司搬迁,公网IP变更,服务器搬迁时未提前调整好泛微oa服务器的IP,但是旧地址仍留有主路由可以使用。 发现问题移动端无法正常连接,/install.do的界面也无法打开,无法进入修改插件库服务器IP的页面 联系了技术,技术表示/install.do无法打开的话也无能为力(因…

[Paper Reading] DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION

DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION link 时间:2021(ICLR) 机构:Sensetime & USTC & CUHK TL;DR 参考2D Deformable Conv,通过在Reference Point附近增加sample points,将DETR的收敛速度提升10倍,对于小目标效果也更好。 …

js学习

变量通过prompt收集输入信息声明变量特殊情况八股文字符类型可以随时变换js中数字前补0为八进制,补0x为十六进制方法:isNaN(),如果是数字返回false,反之返回true字符串转义符字符串与别的类型拼接,拼接后为字符串字符串内设置变量布尔值数据类型,true参与运算时作为1,…

QStyledItemDelegate 和QTreeView实现鼠标hover消息

1.QTreeView设置属性mousetracking和tablettracing 重写QStyledItemDelegate类,重写函数 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); 这个函数里可以处理鼠标hover和点击事件;bo…

c# net6 读取appsettings.json方法

如图:一、appsettings.json{"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHosts": "*","SqlServer": {"Server":…

CSS3第二天(元素显示模式、图片背景设置)

1.元素显示模式 ①块元素 <h1>~<h6>、<p>、<div>、<ul>、<ol>、<li>等 特点: 1.独占一行 2.高、宽、外边距、内边距可控制 3.宽度默认是父级容器宽度的100% 4.里面放行内或块级元素 注意:文字类元素内不能使用块级元素 <p>、…

CSS3(元素显示模式)

1.元素显示模式 ①块元素 <h1>~<h6>、<p>、<div>、<ul>、<ol>、<li>等 特点: 1.独占一行 2.高、宽、外边距、内边距可控制 3.宽度默认是父级容器宽度的100% 4.里面放行内或块级元素 注意:文字类元素内不能使用块级元素 <p>、…