最近在将一个业务流量接入网关时遇到一个问题,本来应该是https请求,经由网关到应用服务器得到重定向url时变成了http请求。问了相关同事,发现他们是根据X-Forwarded-Proto头来判断协议进行转换的。流量从入口nginx到网关再到应用服务器时,入口nginx的XFP头为https,网关把X-Forwarded-Proto协议头覆盖掉了。于是就看了一下kong的XFP头处理逻辑。

kong的nginx_template中发往upstream的location中有这么一句话

proxy_set_header   X-Forwarded-Proto $upstream_x_forwarded_proto;

说明网关传往应用服务器时带了XFP头,不过带的是http,然后再看一下kong是怎么修改upstream_x_forwarded_proto变量的,在kong/core/hander.lua中,

local realip_remote_addr = var.realip_remote_addr
local trusted_ip = singletons.ip.trusted(realip_remote_addr)
 ......
 if trusted_ip then
   var.upstream_x_forwarded_proto = var.http_x_forwarded_proto or
                                    var.scheme

   var.upstream_x_forwarded_host  = var.http_x_forwarded_host  or
                                    var.host

   var.upstream_x_forwarded_port  = var.http_x_forwarded_port  or
                                    var.server_port

 else
   var.upstream_x_forwarded_proto = var.scheme
   var.upstream_x_forwarded_host  = var.host
   var.upstream_x_forwarded_port  = var.server_port
 end

kong先判断realip_remote_addr是否在可信ip范围内,是的话才会将XFP头设置为从上方带过来的XFP头,否则的话就用var.scheme,由于网关这里没有设置trust_ip,而入口nginx到网关采用的协议为http,从而导致网关将XFP设置为了http。找到了地方,那么我们只需要设置一下trust_ip就ok。trust_ip是在kong.conf文件中设置的。由于kong用的nginx原生的可信Ip模块,所以我们可以参照nginx原生的配置,
http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive 这个是nginx的real_ip模块文档。

#kong.conf文件
trusted_ips = 192.168.0.1/16, 10.0.0.1/8, 127.0.0.1                  # Defines trusted IP addresses blocks that are

#real_ip_header = X-Real-IP      # Defines the request header field whose value


#real_ip_recursive = off         # This value sets the ngx_http_realip_module
  • trusted_ips 决定kong是否会从XFF头或者 X-Real-IP头中获取客户端ip(也决定了是否使用传过来的XFP头),为了安全,只会从可信IP中传过来的请求才会取这个头

  • real_ip_header 这个决定从哪里获取客户端ip

  • real_ip_recursive real_ip_recursive 是否递归地排除直至得到用户ip(默认为off)

首先,real_ip_header 指定一个http首部名称,默认是X-Real-Ip,假设用默认值的话,nginx在接收到报文后,会查看http首部X-Real-Ip。

(1)如果有1个IP,它会去核对,发送方的ip是否在set_real_ip_from指定的信任ip列表中。如果是被信任的,它会去认为这个X-Real-Ip中的IP值是前代理告诉自己的,用户的真实IP值,于是,它会将该值赋值给自身的$remote_addr变量;如果不被信任,那么将不作处理,那么$remote_addr还是发送方的ip地址。

(2)如果X-Real-Ip有多个IP值,比如前一方代理是这么设置的:proxy_set_header X-Real-Ip $proxy_add_x_forwarded_for;

得到的是一串IP,那么此时real_ip_recursive 的值就至关重要了。nginx将会从ip列表的右到左,去比较set_real_ip_from 的信任列表中的ip。如果real_ip_recursive为off,那么,当最右边一个IP,发现是信任IP,即认为下一个IP(右边第二个)就是用户的真正IP;如果real_ip_recursive为on,那么将从右到左依次比较,知道找到一个不是信任IP为止。然后同样把IP值复制给$remote_addr。

在本次的问题中,只需要给trusted_ips设置一下信任Ip列表即可。