Rails 请问有人遇到在内网开发时,rails 通过反向代理运行在 nginx 后端,调用 request.remote_ip 无法获取客户端 IP 或者报错吗?

zhangyuan · 2013年01月26日 · 最后由 strfory 回复于 2016年10月20日 · 15457 次阅读

由于开发时,要同时运行多个项目,并且都在 80 端口。所以项目在 3000 端口启动,然后通过 Nginx 做反向代理。客户端请求 nginx 即可。

但是在 controller 里调用 request.remote_ip 得到的 IP 是 127.0.0.1,而不是客户端 IP。于是我在 Nginx 里设置了 HTTP_CLIENT_IP 头为客户端 IP 后,就会抛异常 IpSpoofAttackError,报错:IP spoofing attack。

这只是在内网出的问题。我使用同样的 Nginx 配置,并在公网里的项目里,调用 request.remote_ip 能正常返回客户端 IP。

细节如下:

Nginx 这样配置的

server {
  listen 80;
  server_name abc.com;
    location / { 
    proxy_redirect     off;
    proxy_set_header   Host $host;
    proxy_set_header   X-Forwarded-Host $host;
    proxy_set_header   X-Forwarded-Server $host;

    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_buffering    on; 

    if (!-f $request_filename) {
      proxy_pass http://127.0.0.1:3000;
      break;
    }   
  }
}

后来找到这个问题 http://stackoverflow.com/questions/10710486/wrong-ip-address-with-nginx-unicorn-rails ,添加了一行命令

proxy_set_header   CLIENT_IP $remote_addr;

接下来就报错了。抛出 IpSpoofAttackError 异常,提示 IP spoofing attack?,给打印出了 HTTP_CLIENT_IP 和 HTTP_X_FORWARDED_FOR。对应 rails 源码中的 https://github.com/rails/rails/blob/3-2-stable/actionpack/lib/action_dispatch/middleware/remote_ip.rb 文件。

读了一下源码,发现是 rails 会根据 HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR 或者 REMOTE_ADDR 来得到 remote_ip。

第一种情况 Nginx 没有设置 CLIENT_IP 时,rails 拿到的 HTTP_CLIENT_IP 为nil,就从 HTTP_X_FORWARDED_FOR 里找,但找之前,会处理一下,去掉 HTTP_X_FORWARDED_FOR 里的内网 IP(客户端的 IP 是 192.168.x.x),所以在 HTTP_X_FORWARDED_FOR 里还是找不到。只能在 REMOTE_ADDR 找了。而 REMOTE_ADDR 返回是 127.0.0.1。

第二种情况 Nginx 设置了 CLIENT_IP,rails 拿到的 HTTP_CLIENT_IP 是 192.168.x.x。而这个 IP,并不在处理后的 HTTP_X_FORWARDED_FOR 里,所以报错了。

没找到有效的解决办法,我只能在 development.rb 里配置

config.action_dispatch.ip_spoofing_check = false

因为不想在生产环境里关闭这个检查,所以不想在 production.rb 里增加这个配置。但是,在内网用 production.rb 测试时,又会遇到错误。

请问大家有遇到这个情况吗?该如何解决呢?

编译 nginx 时加上 --with-http_realip_module

real_ip_header X-Forwarded-For;

proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

还一种配置方法:配置两个 server

server { listen 127.0.0.1:8081; server_name 127.0.0.1; location / { passenger_use_global_queue on; passenger_enabled on; rails_env production; root /var/sites/a/public; } }

server { listen 80; server_name abc.com;

real_ip_header X-Forwarded-For; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

location / { proxy_pass http://127.0.0.1:8081;
} }

你输入的地址是 localhost:3000 或者 127.0.0.1:3000 访问的?
输入服务器 ip:端口试试

或者把 ip 地址配置到 hosts 文件中

顺便说下这跟 Nginx 应该没多大关系吧。是由 Rack 封装的

为啥不直接用 request.headers['X-Real-IP'] 呢?

#2 楼 @Ddl1st 目前的环境是必须通过 Nginx 来访问。不经过 ngxin 访问,肯定没问题的。

#3 楼 @luikore request.headers['X-Real-IP'] 是可以的。但是,如果没有反向代理,request.headers['X-Real-IP'] 就是空的。

现在希望在 rails 写一份代码,就能得到客户端 IP,不管前面有没有反向代理或者其他前端其他设施(比如 Nginx、Varnish、甚至 CDN)

#4 楼 @zhangyuan

proxy_pass http://192.168.x.x:3000

这个可以?

#5 楼 @Ddl1st 不可以。这样得到的 remote_ip,依然是反向代理的 IP,不是客户端的 IP。

问题基本集中在,HTTP_CLIENT_IP 为空时,rails 把 HTTP_X_FORWARDED_FOR 里把包括客户端在内的内网 IP 都过滤了。所以在 HTTP_X_FORWARDED_FOR 里找不到 IP 了。只能在 REMOTE_ADDR 找。而 REMOTE_ADDR 一般都是反向代理的 IP。这里就是服务的 IP 或者 127.0.0.1 了。

不过比较奇怪,在办公室其他服务器试了试,还不知道有什么魔法效果,居然 REMOTE_ADDR 里是客户端的真实 IP。

#4 楼 @zhangyuan request.headers['X-Real-IP'] || request.remote_ip ?

#7 楼 @luikore 问题不是 X-Real-Ip 的问题。

而是 因为是内网测试

HTTP_X_FORWARDED_FOR 里保存的是 内网 IP,被 Rails 过滤掉了。

就只能取 REMOTE_ADDR 了。

其实楼主这样部署,放在公网上是没有问题的。

好吧..今天碰到跟楼主一样的问题。

参考下 rails #5523

  • Rails 可以配置 config.action_dispatch.trusted_proxies
  • Rack 要在 initializers 里面给 Rack::Request#trusted_proxy?(ip)打 monkey patch

#9 楼 @ruohanc 😆 我们运维的同学,使用了 http_realip_module 这个 nginx 模块 http://wiki.nginx.org/HttpRealipModule 。指定一个网段,它会把 REMOTE_ADDR 设置为客户端的 IP。

#1 楼 @xue98 除了 real_ip_header,还要用 set_real_ip_from 指令设置网段。见文档 http://wiki.nginx.org/HttpRealipModule

#10 楼 @zhangyuan 为啥觉得方向不对.? 你们直接给 real_ip_header 设置的是 REMOTE_ADDR 这个头.?

Rails 和 Rack 都没有去取 X-Real-IP 头的代码。

而且你如果就设置的是 REMOTE_ADDR 的话,会不会只支持一层代理转发?前端再加上一个 LB 会咋样..?

我这边也遇到类似的问题,环境是 nginx/1.7.5 + PUMA + Rails 4.1.5,不过触发的原因不同,因为 UC 浏览器极速模式,服务器端压缩触发 IpSpoofAttackError。 解决方式是:location / { proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header CLIENT_IP $remote_addr; proxy_pass http://www.blog-eye.com; }

我的部署环境是 nginx + thin + rails 4.2.6,也是通过 Nginx 反向代理后,通过 request.remote_ip 获取为 127.0.0.1,解决方法为:在 nginx.conf 中添加: proxy_set_header X-Forwarded-For $remote_addr; 在 config/application.rb 中配置: require 'ipaddr' config.action_dispatch.trusted_proxies = %w(127.0.0.1 ::1 fc00::/7 10.0.0.0/8).map { |proxy| IPAddr.new(proxy) }

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