remote_addr及X-Forwarded-For及用户真实IP及内网安全
先缕一下背景,计算机网络,连在网络中的机器都有一个(或多个)IP地址,提供Web服务的机器,常有一个便于人们记忆的域名,通过DNS解析到一个(或多个)IP地址。有的网站流量大,需要不止一台机器提供服务,但域名只有一个,只能指向一台机器,怎么办呢?
代理服务器负载均衡(简称LB),提升静态网页的访问速度,增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性
互联网产品做大了,再做它个开放平台,搞完用户,搞合作伙伴。对外提供内网服务,开放平台服务器(简称OP)实现认证,代理调用内网服务。
以下,OP、LB、Server均为Nginx
Web服务器,一些场景,根据来源IP地址,决定服务与否。若是内网接口,只服务内网机器,拒绝服务来自外网的请求。
遇到的问题是,当client c通过OP请求内网接口时,Server解析客户端IP为client c的IP,其为外网IP 遂拒绝了服务。此时,我们希望Server拿到的客户端IP为OP的内网IP,这样就能正常为OP代理的客户服务了。怎么做呢?
HTTP协议虽然定义了Client-Ip请求头,但不是安全的,服务器的做法是取TCP连接的对端IP作为client ip,在Nginx中,解析为变量$remote_addr,不架设代理、负载均衡时,直接拿来用即可。但当在Server前代理时,应利用Nginx的ngx_http_realip_module中的real_ip_header指令,指定用哪个请求header的值来替换默认的$remote_addr。
假设
client a IP:12.34.56.78
OP IP: 34.56.78.90 及内网IP 10.22.33.44
LB IP: 10.23.34.45
这时,我们考察 X-Forwarded-For 的定义及格式,对于上图Server接收来自client c的请求时,X-Forwarded-For首先由OP设置为client c的IP,即
proxy_set_header X-Forwarded-For $remote_addr;
LB接收此header时,应再次设置
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
此时,到达Server的信息应该是:
$remote_addr: 10.23.34.45
X-Forwarded-For: 12.34.56.78,10.22.33.44
注意,Server知道Load Balance的存在,所以不关心$remote_addr的默认值,关心的是真正的请求IP,此时可设置
real_ip_header
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive off;
关于real_ip_recursive指令,官方文档:
If recursive search is disabled, the original client address that
matches one of the trusted addresses is replaced by the last address
sent in the request header field defined by the real_ip_header
directive. If recursive search is enabled, the original client address
that matches one of the trusted addresses is replaced by the last
non-trusted address sent in the request header field.
设置off时,Server得到的$remote_addr为10.22.33.44 即开放平台的IP,正常服务!
注:RFC 7239 定义了一个新的 Forwarded header,更完备地组织这些信息。