Last IP address and action_dispatch.trusted_proxies

I’m experiencing the problem that all of our users have 127.0.0.1 as last IP address. This issue was previously discussed here, here and here, and all of these threads contain valuable input, but I still cannot get it to work.

Our setup is an internal Discourse installation (Docker) behind an internal SSL-proxy. All our users also access Discourse from our internal network (directly or via VPN).

When I tcpdump a packet from Discourse’s nginx to the Rails app, I see the following:

GET / HTTP/1.0
Host: <our discourse host>
X-Real-IP: 10.10.2.3
X-Forwarded-For: 10.10.0.89, 10.10.2.3
X-Forwarded-Proto: https
Connection: close
Cache-Control: max-age=0

This is correct as the SSL proxy is 10.10.2.3 and the client’s IP address is 10.10.0.89.

Now the way I understand that the responsible middleware in Rails works is that it removes all IPs from the X-Forwarded-For list that seem to be proxies. The default list TRUSTED_PROXIES includes all private IP network ranges, thus leaving no IP left and going with 127.0.0.1 as the default (because of REMOTE_ADDR coming from Discourse’s nginx).

I have modified config/application.rb to include

config.action_dispatch.trusted_proxies = %w(127.0.0.1/32 10.10.2.3/32).map { |proxy| IPAddr.new(proxy) }

and restarted the docker container. This seems to have the desired effect at first. When I run a Rails console inside the app, I see the following:

rails c production
Loading production environment (Rails 4.2.7)
irb(main):001:0> app.get '/'
Started GET "/" for 127.0.0.1 at 2016-09-13 17:45:09 +0000
Processing by CategoriesController#index as HTML
Redirected to http://www.example.com/login
Filter chain halted as :redirect_to_login_if_required rendered or redirected
Completed 302 Found in 68ms (ActiveRecord: 13.0ms)
=> 302
irb(main):002:0> app.request.env['action_dispatch.remote_ip'].instance_variable_get :@proxies
=> [#<IPAddr: IPv4:127.0.0.1/255.255.255.255>, #<IPAddr: IPv4:10.10.2.3/255.255.255.255>]
irb(main):003:0> 

So it seems that the RemoteIp middleware now uses the right IP addresses as a proxy list.

However, I still only see 127.0.0.1 as last IP address for our users. Don’t really know what else to do—if anyone can shed some light on the behavior, I’d appreciate it.

Any ideas here @mpalmer?

We don’t use trusted_proxies in our hosted environment; we adjust the nginx config to set X-Real-IP directly, since, IIRC, the RemoteIp middleware prefers that over XFF. The pups exec stanza we use looks like this:

- replace:
    filename: /etc/nginx/conf.d/discourse.conf
    from: "types {"
    to: |
      set_real_ip_from 10.0.0.0/24;
      set_real_ip_from 172.17.0.0/24;
      real_ip_header X-Forwarded-For;
      real_ip_recursive on;
      types {
5 Likes

Thanks for your feedback! I’m not sure if this specific workaround would help in our case, as all IPs involved are in 10.0.0.0/16 and would still be in RemoteIp’s TRUSTED_PROXIES list.

Here’s the relevant code from Rails 4.2. Interestingly, X-Real-IP doesn’t appear anywhere there, only X-Forwarded-For and Client-Ip:

# We assume these things about the IP headers:
#
#   - X-Forwarded-For will be a list of IPs, one per proxy, or blank
#   - Client-Ip is propagated from the outermost proxy, or is blank
#   - REMOTE_ADDR will be the IP that made the request to Rack
ips = [forwarded_ips, client_ips, remote_addr].flatten.compact
# If every single IP option is in the trusted list, just return REMOTE_ADDR
filter_proxies(ips).first || remote_addr

If I understand it correctly, filter_proxies removes all IP address from ips that are in TRUSTED_PROXIES , no matter whether they originally came from X-Forwarded-For or Client-Ip. So we would still end up with remote_addr, which will always be 127.0.0.1

I do realize that this probably more of a Rack/Rails topic than a Discourse one. But maybe someone else has a similar setup and managed to have the proper last IP addresses displayed in the admin interface.

You might need to remove some address ranges from TRUSTED_PROXIES, by the sound of it.

I completely agree, that’s what I’m trying to do (see above) by setting config.action_dispatch.trusted_proxies in config/application.rb—its default value is TRUSTED_PROXIES.
But for some reason this does not have the desired effect.

Does that setting perhaps add to TRUSTED_PROXIES, rather than override it?