我如何重新定义Ruby和Rails request.ip和request.remote_ip方法?

问题描述 投票:2回答:3

我有一个Rails应用程序,我正在尝试重载request.remote_ip和request.ip以便使用cloudflare标头(HTTP_CF_CONNECTING_IP)...我已经尝试过了,但是它们都不起作用:

module Rack
  class Request
    class << self
      def ip
        @ip ||= (@env['HTTP_CF_CONNECTING_IP'] || super)
      end
    end
  end
end

module ActionDispatch
  class Request < Rack::Request
    class << self
      def remote_ip
        @remote_ip ||= (@env['HTTP_CF_CONNECTING_IP'] || super)
      end
    end
  end
end

我不能使用类似的额外方法

def connecting_ip
  @env['HTTP_CF_CONNECTING_IP'] || request.remote_ip
end

在application_controller中,因为我还有其他一些使用request.ip的工具(例如devise)

谢谢!

ruby-on-rails ruby rack
3个回答
7
投票

我相信request是一个实例。但是,您正在定义类方法。删除class << self废话,您将重新定义实例方法。

不过只是一个音符,听起来有点疯狂。小心点框架并不总是喜欢重新排列胆量。


使用实例方法时,您的错误消息表示发生了其他事情。 super调用超类实现。但是,当您重新打开一个类并覆盖所有内容时,实际上是在覆盖原始实现。由于该方法在超类中不存在,因此super不起作用。

相反,您可以使用alias保存原始实现之前声明将替换它的新方法。

module ActionDispatch
  class Request < Rack::Request
    alias :remote_ip_orig :remote_ip
    def remote_ip
      @remote_ip ||= (@env['HTTP_CF_CONNECTING_IP'] || remote_ip_orig)
    end
  end
end

3
投票

[如果您要使用CloudFlare并像在没有Ngingx或Apache模块的情况下那样检测真实IP,那么这种猴子修补方法是一个非常糟糕的主意,这将在将来导致意外结果。使用中间件会更好,因为应该使用中间件。这是我想到并实现的。

module Rack
  class CloudFlareFixup
    def initialize(app)
      @app = app
    end

    def call(env)
      if env['HTTP_CF_CONNECTING_IP']
        env['HTTP_X_FORWARDED_FOR'] = env['HTTP_CF_CONNECTING_IP']
        env['REMOTE_ADDR'] = env['HTTP_CF_CONNECTING_IP']
      end
      @app.call(env)
    end
  end
end

只需将其添加到您的应用程序中。rb

config.middleware.insert_before(0, Rack::CloudFlareFixup)

您可以在https://gist.github.com/mattheworiordan/9024372上看到完整的要旨


0
投票

我尝试了许多解决此问题的方法。这是我发现的:

  • cloudflare-rails gem =>这会将ip和remote_ip都设置为原始ip。我希望ip代表它来自的cloudflare ip,并希望remote_ip代表用户的原始ip,所以这对我不起作用。它通过每12小时查找一次Cloudflare IP范围列表并将其添加到Trusted_Proxies中来进行操作。然后,它修补:-使用cloudflare受信任代理修补ActionDispatch::Request.ipActionDispatch::Request.remote_ipRack::Attack::Request.trusted_proxy?,以便ip和remote_ip忽略它们,并使用发起请求的用户的始发ip。
  • actionpack-cloudflare gem =>这要简单得多。它所做的只是直接设置ActionDispatch.remote_ip = @env['HTTP_CF_CONNECTING_IP'],这与Cloudflare最佳推荐的最佳实践一致,但是似乎对1行代码使用gem似乎并不值得。
  • 手卷
# config/initializers/cloudflare.rb

# These values should rarely or never change and Cloudflare should alert us before that happens.
# The list of Cloudflare proxy server ip ranges comes from https://www.cloudflare.com/ips/
CLOUDFLARE_IP_RANGES = [IPAddr.new("103.21.244.0/22"),IPAddr.new("103.22.200.0/22"),IPAddr.new("103.31.4.0/22"),IPAddr.new("104.16.0.0/12"),IPAddr.new("108.162.192.0/18"),IPAddr.new("131.0.72.0/22"),IPAddr.new("141.101.64.0/18"),IPAddr.new("162.158.0.0/15"),IPAddr.new("172.64.0.0/13"),IPAddr.new("173.245.48.0/20"),IPAddr.new("188.114.96.0/20"),IPAddr.new("190.93.240.0/20"),IPAddr.new("197.234.240.0/22"),IPAddr.new("2405:8100::/32"),IPAddr.new("2405:b500::/32"),IPAddr.new("2606:4700::/32"),IPAddr.new("2803:f800::/32"),IPAddr.new("2a06:98c0::/29"),IPAddr.new("2c0f:f248::/32")
]

# By adding the cloudflare IP ranges as trusted_proxies, rails ignores those IPs when setting remote_ip and correctly sets it to the originating IP
Rails.application.config.action_dispatch.trusted_proxies = CLOUDFLARE_IP_RANGES + ActionDispatch::RemoteIp::TRUSTED_PROXIES

最后,我想阻止未代理的请求

# config/initializers/rack_attack.rb
class Rack::Attack
  class Request < ::Rack::Request
    # Create a remote_ip method for rack request by setting it equal to the cloudflare connecting ip header
    # To restore original visitor IP addresses at your origin web server, Cloudflare recommends your logs or applications
    # look at CF-Connecting-IP instead of X-Forwarded-For since CF-Connecting-IP has a consistent format containing only one IP.
    # https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-CloudFlare-handle-HTTP-Request-headers-
    def remote_ip
      @remote_ip ||= (env['HTTP_CF_CONNECTING_IP'] || ip).to_s
    end

    # This checks the request IP against Cloudflare IP ranges and the action dispatch default trusted proxies.
    # These include various local IPs like 127.0.0.1 so that local requests won't be blocked.
    def proxied?
      Rails.application.config.action_dispatch.trusted_proxies.any? { |range| range === ip }
    end
  end

  # Block all requests coming from non-Cloudflare IPs
  blocklist("block non-proxied requests in production") do |req|
    if req.proxied?
      false
    else
      req.log :warn
      true
    end
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.