web-dev-qa-db-ja.com

Rails nginxのようなプロキシの背後にあるときにログを記録する方法

問題

私はRails 3.2.15とラック1.4.5を2台のサーバーでセットアップしています。最初のサーバーは静的アセットを提供するnginxプロキシです。2番目のサーバーはRailsアプリ。

Rails production.logでは、クライアントのIPアドレス(10.0.10.62)ではなく、常にnginxのIPアドレス(10.0.10.150)が表示されます。

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.62config.action_dispatch.trusted_proxies = /^127\.0\.0\.1$/に設定することにより、config/environments/production.rbを信頼できるプロキシアドレスではないと定義しました。別の answer へ。アプリケーションコントローラーにログを記録しているので、動作していることを確認できます。

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

requestActionDispatch::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であると考えています。そのため、ログにnginx IPアドレスが表示されます。

ログの抜粋では、実稼働環境を再現するために仮想マシンを使用しているため、クライアントとサーバーのIPアドレスは10.0.10.xです。

現在のソリューション

この動作を回避するために、app/middleware/remote_ip_logger.rbにある小さなRackミドルウェアを作成しました。

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 log /home/deployer/site/current/log/production.logRemote IPandrequest.iplinesカスタムコードによって追加されます):

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)
28
cbliard

私の意見では、あなたの現在のアプローチは唯一の健全なものです。欠落している唯一の手順は、envのIPアドレスを上書きすることです。

典型的なREMOTE_ADDRは、プロキシとロードバランサーの層がいくらかある場合、正しいIPを保持することはめったになく、そうでない場合は、この点で一意ではありません。それぞれがリモートIP関連のヘッダーを追加または変更する可能性があります。そして、それらの各フィールドが必ずしも単一のIPアドレスに対応していると想定することはできません。一部のユーザーは、代わりにIPをリストにプッシュまたはシフト解除します。

どのフィールドが正しい値と方法を保持しているのかを確実に知る方法は1つしかありません。それはそこに飛び込んで調べることです。あなたは明らかにそれをすでにやった。さて、env['REMOTE_ADDR']は、Rackミドルウェアを使用して正しい値に設定します。現在行われているように、ログを書いたり、間違ったIPアドレスを処理したりしていないコードを許可しても意味がありません。

(これはRubyなので、もちろんラッキー::リクエストにパッチを当てることもできます...)

エキゾチックなセットアップがクライアントの実際のIPアドレスを見つけようとする試みを台無しにするさまざまな程度を示すカラフルな読み物については、たとえば、これについてWordPressで発生した終わりのない議論を参照してください。

それはPHPですが、提起されたポイントの要点はRubyにも同様に適用されます(これも私がこれを書いているので未解決であり、彼らは永遠に存在していたことに注意してください)。

7

これは私にはトリックを行うように見えました。 (nginx設定で設定)

   proxy_set_header CLIENT_IP $remote_addr;
4
Jason

私も同じ問題に直面していました。これを修正するために、私はあなたの実装をconfig/application.rb修正しました。

config.middleware.insert_before Rails::Rack::Logger, 'RemoteIpLogger'

追加のロガーを作成する必要はなく、最初の行自体に実際のクライアントIPが表示されます。

Started GET "/" for 10.0.10.62 at 2013-11-22 08:01:17 +0000

そしてapp\middleware\remote_ip_logger.rb。ぼくの HTTP_X_FORWARDED_FORにはIPのリストがあり、最初のリストは実際のクライアントのIPです。

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
2
Rohit Bhore

同じ問題に遭遇し、私たちのWebクライアントのサブセットがプライベートネットワーク上のRails app on(Rails 4.2.7))にアクセスし、間違ったIPが報告されるという問題が発生しました。問題を解決するために私たちに役立ったものを追加します。

私は Railsの問題522 を見つけました。これは、質問のようにIPを二重にログするよりも優れた回避策を提供します。したがって、次のように、信頼できるプロキシのリストからプライベートネットワークを削除するために、Rackにパッチを当てます。

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

これは、間違ったIPをログに記録しているコントローラーに対処します。残りの修正は、request.remote_ipは正しく処理されます。これを行うには、config/environments/production.rbに以下を追加します。

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