k8s中ingress以及nginx获取客户端真实IP
1.背景信息
公司游戏官网的项目,集群使用ingress开放出去,后端由于是php语言编写,所以在php的pod里面也需要一个nginx来开放连接。所以本次的路由顺序就如以下:
腾讯云LB → ingress → nginx
所以本次环境,ingress和nginx都需要获取客户端的真实IP。所以本篇文档还是主要讲解一下使用方式和注意事项。
2.基本概念
以上讲解了ingress和nginx获取真实IP的方式,这里还是简单的了解一下基本概念。
(1)remote_addr
代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的
当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器(Nginx,Apache等)
就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站
这样web服务器就会把remote_addr设为这台代理机器的IP,除非代理将你的IP附在请求header中一起转交给web服务器
(2)X-Forwarded-For(简称XFF)
X-Forwarded-For 是一个 HTTP 扩展头部,HTTP协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入
用来表示 HTTP 请求端真实 IP,如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用
并被写入 RFC 7239(Forwarded HTTP Extension)标准之中
XFF的格式为X-Forwarded-For: client, proxy1, proxy2
XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP
(注意:如果未经严格处理,可以被伪造)
如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0
那么按照 XFF 标准,服务端最终会收到以下信息
X-Forwarded-For: IP0, IP1, IP2
Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求
列表中并没有 IP3,IP3 可以在服务端通过 Remote Address 字段获得
(3)X-Real-IP
这又是一个自定义头部字段,通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP
这个设备可能是其他代理,也可能是真正的请求端,这个要看经过代理的层级次数或是是否始终将真实IP一路传下来
(注意:如果未经严格处理,可以被伪造)
3.Ingress 获取客户端真实IP
通常,用户ip的传递依靠的是X-Forwarded-*参数。但是默认情况下,ingress是没有开启的。
ingress的文档还比较详细,这里介绍一下可能用到的这3个参数:
ingress官方文档: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-forwarded-headers
以上有三个比较重要的参数。下面一一解释
3.1 use-forwarded-headers
- 如果Nginx在其他7层代理或负载均衡后面,当期望Nginx将X-Forwarded-*的头信息传递给后端服务时,则需要将此参数设为true
- 如果设为false(默认为false),Nginx会忽略掉X-Forwarded-*的头信息。false设置适用于Nginx直接对外或前面只有3层负载均衡的场景
由于ingress的主配置是从configmap中获取的,更新参数则需要修改名为nginx-configuration的configmap的配置:在data配置块下添加use-forwarded-headers: "true"
修改后,ingress nginx会自动加载更新nginx.conf主配置文件。下图为更新前后配置文件变化对比:
注意:左边为开启use-forwarded-headers后ingress nginx主配置文件,右边为开启前
3.2 forwarded-for-header
用来设置识别客户端来源真实ip的字段,默认是X-Forwarded-For。如果想修改为自定义的字段名,则可以在configmap的data配置块下添加:forwarded-for-header: "THE_NAME_YOU_WANT"。通常情况下,我们使用默认的字段名就满足需求,所以不用对这个字段进行额外配置。当然,你想显示的配置也可以。
3.3 compute-full-forwarded-for
如果只是开启了use-forwarded-headers: "true"的话,会发现还是没能获取到客户端来源的真实ip,原因是当前X-Forwarded-For变量是从remote_addr获取的值,每次取到的都是最近一层代理的ip。为了解决这个问题,就要配置compute-full-forwarded-for字段了,即在configmap的data配置块添加:compute-full-forwarded-for: "true"。其作用就是,将客户端用户访问所经过的代理ip按逗号连接的列表形式记录下来。
待ingress nginx加载configmap并更新主配置文件后,对比更新前后变化如下:
注:左边是未开启compute-full-forwarded-for配置的ingress nginx主配置文件,右边是开启了的
3.4 举例说明
如果从客户端ip0发起一个HTTP请求到达服务器之前,经过了三个代理proxy1、proxy2、proxy3,对应的ip分别为ip1、ip2、ip3,那么服务端最后得到的X-Forwarded-For值为:ip0,ip1,ip2。列表中并没有ip3,ip3可以在服务端通过remote_addr来获得。这样应用程序通过获取X-Forwarded-For字段的第一个ip,就可以得到客户端用户真实ip了。
3.5 注意项
值得注意的是,并不是所有的场景都能通过X-Forwarded-For来获取用户正式ip。
比如,当服务器前端使用了CDN的时候,X-Forwarded-For方式获取到的可能就是CDN的来源ip了,
这种情况,可以根CDN厂商约定一个字段名来记录用户真实ip,然后代理将这个字段逐层传递,最后到服务端。
4.Nginx获取客户端真实IP
先讲解ingress在讲解nginx原因是因为,在架构图里面,nginx在ingress后面,这里有一个非常需要注意的点就是,如果ingress不先拿到客户端的真实IP,nginx不管怎么配置都拿不到,因为在ingress那一层就没有真实的客户端IP了。往后面nginx传递的也就没有真实IP了。
4.1 配置
#放在http模块等,个人比较喜欢放在http set_real_ip_from 192.168.1.0/24; #真实服务器上一级代理的IP地址或者IP段,可以写多行。 所以这里一般都是写成CDN地址 real_ip_header X-Forwarded-For; #从哪个header头检索出所要的IP地址。
#最主要是这条,这条不加,remote_ip字段就是没办法改成真实ip
#各大文章各种解释,官方文档翻译的也不好
#官方文档:http://nginx.org/en/docs/http/ngx_http_realip_module.html
#所以这里就用自己理解的方式来解释一下吧
-
off
:会将real_ip_header
指定的HTTP头中的最后一个IP作为真实IP -
on
:会将real_ip_header
指定的HTTP头中的最后一个不是信任服务器的IP当成真实IP
假设2个ip, 顺序为 1.1.1.1,192.168.1.1
如果为off,那么 192.168.1.1为真实ip,192.168.1.1是cdn的ip,很显然就获取错误
如果为on,那么最后一个不是信任服务器的ip就是1.1.1.1,因为只有2个ip,而且实际上nginx日志也是按照这个顺序记录的,那么192.168.1.1是信任ip,是cdn的地址,那么只有1.1.1.1是用户ip
所以如果前端为cdn,set_real_ip_form设置成所有cdn网段就行,如果不为了安全,设置成为0.0.0.0/0,那么都是信任IP,那么就回会把X-Forwarded-For中的ip从右到左和set_real_ip_from去对比,取到最右的一个不信任ip作为真实ip。如果全是信任的,那么就是从右到左最后一个ip,那就是1.1.1.1
real_ip_recursive on; #递归的去除所配置中的可信IP。排除set_real_ip_from里面出现的IP。如果出现了未出现这些IP段的IP,那么这个IP将被认为是用户的IP。
Nginx使用以上的配置就可以了
参考:https://www.cnblogs.com/yudai/p/10974444.html