[转帖]Nginx 保留 Client 真实 IP
https://lqingcloud.cn/post/nginx-01/#:~:text=%E5%9C%A8%20Nginx%20%E4%B8%AD%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E4%B8%8B%E9%9D%A2%E7%9A%84%E5%8F%98%E9%87%8F%E6%9D%A5%E8%8E%B7%E5%BE%97%E5%AF%B9%E5%BA%94%E7%9A%84%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BF%A1%E6%81%AF%EF%BC%8C%E5%85%B7%E4%BD%93%E8%80%8C%E8%A8%80%E5%A6%82%E4%B8%8B%E6%89%80%E7%A4%BA%EF%BC%9A%20%24proxy_protocol_addr%E5%92%8C%24proxy_protocol_port%E8%A1%A8%E7%A4%BA%E7%9A%84%E6%98%AF%E5%8E%9F%E5%A7%8B%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84IP%E5%9C%B0%E5%9D%80%E5%92%8C%E7%AB%AF%E5%8F%A3%E5%8F%B7%EF%BC%8C%E5%8D%B3%20nginx%20%E4%BD%9C%E4%B8%BA%20Proxy,Protocol%20Reciever%20%E6%97%B6%E4%BB%8E%20Proxy%20Protocol%20%E5%8D%8F%E8%AE%AE%E4%B8%AD%E8%A7%A3%E6%9E%90%E5%88%B0%20IP%20%E4%BF%A1%E6%81%AF%E3%80%82
概述
Nginx 作为经久不衰的负载均衡工具,具备 L7/L4 层流量的负载均衡能力,并且支持 Proxy Protocol、限速、负载均衡等能力。
本文将讨论在 Kubernetes 中使用 Nginx 作为容器流量入口的一些方案:
- 实现类似 kubernetes NodePort 的四层负载均衡能力
- Pod 获取 Client 真实 IP 的能力。
这些内容在我个人看来绝对不是 Kubernetes 生产环境中的最佳实践,但这些“奇淫技巧”或许在一些极端环境中能对我们有一些帮助。
NodePort 功能
通过 Nginx 原生的 stream 模块,我们可以实现和 Kubernetes NodePort 类似的流量 4 层负载均衡功能。
在 Kubernetes 中部署 Nginx 时,我们采取以下方式,Yaml 文件可以参考 Demo:
- 通过 DaemonSet 方式部署
- Nginx 运行在主机网络中
我们部署 whoami 容器作为测试业务,并创建 ClusterIP类型的 Service ,Yaml 文件可以参考 Demo。
为了使用户在 Kubernetes 集群外能正常范围 whoami 服务,我们使用 stream 模块配置反向代理,核心配置文件如下:
|
|
上述配置中,我们直接讲服务器的 18080 端口反向代理到 whoami.default.svc.cluster.local:80
从而实现类似 NodePort 的功能。
默认情况下 Nginx 会一直缓存域名的 IP 地址,因此当 whoami.default.svc.cluster.local
的 ip 地址变更时需要重启 Nginx 容器反向代理才能正常工作。
如何处理 nginx 开源版本的 DNS 动态解析问题,不在本文讨论范畴,大家可以自行百度。
Client 真实 IP
在运行 Kubernetes 的环境中,Pod 处理请求时获取 Client 的真实 IP 是一个比较重要的需求,目前比较靠谱的解决方案我在 《获取Client真实IP》 一文中进行了介绍,本文不再一一赘述。
这里我将主要验证一种基于 Nginx + Proxy Protocol 的方案来实现获取 Client 真实 IP 。
1. Proxy Protocol 协议
Proxy Protocol 是 HAProxy 的作者 Willy Tarreau 于2010年开发和设计的一个 Internet 协议,通过为 tcp 添加一个很小的头信息,使 TCP 连接能够在复杂网络场景下,传递真实的 Client IP 信息。
Proxy Protocol 工作时存在 Sender/Reciever 两种角色。Sender 负责构建 TCP 连接中的头信息并和 Reciever 建立 TCP 连接。当 Reciever 接受到 TCP 报文时,根据协议解析报文头获取 Sender 填入的内容。
目前 Proxy Protocol有两个版本,v1 仅支持 human-readable 报头格式(ASCIII码),v2 同时支持 human-readable 和二进制格式。v1 和 v2 版本仅在报文格式上存在差异,v2 并没有在 v1 基础上新增特殊功能。 Proxy Protocol 协议具体的报文格式,可以参考文档《Proxy Protocol》。
需要注意,Sender 构建报文时填写的源IP和端口信息可以是自身的 IP 或者端口,也可以是任意值。
Proxy Protocol 协议实际上改变了 Client/Server 建立 TCP 连接的过程,存在以下局限:
- 使用 Proxy Protocol 通信时,Client/Server(Sender/Reciever) 都需要支持 Proxy Protocol 协议。
- 对于服务器的同一个监听端口,无法同时兼容带 Proxy Protocol 包的连接和不带 Proxy Protocol 包的连接。
根据 HAProxy 官方的描述 Proxy Protocol 协议最主要的用途是:在多层负载均衡环境中保留Client源IP。
假定某个系统存在以下网络拓扑架构:
上述网络架构中,请求从 Client 到 Server 经历了 L4/L7 两次转发。这种情况下报文到达 nginx(L7 负载均衡)时,就已经丢失了 Client 原始 IP 信息。为了解决这个问题,我们可以在将 L4LB 和 Nginx 之间建立 Proxy Protocol 连接,使 nginx 可以解析 Proxy Protocol 包获取真实 IP 并设置到后续 HTTP 连接的 Header 中。该方案中 Client 和 Server 都无需支持 Proxy Protocol 协议,所有功能实现或者配置都下沉到了 L4LB / L7LB 两个基础设施中。
HAProxy 官方列出了不少支持 Proxy Protocol 协议的软件,其中绝大部分正是一些商业/开源的负载均衡软件:
- AWS Network Load Balancer
- DigitalOcean Load Balancer
- Google Cloud Load Balancing
- Envoy Proxy
- NGINX/NGINX Plus
- HAProxy
2. 方案验证
截至到 Nginx 1.11.4 版本,用户可以将 Nginx 作为 Proxy Protocol 的 Sender/Reciever 部署到应用在自己的生产环境中。
在 Nginx 中可以通过下面的变量来获得对应的客户端信息,具体而言如下所示:
- $proxy_protocol_addr和$proxy_protocol_port表示的是原始客户端的IP地址和端口号,即 nginx 作为 Proxy Protocol Reciever 时从 Proxy Protocol 协议中解析到 IP 信息。
- $remote_addr和$remote_port表示的 Nginx 服务本身的 IP 地址和端口。
如果用户使用了 RealIP 扩展模块,那么这个模块会重写$remote_addr和$remote_port这两个值,将其替换成和$proxy_protocol_addr一样的值,然后使用$realip_remote_addr和$realip_remote_port来表示原有值。
使用 Nginx 的 Proxy Protocol 功能和 RealIP 模块,我们可以改进前文中的示例,使 whoami 容器在任意情况下都可以获取 Client 的真实 IP 地址。
在我们的方案中,nginx 转发用户请求时需要作为 Sender 将 Client 真实 IP 封装到 Proxy Protocol 包中,Pod 在接收到连接请求时从 Proxy Protocol 包获取 Client 的真实 IP 信息。为了验证上述方案,我修订了首先 whoami 使之可以作为 Server 正常处理 Proxy Protocol 。
修改 whoami 使它支持 Proxy Protocol 非常简单,有兴趣的可以参考: https://github.com/pires/go-proxyproto
修订 nginx 的配置文件,如下:
|
|
根据上述的验证,我们可以达到预期目标,但是需要推动业务修改底层代码以支持 proxy protocol。这些修订的成本需要业务组件进行评估,可能微不足道也可能非常巨大!
上述配置中 nginx 监听的 18080 没有开启 proxy_protocol 监听,只能接收普通的 tcp 请求。我们可以配置该端口处理 proxy_protocol 请求,此时我们需要修改 client 使它发送 proxy_protocol 报文。
这种情况下会有一个非常有意思的地方,client 在发送 proxyproto 头时,可以将源 ip 地址指定为任意值!
|
|
参考
[转帖]Nginx 保留 Client 真实 IP的更多相关文章
- Nginx负载均衡反向代理 后端Nginx获取客户端真实IP
Nginx 反向代理后,后端Nginx服务器无法正常获取客户端的真实IP nginx通过http_realip_module模块来实现的这需要重新编译,如果提前编译好了就无需重新编译了1,重新编译ng ...
- 13.多级代理下Nginx透传真实IP
1.基于代理(七层负载均衡)情况下 透传客户端的真实IP 环境: 10.0.0.5 proxy_node1 一级代理 10.0.0.6 proxy_node2 二级代理 10.0.0.7 proxy_ ...
- CDN下nginx获取用户真实IP地址
随着nginx的迅速崛起,越来越多公司将apache更换成nginx. 同时也越来越多人使用nginx作为负载均衡, 并且代理前面可能还加上了CDN加速,但是随之也遇到一个问题:nginx如何获取用户 ...
- nginx 如何显示真实ip
nginx做反向代理显示在后台访问的真实ip总是显示127.0.0.1 只要添加如下内容: proxy_set_header Host $host; proxy_set_header X-For ...
- nginx获取上游真实IP(ngx_http_realip_module)
realip模块的作用是:当本机的nginx处于一个反向代理的后端时获取到真实的用户IP,如果没有realip模块,nginx的access_log里记录的IP会是反向代理服务器的IP,PHP中$_S ...
- nginx中获取真实ip(转)
原文:http://blog.csdn.net/a936676463/article/details/8961504 server { listen 80; server_name lo ...
- 通过Nginx获取用户真实IP
nginx配置 location / { proxy_set_header Host $host; proxy_set_header X-real-ip $remote_addr; proxy_set ...
- linux下利用tcpdump抓包工具排查nginx获取客户端真实IP实例
一.nginx后端负载服务器的API在获取客户端IP时始终只能获取nginx的代理服务器IP,排查nginx配置如下 upstream sms-resp { server ; server ; } s ...
- nginx使用用户真实IP做hash(解决经过CND后ip_hash失效问题)
在nginx中常用的有以下四种负载均衡的算法,分别是:round-robin.ip-hash.least-connected和weighted.当然在实际生产中或许使用最多的就是ip-hash了,一般 ...
- nginx中获取真实ip
nginx反向代理配置时,一般会添加下面的配置: proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; ...
随机推荐
- Kiractf
信息收集 主机发现和端口扫描只开放了80的web服务 WEB打点 访问首页有文件上传,肯定可以利用一波.language那个页面甚至文件包含都写脸上了. root@Lockly tmp/ki ...
- 拥抱Serverless释放生产力,探索华为云Serverless车联网最佳实践
华为云Serverless车联网场景解决方案,以FunctionGraph为核心的Serverless化组合方案,使用FunctionGraph.OBS.DIS等技术,可以实现架构的灵活扩展,在出行高 ...
- 数仓集群管理:单节点故障RTO机制分析
摘要:大规模分布式系统中的故障无法避免.发生单点故障时,集群状态和业务是如何恢复的? 本文分享自华为云社区<GaussDB (DWS) 集群管理系列:单节点故障RTO机制分析(集群状态恢复篇)& ...
- 基于Redis + Lua脚本的设计红包雨
摘要:红包雨是一个典型的高并发场景,短时间内有海量请求访问服务端,为了让系统运行顺畅,抢红包采用了基于 Redis + Lua 脚本的设计方案. 本文分享自华为云社区<红包雨中:Redis 和 ...
- 互斥锁Mutex:鸿蒙轻内核中处理临界资源独占的“法官”
摘要:本文带领大家一起剖析鸿蒙轻内核的互斥锁模块的源代码,包含互斥锁的结构体.互斥锁池初始化.互斥锁创建删除.申请释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十 互斥锁Mutex& ...
- Springboot中,如何读取配置文件中的属性
摘要:在比较大型的项目的开发中,比较经常修改的属性我们一般都是不会在代码里面写死的,而是将其定义在配置文件中,之后如果修改的话,我们可以直接去配置文件中修改,那么在springboot的项目中,我们应 ...
- 火山引擎DataLeap数据血缘技术建设实践
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 DataLeap是火山引擎数智平台VeDI旗下的大数据研发治理套件产品,帮助用户快速完成数据集成.开发.运维.治理 ...
- Solon cloud 使用融断器 sentinel 或 guava 或 semaphore
Solon Cloud 是一套防腐层的架构方案.提供统一的接口定义和配置设计,从而实现不同框架统一体验的效果. 目前,已适配的融断器有三个插件且体验方式完全相同,分别是: sentinel-solon ...
- Android 启动优化(二) - 有向无环图的原理以及解题思路
Android 启动优化(一) - 有向无环图 Android 启动优化(二) - 拓扑排序的原理以及解题思路 Android 启动优化(三) - AnchorTask 使用说明 Android 启动 ...
- 【Boost】Windows端使用 MSVC14.2 编译 Boost 并在 CMake 项目中使用
Write 2023.7.24 关于 boost 在 Windows 下的使用 gcc 安装与 CLion 的配置, 能够查到的英文资料都比较少, 踩过坑后记录一下. MinGW 安装 Boost B ...