2020年 · Linux

Nginx在多层代理下获取真实客户端IP地址(一)

需求:Nginx代理到后端Nginx代理WEB站点,后端WEB站点需要获取到真实client访问IP地址。
以下为Nginx安装版本、系统环境和安装模块参数(with-http_realip_module需要增加的模块):
~# nginx -V
nginx version: nginx/1.16.0
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
built with OpenSSL 1.1.1c 28 May 2019
TLS SNI support enabled
configure arguments: –prefix=/usr/local/nginx –user=www –group=www –with-http_stub_status_module –with-http_v2_module –with-http_ssl_module –with-http_gzip_static_module –with-http_realip_module –with-http_flv_module –with-http_mp4_module –with-openssl=../openssl-1.1.1c –with-pcre=../pcre-8.43 –with-pcre-jit –with-ld-opt=-ljemalloc
访问的WEB是一个nginx+php的站点,增加一个测试的php页面:
<?php
foreach($_SERVER as $k=>$v)
echo $k . ‘=’ . $v . ‘<br />’;
修改所有主机nginx.conf http模块中日志格式为:
log_format main ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘$status $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘;
场景一(最简单的场景):

server {
listen 80;
server_name blog.ohyeahwoo.com;
access_log /data/wwwlogs/blog.ohyeahwoo.com_nginx.log main;
error_log /data/wwwlogs/blog.ohyeahwoo.com_error_nginx.log error;
index index.html index.htm index.php;
root /data/blog.ohyeahwoo.com;
location ~ [^/]\.php(/|$) {
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}}
fastcgi.conf文件配置如下:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for;    ##新增此行

# PHP only, required if PHP was built with –enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;

在客户机192.168.31.99上修改host: 192.168.31.151 blog.ohyeahwoo.com
在客户机上打开浏览器访问http://blog.ohyeahwoo.com,看到的结果是:
REMOTE_ADDR=192.168.31.99
HTTP_X_FORWARDED_FOR=192.168.31.99

结果描述:REMOTE_ADDR和HTTP_X_FORWARDED_FOR都是真实客户端IP地址192.168.31.151。

场景二(增加一台Nginx反向代理):

在服务器nginx proxy(192.168.31.100)上nginx的vhost配置:
upstream realip_80 {  server blog.ohyeahwoo.com:80; }
server {
listen 80;
server_name blog.ohyeahwoo.com ;
access_log /data/wwwlogs/blog.ohyeahwoo.com_nginx.log main;
location / {
proxy_pass http://realip_80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
include fastcgi_params;
}}
只需要确定 fastcgi_params中是否有:
fastcgi_param REMOTE_ADDR $remote_addr;

在客户机192.168.31.99上修改host: 192.168.31.100 blog.ohyeahwoo.com
在客户机上打开浏览器访问http://blog.ohyeahwoo.com,看到的结果是:
REMOTE_ADDR=192.168.31.100
HTTP_X_FORWARDED_FOR=192.168.31.99, 192.168.31.100
HTTP_X_REAL_IP=192.168.31.99

结果描述
1.REMOTE_ADDR变成了反向代理192.168.31.100的IP地址
2.HTTP_X_FORWARDED_FOR记录了真实客户端IP和反向代理IP,以逗号分隔。
3.新出现的HTTP_X_REAL_IP也是真实客户端IP地址,是由192.168.31.100的upstream realip_80配置写入的

==============================================
结论:在默认配置情况下,如果要取得客户端真实IP地址的话,只有取HTTP_X_FORWARDED_FOR的第一个逗号前的IP地址最靠谱,其他的地址都有可能被重写,此方法一般是后端应用需要获取客户的IP地址时,比较靠谱。
参考:https://www.cnblogs.com/harryc/p/6361892.html