先说一下故事背景:nginx前端有一个阿里云SLB,然后外界从443进入SLB,SLB将流量转到nginx的80端口,同时80做了一个反向代理到同机器的8082端口服务。8082服务的配置文件如下:
但是这样的配置发现了一个问题:虽然已经allow 127.0.0.1
,但是在浏览器里还是能正常打开/status
界面,如图:
原因是deny和allow后面跟着的IP都是$remote_addr
,而我这个环境里后端8082的服务,看到的$remote_addr
就是127.0.0.1
,所以才会出现这样子。那么如何解决呢?
一般来说遇到这种有代理的情况,我们都是用http_x_forwarded_for
来获取后端的真实IP,但是如何让deny和allow去搭配http_x_forwarded_for
呢?用real_ip_header
。
首先先nginx -V
查看一下是否有--with-http_realip_module
的模块,如果没有,那么需要重新编译nginx,如下:
1
2./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-threads --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_spdy_module --with-cc-opt='-O2 -g'
make && make install
然后将上面文件的status|ping
那段location
改成如下:
1
2
3
4
5
6
7
8
9
10
11location ~ ^/(status|ping)$ {
real_ip_header X-Forwarded-For;
set_real_ip_from 0.0.0.0/0;
real_ip_recursive on;
allow 准许访问的IP地址1;
allow 准许访问的IP地址2;
allow 准许访问的IP地址3;
deny all;
fastcgi_pass 127.0.0.1:9000;
include fastcgi.conf;
}
解释一下语法:
real_ip_header X-Forwarded-For
表示从哪个header头检索出要的IP地址;set_real_ip_from
真实服务器上一级代理的IP地址或者IP段,不过经过我的反复试验,发现这个代理地址无法确定。不过我胆子大,写了0.0.0.0/0
,这代表所有转发的IP都会被承认;real_ip_recursive
的用途:递归的去除所配置中的可信IP,排除set_real_ip_from
里面出现的IP。如果出现了未出现这些IP段的IP,那么这个IP将被认为是用户的IP;
重启nginx之后,就OK了。
不过问题还是有两个:
http_x_forwarded_for
是可以伪造的,因为这个值是通过获取HTTP头的X_FORWARDED_FOR
属性取得,那有没有比它更准确的方法?set_real_ip_from
后面填写的IP究竟是不是http_x_forwarded_for
多个IP后面的值?