Проблема
У меня есть рельсы 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)