Hi Rails Core!
While adding a reverse proxy to a Rails 4 app, I was surprised to see that request.ip
and request.remote_ip
had different values.
That’s a gotcha that I’d like to fix.
Here’s a demo app showing how Client-IP
and X-Forwarded-For
headers values that yield different results.
The difference stems from ActionDispatch::Request#remote_ip
being more strict about trusted proxies than Rack::Requests#ip
.
I’d like to clean up the inconsistencies, and am looking for a +1 before making a pull request.
Here are some of the changes I’d like to make:
-
Make ActionDispatch::Request#remote_ip
an alias for #ip
. Eliminate the developer gotcha.
-
Move Rails’ customizations in the ActionDispatch::Request subclass to a mixin for Rack::Request. That eliminates developer gotchas if they use a Rack::Request instead of an ActionDispatch::Request.
-
Add hooks for extending the logic to calculate IP addresses for easier developer customization.
-
Get rid of ActionDispatch::RemoteIp
middleware. It’s no longer needed when all the code to calculate IP addresses lives in Request objects.
Of course I’d maintain backwards compatibility (e.g. config like action_dispatch.trusted_proxies
).
I couldn’t find any documentation about why remote_ip
and ip
would be different. I assume the only reason two different methods exists is b/c Rails’ requests.remote_ip
method pre-dates Rack and Rack::Request#ip
.
Feedback welcome!
Aaron Suggs
Operations Engineer, Kickstarter
I don't know very well, but isn't ip meant to be for example the nginx
instance that proxied the request and remote_ip the client? Are you
proxing behind something or direct facing the web? If it's direct,
then the values should be the same (from what I think should be,
haven't read the code)
I don’t know very well, but isn’t ip meant to be for example the nginx
instance that proxied the request and remote_ip the client?
Nope. Rack::Request#ip is more complicated than env[‘REMOTE_ADDR’] (which would be the ip address on the other end of the TCP socket, i.e., the nginx server). Rack::Request#ip tries to figure out the “true” client IP by examining env[‘HTTP_CLIENT_IP’] and env[‘HTTP_X_FORWARDED_FOR’] headers. Rails’s remote_ip
does the same thing, except it’s more strict/secure about what it considers trusted proxy IPs.
My suggested solution is to move Rails’ extra strictness to the Rack::Request#ip method. That way request.remote_ip
and request.ip
would return the same value.
Are you
proxing behind something or direct facing the web? If it’s direct,
then the values should be the same (from what I think should be,
haven’t read the code)
I’m proxying. Indeed, if the ruby web server were directly connected to clients, remote_ip
and ip
would be the same. The problematic edge case is when you use a proxy server that’s not in Rails’ list of trusted proxies (i.e., private IP address, like 10.*
).
-Aaron Suggs
I faced exactly the same issue last week when trying to figure out the correct Vagrant client ip for testing purposes.
Since I later decided upon a better strategy for my case which didn’t rely on the client ip but an extra param instead, I didn’t go further to understand why I was not getting the proper id as after reading the code it seemed I was using a trusted ip for the proxy server…
I wasn’t aware of the ip method from Rack though…
But now that you mentioned the IP range 10.x maybe this would explain the weird behavior I got. I think this is the range used in the host for private networks with Vagrant / VirtualBox…