sentry-raven/lib/raven/utils/real_ip.rb
require 'ipaddr'
# Based on ActionDispatch::RemoteIp. All security-related precautions from that
# middleware have been removed, because the Event IP just needs to be accurate,
# and spoofing an IP here only makes data inaccurate, not insecure. Don't re-use
# this module if you have to *trust* the IP address.
module Raven
module Utils
class RealIp
LOCAL_ADDRESSES = [
"127.0.0.1", # localhost IPv4
"::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) }
attr_accessor :ip, :ip_addresses
def initialize(ip_addresses)
self.ip_addresses = ip_addresses
end
def calculate_ip
# CGI environment variable set by Rack
remote_addr = ips_from(ip_addresses[:remote_addr]).last
# Could be a CSV list and/or repeated headers that were concatenated.
client_ips = ips_from(ip_addresses[:client_ip])
real_ips = ips_from(ip_addresses[:real_ip])
forwarded_ips = ips_from(ip_addresses[:forwarded_for])
ips = [client_ips, real_ips, forwarded_ips, remote_addr].flatten.compact
# If every single IP option is in the trusted list, just return REMOTE_ADDR
self.ip = filter_local_addresses(ips).first || remote_addr
end
protected
def ips_from(header)
# Split the comma-separated list into an array of strings
ips = header ? header.strip.split(/[,\s]+/) : []
ips.select do |ip|
begin
# Only return IPs that are valid according to the IPAddr#new method
range = IPAddr.new(ip).to_range
# we want to make sure nobody is sneaking a netmask in
range.begin == range.end
rescue ArgumentError
nil
end
end
end
def filter_local_addresses(ips)
ips.reject { |ip| LOCAL_ADDRESSES.any? { |proxy| proxy === ip } }
end
end
end
end