概要
Rails の request.remote_ip
はリクエストに Proxyサーバー・ロードバランサーを挟む場合でも「良い感じ」で「元のIPアドレス」の推定をしてくれそう
このあたりのコードを読むと action_dispatch.remote_ip
というものが呼び出されている
# Originating IP address, usually set by the RemoteIp middleware.
def remote_ip
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
# Sort through the various IP address headers, looking for the IP most likely to
# be the address of the actual remote client making this request.
#
# REMOTE_ADDR will be correct if the request is made directly against the Ruby
# process, on e.g. Heroku. When the request is proxied by another server like
# HAProxy or NGINX, the IP address that made the original request will be put in
# an `X-Forwarded-For` header. If there are multiple proxies, that header may
# contain a list of IPs. Other proxy services set the `Client-Ip` header
# instead, so we check that too.
#
# As discussed in [this post about Rails IP
# Spoofing](https://web.archive.org/web/20170626095448/https://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/),
# while the first IP in the list is likely to be the "originating" IP, it
# could also have been set by the client maliciously.
#
# In order to find the first address that is (probably) accurate, we take the
# list of IPs, remove known and trusted proxies, and then take the last address
# left, which was presumably set by one of those proxies.
def calculate_ip
# Set by the Rack web server, this is a single value.
remote_addr = ips_from(@req.remote_addr).last
# Could be a CSV list and/or repeated headers that were concatenated.
client_ips = ips_from(@req.client_ip).reverse!
forwarded_ips = ips_from(@req.x_forwarded_for).reverse!
# `Client-Ip` and `X-Forwarded-For` should not, generally, both be set. If they
# are both set, it means that either:
#
# 1) This request passed through two proxies with incompatible IP header
# conventions.
#
# 2) The client passed one of `Client-Ip` or `X-Forwarded-For`
# (whichever the proxy servers weren't using) themselves.
#
# Either way, there is no way for us to determine which header is the right one
# after the fact. Since we have no idea, if we are concerned about IP spoofing
# we need to give up and explode. (If you're not concerned about IP spoofing you
# can turn the `ip_spoofing_check` option off.)
should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
if should_check_ip && !forwarded_ips.include?(client_ips.last)
# We don't know which came from the proxy, and which from the user
raise IpSpoofAttackError, "IP spoofing attack?! " \
"HTTP_CLIENT_IP=#{@req.client_ip.inspect} " \
"HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
end
# We assume these things about the IP headers:
#
# - X-Forwarded-For will be a list of IPs, one per proxy, or blank
# - Client-Ip is propagated from the outermost proxy, or is blank
# - REMOTE_ADDR will be the IP that made the request to Rack
ips = forwarded_ips + client_ips
ips.compact!
# If every single IP option is in the trusted list, return the IP that's
# furthest away
filter_proxies(ips + [remote_addr]).first || ips.last || remote_addr
end
Trusted Proxy
Railsはデフォルトで「信頼できるプロクシ」の一覧を持っているようだ
# The default trusted IPs list simply includes IP addresses that are guaranteed
# by the IP specification to be private addresses. Those will not be the
# ultimate client IP in production, and so are discarded. See
# https://en.wikipedia.org/wiki/Private_network for details.
TRUSTED_PROXIES = [
"127.0.0.0/8", # localhost IPv4 range, per RFC-3330
"::1", # localhost IPv6
"fc00::/7", # private IPv6 range fc00::/7
"10.0.0.0/8", # private IPv4 range 10.x.x.x
"172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
"192.168.0.0/16", # private IPv4 range 192.168.x.x
].map { |proxy| IPAddr.new(proxy) }
なので、ロードバランサーが間にある構成でも、ロードバランサーが自己申告した HTTP_X_FORWARDED_FOR
を信頼して request.remote_ip
の値として返してくれていそうだ
Trusted Proxy の設定
なおRails config で trustex proxy を指定することもできるようだが、上記の通り、デフォルトでもプライベートのIPアドレスは心されている模様
config.action_dispatch.trusted_proxies
備考
素のIPアドレスを返すのは以下
request.remote_addr
request.env['REMOTE_ADDR']
HTTP_X_FORWARDED_FOR
は以下で確認できる
request.env['HTTP_X_FORWARDED_FOR']
リンク
- https://blog.tmtms.net/entry/202003/rack_request_ip
- https://unfit-for.work/posts/2022/gcp-rails-remote-ip/
- https://qiita.com/sugiyasu-qr/items/0a587b7107b117b369d2
- https://qiita.com/ThreeTreesLight/items/c14cf33b4621610a1e23
- https://tech.medpeer.co.jp/entry/cloudfront_viewer_address
- https://stackoverflow.com/questions/10997005/whats-the-difference-between-request-remote-ip-and-request-ip-in-rails
- https://stackoverflow.com/questions/76112921/remote-ip-returns-the-proxys-ip-address-instead-of-the-clients-ip-address