Class: UnXF

Inherits:
Object
  • Object
show all
Defined in:
lib/unxf.rb

Overview

Rack middleware to remove “HTTP_X_FORWARDED_FOR” in the Rack environment and replace “REMOTE_ADDR” with the value of the original client address.

Constant Summary collapse

REMOTE_ADDR =

:stopdoc: reduce garbage overhead by using constant strings

"REMOTE_ADDR".freeze
HTTP_X_FORWARDED_FOR =
"HTTP_X_FORWARDED_FOR"
HTTP_X_FORWARDED_PROTO =
"HTTP_X_FORWARDED_PROTO"
RACK_URL_SCHEME =
"rack.url_scheme".freeze
UNXF_FOR =
"unxf.for".freeze
UNXF_PROTO =
"unxf.proto".freeze
HTTPS =
"https"
RFC_1918 =

local LAN addresses described in RFC 1918

%w(10.0.0.0/8 172.16.0.0/12 192.168.0.0/16)
LOCALHOST =

IPv4 localhost addresses (127.0.0.0/8)

%w(127.0.0.0/8)
LOCALHOST6 =

IPv6 localhost address (::1/128)

%w(::1/128)

Instance Method Summary collapse

Constructor Details

#initialize(app, trusted = [:RFC_1918, :LOCALHOST, :LOCALHOST6]) ⇒ UnXF

In your Rack config.ru:

use UnXF

If you do not want to trust any hosts other than “0.6.6.6”, you may only specify one host to trust:

use UnXF, "0.6.6.6"

If you want to trust “0.6.6.6” in addition to the default set of hosts:

use UnXF, [ :RFC_1918, :LOCALHOST, "0.6.6.6" ]


40
41
42
43
44
45
46
47
48
49
50
# File 'lib/unxf.rb', line 40

def initialize(app, trusted = [:RFC_1918, :LOCALHOST, :LOCALHOST6])
  @app = app
  @trusted = Patricia.new
  @trusted6 = Patricia.new(:AF_INET6)
  Array(trusted).each do |mask|
    mask = UnXF.const_get(mask) if Symbol === mask
    Array(mask).each do |prefix|
      (/:/ =~ prefix ? @trusted6 : @trusted).add(prefix, true)
    end
  end
end

Instance Method Details

#call(env) ⇒ Object

Rack entry point



53
54
55
# File 'lib/unxf.rb', line 53

def call(env) # :nodoc:
  unxf!(env) || @app.call(env)
end

#on_broken_addr(env, xff_str) ⇒ Object

Our default action on a broken address is to just fall back to calling the app without modifying the env



91
92
93
# File 'lib/unxf.rb', line 91

def on_broken_addr(env, xff_str)
  @app.call(env)
end

#on_untrusted_addr(env, xff_str) ⇒ Object

Our default action on an untrusted address is to just fall back to calling the app without modifying the env



97
98
99
# File 'lib/unxf.rb', line 97

def on_untrusted_addr(env, xff_str)
  @app.call(env)
end

#unxf!(env) ⇒ Object

returns nil on success and a Rack response triplet on failure This allows existing applications to use UnXF without putting it into the middleware stack (to avoid increasing stack depth and GC time)



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/unxf.rb', line 60

def unxf!(env)
  if xff_str = env.delete(HTTP_X_FORWARDED_FOR)
    env[UNXF_FOR] = xff_str
    xff = xff_str.split(/\s*,\s*/)
    addr = env[REMOTE_ADDR]
    begin
      while (/:/ =~ addr ? @trusted6 : @trusted).include?(addr) &&
            tmp = xff.pop
        addr = tmp
      end
    rescue ArgumentError
      return on_broken_addr(env, xff_str)
    end

    # it's stupid to have https at any point other than the first
    # proxy in the chain, so we don't support that
    if xff.empty?
      env[REMOTE_ADDR] = addr
      if xfp = env.delete(HTTP_X_FORWARDED_PROTO)
        env[UNXF_PROTO] = xfp
        /\Ahttps\b/ =~ xfp and env[RACK_URL_SCHEME] = HTTPS
      end
    else
      return on_untrusted_addr(env, xff_str)
    end
  end
  nil
end