我有一个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的其他宝石(如设计)
谢谢!
答案 0 :(得分:6)
我相信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
答案 1 :(得分: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
只需将其添加到您的application.rb
即可config.middleware.insert_before(0, Rack::CloudFlareFixup)
看到完整的要点
答案 2 :(得分:0)
我尝试了许多解决此问题的方法。这是我发现的:
cloudflare-rails
gem =>会将ip和remote_ip都设置为原始ip。我希望ip代表它来自的cloudflare ip,并希望remote_ip代表用户的原始ip,所以这对我不起作用。它通过每12小时查找一次Cloudflare IP范围列表并将其添加到Trusted_Proxies中来进行操作。然后,它使用cloudflare受信任的代理修补:-ActionDispatch::Request.ip
,ActionDispatch::Request.remote_ip
和Rack::Attack::Request.trusted_proxy?
,以便ip和remote_ip忽略它们,并使用发起请求的用户的原始ip。actionpack-cloudflare
gem =>这要简单得多。它所做的只是直接设置ActionDispatch.remote_ip = @env['HTTP_CF_CONNECTING_IP']
,这符合Cloudflare最佳建议的最佳实践,但是使用gem编写1行代码似乎并不值得。# 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