nginx利用反向代理实现获取用户真实ip

以下所有的实操都是在redhat7.3上


我们访问互联网上的服务时,大多数时,客户端并不是直接访问到服务端的,而是客户端首先请求到反向代理,反向代理再转发到服务端实现服务访问,通过反向代理实现路由/负载均衡等策略。这样在服务端拿到的客户端IP将是反向代理IP,而不是真实客户端IP,因此需要想办法来获取到真实客户端IP

web服务器获得真正的用户和真实的ip ?
客户端访问服务端的数据流走向

172.25.0.1(client)->192.168.0.1(拨号
ADSL)->cdn(10.0.0.1)->slb(阿里云11.0.0.1)(反向代理)(4层负载均衡)–>->12.0.0.1(server(nginx)(真实的服务器))
server1 服务器
server2 反向代理
客户端 测试

可以看出,服务端根本获取不到真实的客户端ip,只能获取到上一层服务的ip,那么nginx怎样才能获取到真实的ip呢?

反向代理:获取上一节的ip,一层一层会找到真实client的ip;

问题背景:

在实际应用中,我们可能需要获取用户的ip地址,比如做异地登陆的判断,或者统计ip访问次数等,通常情况下我们使用request.getRemoteAddr()就可以获取到客户端ip,但是当我们使用了nginx作为反向代理后,使用request.getRemoteAddr()获取到的就一直是nginx服务器的ip的地址,那这时应该怎么办?
part1:解决方案

我在查阅资料时,有一本名叫《实战nginx》的书,作者张晏,这本书上有这么一段话“经过反向代理后,由于在客户端和web服务器之间增加了中间层,因此web服务器无法直接拿到客户端的ip,通过 r e m o t e a d d r 变 量 拿 到 的 将 是 反 向 代 理 服 务 器 的 i p 地 址 ” 。 这 句 话 的 意 思 是 说 , 当 你 使 用 了 n g i n x 反 向 服 务 器 后 , 在 w e b 端 使 用 r e q u e s t . g e t R e m o t e A d d r ( ) ( 本 质 上 就 是 获 取 remote_addr变量拿到的将是反向代理服务器的ip地址”。这句话的意思是说,当你使用了nginx反向服务器后,在web端使用request.getRemoteAddr()(本质上就是获取 remoteaddrip使nginxweb使request.getRemoteAddr()remote_addr),取得的是nginx的地址,即 r e m o t e a d d r 变 量 中 封 装 的 是 n g i n x 的 地 址 , 当 然 是 没 法 获 得 用 户 的 真 实 i p 的 , 但 是 , n g i n x 是 可 以 获 得 用 户 的 真 实 i p 的 , 也 就 是 说 n g i n x 使 用 remote_addr变量中封装的是nginx的地址,当然是没法获得用户的真实ip的,但是,nginx是可以获得用户的真实ip的,也就是说nginx使用 remoteaddrnginxipnginxipnginx使remote_addr变量时获得的是用户的真实ip,如果我们想要在web端获得用户的真实ip,就必须在nginx这里作一个赋值操作,如下:

proxy_set_header X-real-ip $remote_addr;

其中这个X-real-ip是一个自定义的变量名,名字可以随意取,这样做完之后,用户的真实ip就被放在X-real-ip这个变量里了,然后,在web端可以这样获取:

request.getAttribute(“X-real-ip”)

part2:原理介绍:
关于nginx反向代理的原理的介绍
proxy_set_header原理

用一台服务器server1模拟实现获取本机ip

在server1上:

[root@server1 ~]# tar zxf nginx-1.17.0.tar.gz
[root@server1 ~]# cd nginx-1.17.0
[root@server1 nginx-1.17.0]# yum install -y zlib-devel# 这是编译nginx的依赖包
[root@server1 nginx-1.17.0]# yum install -y openssl-devel  # 这是编译nginx的依赖
[root@server1 ~]# cd /usr/local/nginx/

[root@server1 conf]# vim nginx.conf
#当访问域名server1.example.com,实际访问的是直接返回client real ip: $remote_addr\n
配置文件-->虚拟主机
  server {
        listen 80;   #监听80端口
        server_name server1.example.com;  #定义域名
        set_real_ip_from 172.25.70.1;   #真正的ip地址;
        real_ip_header  X-Forwarded-For;
        real_ip_recursive on;
        location / {
                return 200 "client real ip: $remote_addr\n";
        }
}
[root@server1 conf]#/usr/local/nginx/sbin/nginx -t #启动失败;
[root@server1 conf]#  systemctl stop nginx
添加一个模块:-with-http_realip_module选项(后台Nginx服务器记录原始客户端的IP地址 )
[root@server1 nginx-1.17.0]# ./configure --prefix=/usr/local/nginx  --with-file-aio  --with-http_realip_module 
************************
--with-file-aio 启用file aio支持(一种APL文件传输格式)
--with-http_realip_module:它的意义在于能够使得后台服务器记录原始客户端的IP地址。
*************************

[root@server1 nginx-1.17.0]#  make && make install

[root@server1 nginx-1.17.0]# vim /usr/local/nginx/conf/nginx.conf

参数解释:

1.使用realip模块后,$remote_addr输出结果为真实客户端IP,可以使用$realip_remote_addr获取最后一个反向代理的IP; 
3.real_ip_headerX-Forwarded-For:告知Nginx真实客户端IP从哪个请求头获取; 
4.set_real_ip_from 172.25.78.0/24:告知Nginx哪些是反向代理IP,即排除后剩下的就是真实客户端IP
5.real_ip_recursive on:是否递归解析,当real_ip_recursive配置为off时,Nginx会把real_ip_header指定的请求头中的最后一个IP作为真实客户端IP;
当real_ip_recursive配置为on时,Nginx会递归解析real_ip_header指定的请求头,最后一个不匹配set_real_ip_from的IP作为真实客户端IP。 

添加虚拟机从X-Forwarded-For中获取到真实客户端IP(最后一行):

132   server {
133         listen 80;
134         server_name server1.example.com;
135         set_real_ip_from 172.25.70.1; ###set_real_ip_from:  功能:通过该指令指定信任的地址,将会被替代为精确的IP地址。从0.8.22版本后也可以使用信任的Unix套接字。这里设置的IP就是指前端Nginx 、Varnish或者 Squid 的 IP地址。
136         real_ip_header  X-Forwarded-For;  # real_ip_header::这个指令用于设置使用哪个头来替换IP地址。如果使用了X-Forwarded-For,那么该模块将会使用X-Forwarded-For头中的最后一个IP地址来替换前端代理的IP地址。
137         real_ip_recursive on;  # 是否递归解析,off表示默认从最后一个地址开始解析; # on表示从前往后依次递归获取ip
138         location / {
139                 return 200 "client real ip: $remote_addr\n"; # $remote_addr 代表客户端真实IP
140         }
141 
142 
143 }

nginx利用反向代理实现获取用户真实ip_第1张图片

[root@server1 nginx-1.17.0]# cd /usr/local/nginx/conf/
[root@server1 conf]#  systemctl restart nginx.service
[root@server1 conf]# vim /etc/hosts
172.25.70.1     server1  server1.example.com
[root@server1 conf]# curl -H 'X-Forwarded-For:1.1.1.1,172.25.70.1' server1.example.com
client real ip: 1.1.1.1     #虚拟主机添加成功

在这里插入图片描述
[root@server1 conf]# scp -r /usr/local/nginx/ server2: #将其nginx的安装路径发过去

以上操作仅仅实现了获取本机ip和使用X-Forwarded-For的功能,现在我们来模拟实际生产中在有反向代理的阻碍下获取真实客户端ip,这里我们用一个反向代理来模拟

server1 后端服务器
server2 nginx反向代理服务器

配置server2(nginx反向代理服务器):

[root@server2 nginx]# cd /usr/local/nginx/
[root@server2 nginx]# ls
[root@server2 nginx]# id nginx  #查看是否有nginx用户,没有重新建立;
uid=1000(nginx) gid=1000(nginx) groups=1000(nginx)
[root@server2 nginx]# cd conf/
[root@server2 conf]# vim nginx.conf

 19         upstream westos{
 20                 server 172.25.70.1:80;
 21         }

135   server {
136         listen 80;
137         server_name server1.example.com;
138         set_real_ip_from 172.25.70.1;
139         real_ip_header  X-Forwarded-For;
140         real_ip_recursive on;
141         location / {
142 
143                 proxy_pass http://westos;
144         }
145 }

nginx利用反向代理实现获取用户真实ip_第2张图片

[root@server2 conf]# ../sbin/nginx
[root@server2 conf]# netstat -tnlp
[root@server2 conf]# curl localhost
server2.example.com

nginx利用反向代理实现获取用户真实ip_第3张图片

#客户端(70)测试:

[root@foundation70 ~]# curl 172.25.70.2
server2.example.com

——————————————————————————————————————————————————————————————————————
在server2上:
[root@server2 conf]# vim nginx.conf
135 server {
136 listen 80;
137 server_name server2.example.com;
138 # set_real_ip_from 172.25.70.1;
139 # real_ip_header X-Forwarded-For;
140 # real_ip_recursive on;
141 location / {
142
143 proxy_pass http://westos;
144 }
145 }
nginx利用反向代理实现获取用户真实ip_第4张图片


[root@server2 conf]# curl 172.25.70.1
server1.example.com
[root@server2 conf]# ../sbin/nginx -s reload

在客户端测试

[root@foundation70 ~]# vim /etc/hosts
172.25.70.2     server2 server2.example.com
[root@foundation70 ~]# curl server2.example.com
server2.example.com
[root@foundation70 ~]# curl server2.example.com
server1.example.com

————————————————————————————————————
获取真正的客户端ip

在server1上:
[root@server1 conf]# vim nginx.conf
注释掉131-140行
 44         set_real_ip_from 172.25.70.2;
 45         real_ip_header  X-Forwarded-For;
 46         real_ip_recursive on;

nginx利用反向代理实现获取用户真实ip_第5张图片

[root@server1 conf]# ../sbin/nginx -s reload
在server2上:
[root@server2 conf]# vim nginx.conf
 19         upstream westos{
 20                 server 172.25.70.1:80;
 21         }

135   server {
136         listen 80;
137         server_name server2.example.com;
138 #       set_real_ip_from 172.25.70.1;
139 #       real_ip_header  X-Forwarded-For;
140 #       real_ip_recursive on;
141         location / {
142                 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     #添加该变量是为了真正的获得用户的IP
143                 proxy_pass http://westos;
144         }
145 }
[root@server2 conf]# ../sbin/nginx -t
[root@server2 conf]# ../sbin/nginx -s reload

测试:


[root@foundation70 ~]# curl server2.example.com(172.25.70.2)
server1.example.com

在server1上的日志:
[root@server1 logs]# cat access.log     #是真正访问的是客户端
172.25.70.250 - - [26/Sep/2019:22:05:25 +0800] "GET / HTTP/1.0" 200 20 "-" "curl/7.29.0"  #获得ip是客户端的

——————————————

获得是server1的获得了server2(反向代理)的ip

在server1上:

[root@server1 conf]# vim nginx.conf
 44 #       set_real_ip_from 172.25.70.2;
 45 #        real_ip_header  X-Forwarded-For;
 46 #        real_ip_recursive on;
再次测试:
[root@foundation70 ~]# curl server2.example.com
server1.example.com

在server1上的日志:
[root@server1 logs]# cat access.log 
172.25.70.2 - - [26/Sep/2019:22:14:09 +0800] "GET / HTTP/1.0" 200 20 "-" "curl/7.29.0"

在这里插入图片描述

你可能感兴趣的:(Linux运维进阶)