Подтвердить что ты не робот

Как зарегистрировать реальный клиентский ip в журнале rails, когда позади прокси-сервера, такого как nginx

Проблема

У меня есть рельсы 3.2.15 с установкой стойки 1.4.5 на двух серверах. Первый сервер - это прокси-сервер nginx, обслуживающий статические активы. Второй сервер - единорог, обслуживающий приложение rails.

В Rails production.log я всегда вижу IP-адрес nginx (10.0.10.150), а не мой IP-адрес клиента (10.0.10.62):

Started GET "/" for 10.0.10.150 at 2013-11-21 13:51:05 +0000

Я хочу иметь реальный клиентский IP-адрес в журналах.

Наша настройка

Заголовки HTTP X-Forwarded-For и X-Real-IP правильно настроены в nginx, и я определил 10.0.10.62 как не доверенный адрес прокси, установив config.action_dispatch.trusted_proxies = /^127\.0\.0\.1$/ в config/environments/production.rb, благодаря другому ответ. Я могу проверить, что он работает, потому что я регистрирую их в контроллере приложений:

в app/controllers/application_controller.rb:

class ApplicationController < ActionController::Base
    before_filter :log_ips

    def log_ips
        logger.info("request.ip = #{request.ip} and request.remote_ip = #{request.remote_ip}")
    end
end

в production.log:

request.ip = 10.0.10.150 and request.remote_ip = 10.0.10.62

Исследование

При исследовании я увидел, что Rails::Rack::Logger отвечает за протоколирование IP-адреса:

def started_request_message(request)
  'Started %s "%s" for %s at %s' % [
    request.request_method,
    request.filtered_path,
    request.ip,
    Time.now.to_default_s ]
end

request является экземпляром ActionDispatch::Request. Он наследует Rack::Request, который определяет, как вычисляется IP-адрес:

def trusted_proxy?(ip)
  ip =~ /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|^::1$|^fd[0-9a-f]{2}:.+|^localhost$/i
end

def ip
  remote_addrs = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
  remote_addrs.reject! { |addr| trusted_proxy?(addr) }

  return remote_addrs.first if remote_addrs.any?

  forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []

  if client_ip = @env['HTTP_CLIENT_IP']
    # If forwarded_ips doesn't include the client_ip, it might be an
    # ip spoofing attempt, so we ignore HTTP_CLIENT_IP
    return client_ip if forwarded_ips.include?(client_ip)
  end

  return forwarded_ips.reject { |ip| trusted_proxy?(ip) }.last || @env["REMOTE_ADDR"]
end

Переадресованный IP-адрес фильтруется с помощью trusted_proxy?. Поскольку наш nginx-сервер использует общедоступный IP-адрес, а не частный IP-адрес, Rack::Request#ip считает, что он не прокси-сервер, а реальный клиентский ip, который пытается выполнить IP-спуфинг. Вот почему я вижу IP-адрес nginx в своих журналах.

В журнальных отрывках клиент и сервер имеют IP-адрес 10.0.10.x, потому что я использую виртуальные машины для воспроизведения нашей производственной среды.

Наше текущее решение

Чтобы обойти это поведение, я написал небольшое промежуточное ПО Rack, расположенное в app/middleware/remote_ip_logger.rb:

class RemoteIpLogger
  def initialize(app)
    @app = app
  end

  def call(env)
    remote_ip = env["action_dispatch.remote_ip"]
    Rails.logger.info "Remote IP: #{remote_ip}" if remote_ip
    @app.call(env)
  end
end

И я вставляю его сразу после ActionDispatch::RemoteIp промежуточного программного обеспечения

config.middleware.insert_after ActionDispatch::RemoteIp, "RemoteIpLogger"

Таким образом, я вижу реальный клиентский IP-адрес в журналах:

Started GET "/" for 10.0.10.150 at 2013-11-21 13:59:06 +0000
Remote IP: 10.0.10.62

Я чувствую себя немного неудобно с этим решением. nginx + unicorn - обычная установка для приложения rails. Если мне нужно самостоятельно регистрировать клиентский IP-адрес, это означает, что я что-то пропустил. Это потому, что сервер Nginx использует общедоступный IP-адрес при общении с сервером rails? Есть ли способ настроить trusted_proxy? метод Rack::Request?

EDITED: добавьте конфигурацию nginx и захват HTTP-запроса

/etc/nginx/sites-enabled/site.example.com.conf:

server {
    server_name    site.example.com;
    listen         80;


    location ^~ /assets/ {
       root /home/deployer/site/shared;
       expires 30d;
    }

    location / {
      root /home/deployer/site/current/public;
      try_files $uri @proxy;
    }

    location @proxy {
      access_log  /var/log/nginx/site.access.log combined_proxy;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_read_timeout 300;

      proxy_pass http://rails.example.com:8080;
    }
}

Сервер Nginx 10.0.10.150. Сервер Rails - 10.0.10.190. Моя машина 10.0.10.62 При выполнении curl http://10.0.10.150/ с моей машины сервер tcpdump port 8080 -i eth0 -Aq -s 0 на сервере rails отображает запросы HTTP-заголовков:

GET / HTTP/1.0
X-Forwarded-For: 10.0.10.62
X-Forwarded-Proto: http
Host: 10.0.10.150
Connection: close
User-Agent: curl/7.29.0
Accept: */*

И журнал rails /home/deployer/site/current/log/production.log (удаленные IP-адреса и строки request.ip добавляются специальным кодом):

Started GET "/" for 10.0.10.150 at 2013-11-22 08:01:17 +0000
Remote IP: 10.0.10.62
Processing by Devise::RegistrationsController#new as */*
request.ip = 10.0.10.150 and request.remote_ip = 10.0.10.62
  Rendered devise/shared/_links.erb (0.1ms)
  Rendered devise/registrations/new.html.erb within layouts/application (2.3ms)
  Rendered layouts/_landing.html.erb (1.5ms)
Completed 200 OK in 8.9ms (Views: 7.5ms | ActiveRecord: 0.0ms)
4b9b3361

Ответ 1

По моему мнению, ваш нынешний подход является единственным нормальным. Единственный, что не хватает, это перезаписать IP-адрес в env.

В типичном REMOTE_ADDR редко используется правильный IP-адрес, если у вас есть количество уровней прокси-серверов и балансировщиков, а что нет - вы не уникальны в этом отношении. Каждый потенциально добавляет или изменяет удаленные заголовки, связанные с IP. И вы не можете предположить, что каждое из этих полей обязательно соответствует одному IP-адресу. Некоторые будут нажимать или не изменять IP-адрес в списке.

Существует только один способ точно знать, какое поле содержит правильное значение и как, и что нужно погрузиться туда и посмотреть. Вы, очевидно, уже это сделали. Теперь просто перепишите env['REMOTE_ADDR'] с его правильным значением, используя промежуточное ПО Rack. Там мало смысла позволять какой-либо части кода вы не записывали журнал или обрабатывали неправильный IP-адрес, как это происходит сейчас.

(Это Ruby, вы также можете использовать патч обезьяны Rack:: Request, конечно...)

Для ярких чтений, иллюстрирующих различные степени, в которых экзотические установки могут испортить попытки поиска реального IP-адреса клиента, см., например, бесконечные обсуждения, которые произошли об этом для WordPress:

Это PHP, но суть поднятых пунктов одинаково хорошо применима к Ruby. (Обратите внимание, что они не решены, поскольку я пишу это тоже, и что они были вокруг для эонов.)

Ответ 2

Казалось, это трюк для меня. (устанавливается в конфигурации nginx)

   proxy_set_header CLIENT_IP $remote_addr;

Ответ 3

Я столкнулся с той же проблемой, что подмножество наших веб-клиентов обращается к нашему сервису rails (Rails 4.2.7) в нашей частной сети, и мы получаем неверный IP-адрес. Итак, я подумал, что добавлю, что сработало для нас, чтобы решить проблему.

Я нашел Rails issue 5223, который обеспечил лучшее обходное решение, чем двойное протоколирование IP-типа, как это делает вопрос. Итак, мы обеими играми обезьяны, чтобы удалить частную сеть из списка доверенных прокси, например:

module Rack
  class Request
    def trusted_proxy?(ip)
      ip =~ /^127\.0\.0\.1$/
    end
  end
end

Это указывает на то, что контроллер регистрирует неправильный IP-адрес, а другая половина исправления обеспечивает правильную обработку request.remote_ip. Для этого добавьте следующее в конфигурацию /environment/production.rb:

config.action_dispatch.trusted_proxies = [IPAddr.new('127.0.0.1')]