ruby - potenciales - facebook crm integration
Cómo registrar la IP de cliente real en el registro de rieles cuando está detrás de un proxy como nginx (5)
Problema
Tengo una instalación de carriles 3.2.15 con rack 1.4.5 en dos servidores. El primer servidor es un proxy nginx que sirve activos estáticos. El segundo servidor es un unicornio que sirve la aplicación Rails.
En Rails production.log
siempre veo la dirección IP nginx (10.0.10.150) y no la dirección IP de mi cliente (10.0.10.62):
Started GET "/" for 10.0.10.150 at 2013-11-21 13:51:05 +0000
Quiero tener la IP del cliente real en los registros.
Nuestra configuración
Los encabezados HTTP X-Forwarded-For
y X-Real-IP
están configurados correctamente en nginx y he definido 10.0.10.62
como una dirección proxy de confianza al establecer config.action_dispatch.trusted_proxies = /^127/.0/.0/.1$/
en config/environments/production.rb
, gracias a otra answer . Puedo verificar que esté funcionando porque los registro en el controlador de la aplicación:
en la 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
en production.log
:
request.ip = 10.0.10.150 and request.remote_ip = 10.0.10.62
Investigación
Al investigar, vi que Rails::Rack::Logger
es responsable de registrar la dirección 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
es una instancia de ActionDispatch::Request
. Hereda Rack::Request
que define cómo se calcula la dirección 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
La dirección IP reenviada se filtra con trusted_proxy?
. Debido a que nuestro servidor nginx está utilizando una dirección IP pública y no una dirección IP privada, Rack::Request#ip
cree que no es un proxy, sino la verdadera IP del cliente que intenta hacer un spoofing de IP. Es por eso que veo la dirección IP de nginx en mis registros.
En extractos de registro, el cliente y los servidores tienen la dirección IP 10.0.10.x porque estoy usando máquinas virtuales para reproducir nuestro entorno de producción.
Nuestra solución actual
Para eludir este comportamiento, escribí un pequeño middleware de Rack ubicado en 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
Y lo inserto justo después del middleware ActionDispatch::RemoteIp
config.middleware.insert_after ActionDispatch::RemoteIp, "RemoteIpLogger"
De esta manera puedo ver la IP del cliente real en los registros:
Started GET "/" for 10.0.10.150 at 2013-11-21 13:59:06 +0000
Remote IP: 10.0.10.62
Me siento un poco incómodo con esta solución. nginx + unicorn es una configuración común para la aplicación de rieles. Si tengo que registrar la IP del cliente yo mismo, significa que he perdido algo. ¿Es porque el servidor Nginx está usando una dirección IP pública cuando se comunica con el servidor de rieles? ¿Hay alguna manera de personalizar el trusted_proxy?
método de Rack::Request
?
EDITADO : agregue la configuración nginx y una captura de solicitud 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;
}
}
El servidor Nginx es 10.0.10.150
. El servidor Rails es 10.0.10.190
. Mi máquina es 10.0.10.62
Al hacer curl http://10.0.10.150/
desde mi máquina, un tcpdump port 8080 -i eth0 -Aq -s 0
en el servidor de rieles muestra estos encabezados HTTP de solicitud:
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: */*
Y los rieles registran /home/deployer/site/current/log/production.log
(líneas IP remotas y request.ip que se agregan mediante código personalizado):
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)
Corto y simple:
request.remote_ip
En mi opinión, tu enfoque actual es el único sensato. El único paso que falta es sobrescribir la dirección IP en env
.
El REMOTE_ADDR típico rara vez tiene la IP correcta si tiene cualquier cantidad de capas de proxies y equilibradores de carga y lo que no, no es único en este sentido. Cada uno potencialmente agrega o cambia los encabezados remotos relacionados con IP. Y no puede suponer que cada uno de esos campos corresponde necesariamente a una única dirección IP. Algunos presionarán o desharán una IP a una lista.
Solo hay una forma de saber con certeza qué campo tiene el valor correcto y cómo, y eso es bucear allí y mirar. Evidentemente ya has hecho eso. Ahora, simplemente sobrescriba env[''REMOTE_ADDR'']
con su valor correcto usando su middleware Rack. No tiene sentido dejar que cualquier parte del código que no haya escrito log o procese la dirección IP incorrecta, como está sucediendo ahora.
(Esto es Ruby, también puedes parche de mono Rack :: Solicitud, por supuesto ...)
Para una lectura colorida que ilustre los diversos grados en los que las configuraciones exóticas pueden arruinar los intentos de encontrar la dirección IP real de un cliente, consulte, por ejemplo, las discusiones interminables que ocurrieron sobre esto con WordPress:
- https://core.trac.wordpress.org/ticket/9235
- https://core.trac.wordpress.org/ticket/4198
- https://core.trac.wordpress.org/ticket/4602
Es PHP, pero la esencia de los puntos planteados se aplica igualmente a Ruby. (Tenga en cuenta que no están resueltos mientras escribo esto, también, y que han existido por eones).
Estaba enfrentando el mismo problema. Para solucionar este problema, hice referencia a su implementación, justo debajo de la línea en config/application.rb
lo arregló.
config.middleware.insert_before Rails::Rack::Logger, ''RemoteIpLogger''
No es necesario escribir extra loggers, verá IP del cliente real en la primera fila.
Started GET "/" for 10.0.10.62 at 2013-11-22 08:01:17 +0000
Y en la app/middleware/remote_ip_logger.rb
. Mi HTTP_X_FORWARDED_FOR
está teniendo una lista de IP y la primera es la IP del cliente real.
class RemoteIpLogger
def initialize(app)
@app = app
end
def call(env)
if env["HTTP_X_FORWARDED_FOR"]
remote_ip = env["HTTP_X_FORWARDED_FOR"].split(",")[0]
env[''REMOTE_ADDR''] = env["action_dispatch.remote_ip"] = env["HTTP_X_FORWARDED_FOR"] = remote_ip
@app.call(env)
else
@app.call(env)
end
end
end
Esto pareció hacer el truco para mí. (establecido en configuración nginx)
proxy_set_header CLIENT_IP $remote_addr;
Me encontré con el mismo problema, que un subconjunto de nuestros clientes web acceden a nuestra aplicación de rieles en (Rails 4.2.7) en nuestra red privada y recibimos informes de IP incorrectos. Entonces, pensé que agregaría lo que funcionó para nosotros para resolver el problema.
Encontré el problema 5223 de Rails que proporcionaba una mejor solución que el doble de registrar el IP como lo hace la pregunta. Por lo tanto, aplicamos el parche Rack para eliminar la red privada de la lista de servidores proxy de confianza de la siguiente manera:
module Rack
class Request
def trusted_proxy?(ip)
ip =~ /^127/.0/.0/.1$/
end
end
end
Eso se dirige al controlador que registra la dirección IP incorrecta, la otra mitad de la corrección para garantizar que request.remote_ip
se maneje correctamente. Para hacerlo, agregue lo siguiente a su config / environments / production.rb:
config.action_dispatch.trusted_proxies = [IPAddr.new(''127.0.0.1'')]