Rails 动态 CDN+ 负载均衡网络条件下,Rails 获取用户真实 IP 的问题

crayon · 发布于 2016年11月16日 · 最后由 embbnux 回复于 2016年11月21日 · 1789 次阅读
10804
本帖已被设为精华帖!

背景

有人刷登录、注册、短信等接口,而且量还比较大,需要根据IP,进行访问次数的限制。在没有CDN的情况下,获取用户IP还比较好办,有了动态CDN,获取到的就基本都是CDN统一出口的IP了。这时候根据IP进行访问次数限制,就非常容易造成误伤。

Rails获取用户IP

大部分情况下,以下两行代码就够用了:

request.ip
request.remote_ip

request.ip,这个很多情况下可能会取到内网IP,要看服务器机房的网络架构,但request.remote_ip肯定就能取到外网IP了。内网IP和外网IP,大家一眼就能看出来,各取所需就行。

3种由简入繁的网络请求情况

  • 无CDN无负载均衡

这种情况最好办,request.ip就拿到外网IP了;

如果拿不到,用request.remote_ip,我就用这个😅

  • 无CDN有负载均衡

这种情况,你就安心的用request.remote_ip吧😆

  • 有CDN有负载均衡

这种情况,request.remote_ip获取到的是CDN的IP,不是用户的😤

我的项目部署情况

  • Ruby 2.3.0
  • Rails 4.2.5
  • nginx 1.8.1
  • Phusion Passenger 5.0.26

用户真实IP在哪里?

我的CDN服务提供商,把用户真实IP放在下面这个请求头里:

Headers['Cdn-Src-Ip']

问一下自己的CDN服务提供商,或者用抓包工具抓一下,就可以确认了。请求头里没有的情况,我没遇到,暂时不知道如何解决,大家轻拍...😢

nginx里的处理

首先,我们要确认一下,nginx里到底有没有这个东西?打印出来看看😎

sudo vi /etc/nginx/nginx.conf

在log_format里,添加以下变量:

$http_cdn_src_ip

然后,保存配置,重启nginx,去access.log里面看看是否打印出来了

tail -100f /var/log/nginx/access.log |more

nginx里,是有用户的真实IP了;但是,Rails里是没有headers['Cdn-Src-Ip']的,想办法传过去!

Google出来的,基本都是用这个方法:

proxy_set_header

看了好多国内外文章,经历过了N次配置和重启,都无效,真是想死的心都有,在线上生产环境,每次都要配好多台服务器......😹

后来,在Passenger旧版的官方文档里,发现了相关的东西,然后又找到了新版的文档,终于解决!

Passenger 5设置额外headers的方法

passenger_set_header

nginx配置文件里,参考设置如下:

server {
    listen 80;
    server_name  192.168.0.xxx;
    root /www/project-name/production/current/public;
    rails_env production;
    passenger_enabled on;
    passenger_set_header X-Real-IP $http_cdn_src_ip;
}

保存设置,重启nginx,去Rails里看看去!

Rails里的处理

因为我设置了:

passenger_set_header X-Real-IP $http_cdn_src_ip;

所以,我在Rails里是这么取的:

request.headers['X-Real-IP']

现在,总算是拿到用户的真实IP了!👏

这个问题,搞了挺久,分享出来,希望以后的人,少走弯路。有其他解决方法的,也欢迎分享出来!

共收到 10 条回复
2 huacnlee 将本帖设为了精华贴 11月16日 09:47
2
huacnlee · #2 · 2016年11月16日 1 个赞

核心就是要 passenger_set_headerproxy_set_header

11524
gyorou · #3 · 2016年11月16日

如果是反向代理的话,proxy_set_header。 如果是passenger的话passenger_set_header呗。

10804
crayon · #4 · 2016年11月16日

#2楼 @huacnlee 是的,就是这一步卡了比较久

15999
embbnux · #5 · 2016年11月16日

也可以设置 X-Forwarded-For ,之前用 slb 也研究了一下: Rails使用负载均衡后获取用户ip

3673
limkurn · #6 · 2016年11月16日

#5楼 @embbnux 如果上游没有改过,通用情况 X-Forwarded-For 是能够解决问题的,但是上游另外设置了就得取上游设置的变量吧

4472
assyer · #7 · 2016年11月18日

类似的问题还有获取用户访问时的 scheme,之前折腾了好久才意识到在 app 层面如果不通过 referfer,其实是不知道用户访问的是 http 还是 https,也是需要在代理那边加一些特定的头部

24996
lilijreey · #8 · 2016年11月18日

主要是解决问题的思路

14099
rocLv · #9 · 2016年11月20日

 根据源代码设置X-Forwarded-For的话,request.remote_ip的值应该就是真实的ip地址。 没有环境,没法测试

9442
flowerwrong · #10 · 2016年11月20日

x-非标准,还得看代理是否支持。

15999
embbnux · #11 · 2016年11月21日

#6楼 @limkurn

#9楼 @roclv 对,比如楼主这里, 改成这样:

passenger_set_header X-Forwarded-For $http_cdn_src_ip;

然后配置一下 在 rails 里配置下 custom_proxies ,还是用 request.remote_ip 就可以拿到真实的 ip

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册