私は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.62
をconfig.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
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であると考えています。そのため、ログに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.log
(Remote 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)
私の意見では、あなたの現在のアプローチは唯一の健全なものです。欠落している唯一の手順は、env
のIPアドレスを上書きすることです。
典型的なREMOTE_ADDRは、プロキシとロードバランサーの層がいくらかある場合、正しいIPを保持することはめったになく、そうでない場合は、この点で一意ではありません。それぞれがリモートIP関連のヘッダーを追加または変更する可能性があります。そして、それらの各フィールドが必ずしも単一のIPアドレスに対応していると想定することはできません。一部のユーザーは、代わりにIPをリストにプッシュまたはシフト解除します。
どのフィールドが正しい値と方法を保持しているのかを確実に知る方法は1つしかありません。それはそこに飛び込んで調べることです。あなたは明らかにそれをすでにやった。さて、env['REMOTE_ADDR']
は、Rackミドルウェアを使用して正しい値に設定します。現在行われているように、ログを書いたり、間違ったIPアドレスを処理したりしていないコードを許可しても意味がありません。
(これはRubyなので、もちろんラッキー::リクエストにパッチを当てることもできます...)
エキゾチックなセットアップがクライアントの実際のIPアドレスを見つけようとする試みを台無しにするさまざまな程度を示すカラフルな読み物については、たとえば、これについてWordPressで発生した終わりのない議論を参照してください。
それはPHPですが、提起されたポイントの要点はRubyにも同様に適用されます(これも私がこれを書いているので未解決であり、彼らは永遠に存在していたことに注意してください)。
これは私にはトリックを行うように見えました。 (nginx設定で設定)
proxy_set_header CLIENT_IP $remote_addr;
私も同じ問題に直面していました。これを修正するために、私はあなたの実装を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
同じ問題に遭遇し、私たちの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')]