引入问题
我的K8s环境是宿主机的hyper-v虚拟出来的,如果要映射到外面则还需要再我的宿主机上面再做一层反代,我采用的是nginx,当ingress整好之后,我从我从我腾讯云上复制了一段nginx配置放到了我的宿主机,主要配置如下:
location /test/ {
proxy_pass http://kubernetes.boychai.xyz/test/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
为了防止传入的ip是代理主机的ip我这里设置了Host、X-Real-IP、REMOTE-HOST、X-Forwarded-For。经过测试之后发现使用宿主机配置的代理访问时返回404,在宿主机上直接却没问题。
访问问题
日志
我去查看了宿主机的nginx日志、ingress-nginx-controller日志、应用程序的日志,发现除了宿主机的nginx均没有日志记录,宿主机日志信息如下
111.180.204.54 - - [25/Aug/2023:22:12:21 +0800] "GET /test/ HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
除了这一条之外其他的日志均无404。
解决
看到日志之后有点懵,因为我的宿主机是可以直接访问ingress暴露出来的服务的,而且没有报错正常访问,我是用反代之后就报错404,我最开始以为这个404就是我宿主机报的,但是宿主机的404默认页面会返回nginx的版本,如下图
而我反代返回的404页面则是这样的
一想就是我ingress返回的页面,但是我去查看ingress-nginx-controller的日志并无404的报错,日志查看命令如下,访问时并无产生记录
[root@kubernetes ~]# kubectl -n ingress-nginx logs -f ingress-nginx-controller-kc5np
我这里去尝试修改宿主机的反代配置,配置如下
location /test/ {
proxy_pass http://kubernetes.boychai.xyz/test/;
}
发现这样是可以正常访问程序的,这就奇怪了,难不成还能是因为我设置了这几个header的问题?我挨个注释这些header的配置发现问题出在下面这段配置
proxy_set_header Host $host;
具体原因也没搞清楚但是取消使用这条配置就好了...
原因
在Kubernetes的Ingress中,Host 头部用于根据不同的域名(或主机名)将请求路由到不同的服务。每个Ingress规则可以基于请求的 Host 头部将流量路由到不同的后端服务。我宿主机代理的域名和Ingress设置的域名不同,所以导致了这个问题,我外部代理的域名是tools.boychai.xyz
而我k8s设置Ingress的域名则是kubernetes.boychai.xyz
,当我在宿主机的代理设置了proxy_set_header Host $host;
这段配置之后,请求发到Ingress之后,Ingress拿到的路由请求域名则是tools.boychai.xyz
,而我又没有设置这个资源则就返回了404。
IP传递问题
日志
能够访问之后发现最终的应用拿不到真是访问的ip,这里通过nginx直接返回X-Forwarded-For头信息来查看问题出在什么位置,宿主机Nginx配置如下
location /aaa {
default_type text/html;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
return 200 "proxy_add_x_forwarded_for:$proxy_add_x_forwarded_for";
}
location /test/ {
# ingress暴露的地址`http://kubernetes.boychai.xyz/test/`
proxy_pass http://kubernetes.boychai.xyz/test/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
后端Nginx配置如下
location / {
default_type text/html;
return 200 "proxy_add_x_forwarded_for:$proxy_add_x_forwarded_for";
}
这样访问反代的/aaa
就是访问反代主机的ip,访问反代的/test
就会返回访问nginx的后端访问的ip,访问结果如下
访问/aaa
返回
proxy_add_x_forwarded_for:111.180.204.54
访问/test
返回
proxy_add_x_forwarded_for:192.16.1.1, 192.16.1.2
解决
查看访问返回的信息发现直接访问代理的ip是没问题的,那就是Ingress的锅了,这里的1.1
和1.2
依次是反代的ip和k8s主机的ip,到ingress这层没有把x_forwarded_for
头加进来,这里我去官方翻了翻文档发现了三条和x_forwarded_for
有关系的配置,如下
data:
...
compute-full-forwarded-for: "true"
# 这一条可以不加也需要知道
# forwarded-for-header: "X-Forwarded-For"
use-forwarded-headers: "true"
给ingress的cm加上这两条配置即可解决问题,最终/test
返回的内容如下
proxy_add_x_forwarded_for:111.180.204.54, 192.16.1.1, 192.16.1.2
原因
Ingress默认是没有配置传递真实IP功能的,需要配置,这三条配置和官网文档如下:
- use-forwarded-headers
文档位置: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-forwarded-headers
如果为true
,则ingress-nginx会将传入的x-forward-*传递到上游,如果是Ingress上层还有一层ingress则需要配置这一条。如果他直接暴露在公网中或者它基于L3的网络负载后门则不需要管,因为它默认就是false。 - forwarded-for-header
文档位置: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#forwarded-for-header
这个用来设置客户端来源的真实IP,默认就是X-Forwarded-For。这里不需要额外配置。 - compute-full-forwarded-for
文档位置: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#compute-full-forwarded-for
如果开启了use-forwarded-headers
的话,会发现还是没能获取到客户端的真实IP,原因是当前X-Forwaded-Fox变量是从remote_addr获取的,每次都是拿上一层的代理ip,这段配置的作用是将客户端用户访问所经过的代理ip都追加到X-Forwaded-Fox.