Skip to content

Instantly share code, notes, and snippets.

@mudge
Last active November 21, 2023 14:06
Show Gist options
  • Save mudge/e5c72d701d7fcbda67608deb800193cb to your computer and use it in GitHub Desktop.
Save mudge/e5c72d701d7fcbda67608deb800193cb to your computer and use it in GitHub Desktop.
How to configure Rails and Rack::Attack to use the real client IP when running behind Cloudflare
Rails.application.configure do
# Add Cloudflare's IPs to the trusted proxy list so they are ignored when
# determining the true client IP.
#
# See https://www.cloudflare.com/ips-v4/ and https://www.cloudflare.com/ips-v6/
config.action_dispatch.trusted_proxies = ActionDispatch::RemoteIp::TRUSTED_PROXIES + %w[
173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/13
104.24.0.0/14
172.64.0.0/13
131.0.72.0/22
2400:cb00::/32
2606:4700::/32
2803:f800::/32
2405:b500::/32
2405:8100::/32
2a06:98c0::/29
2c0f:f248::/32
].map { |proxy| IPAddr.new(proxy) }
end
Rack::Attack.throttle("req/ip", limit: 300, period: 5.minutes) do |req|
req.get_header("action_dispatch.remote_ip") unless req.path.start_with?("/assets")
end
@tmak
Copy link

tmak commented Oct 20, 2023

True. We're actually running on Heroku, at the moment 😅
The gem's test suite looked solid to me to use a gem, and monkey patching, rather than having to manage this functionality on our own.

Talking about Cloudflare, Heroku, and Rack::Attack. We use this Rack::Attack configuration to prevent skipping Cloudflare in production. May be helpful for you or others who get here.

class Rack::Attack
  if %w[1 true True enabled yes].include?(ENV["REJECT_UNPROXIED_REQUESTS"])
    blocklist("block non-proxied requests in production") do |request|
      raw_ip = request.get_header("HTTP_X_FORWARDED_FOR")
      ip_addresses = raw_ip ? raw_ip.strip.split(/[,\s]+/) : []
      proxy_ip = ip_addresses.last

      if ::Rails.application.config.cloudflare.ips.any? { |proxy|
           proxy === proxy_ip
         }
        false
      else
        ::Rails.logger.warn "Rack Attack IP Filtering: blocked request from #{proxy_ip} to #{request.url}"
        true
      end
    end
  end
end

@trevorturk
Copy link

Ah yes, I do something similar via:

  blocklist("herokuapp") do |req|
    req.host =~ /herokuapp/
  end

...which works for my case, simply enforcing access through my domain as opposed to Heroku directly.

@delphaber
Copy link

Thanks for sharing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment