logjam_agent 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 375304657bb752c670704f325f55054bf811d9c5
4
- data.tar.gz: 09ab3cf2c0037d3c2f0986d9d926b342f543e7ac
3
+ metadata.gz: 758acadd75c92e8f90ec5048473e2f5790d708bb
4
+ data.tar.gz: b63d83423a36353e614eca9e0ac25716b498f327
5
5
  SHA512:
6
- metadata.gz: cc70919214e1d998562108257dbb824235c71fc8a0e90c914207968cac7559f07f250205fa081757201639a8192fd4a44041b940cade6b29b1d71c857de66db4
7
- data.tar.gz: f952178602f923e69cd98a9963e9a95bd4e01ed1575b520a60e0bad92e68ba97e5b3418421e171f601c1844c9dca71fde22a6cd3955e5a9fad39c1329513ba06
6
+ metadata.gz: aced1491d337a12c47163792ec3f0f2bc413f2fd358536fe405c39d33afe22a0417a9388160384ae091006b7b77a2c498d2db3801f8062c5885f12cffb34f0f7
7
+ data.tar.gz: 2833c3916bc3538c4a8d6cf49bc8d17b1711b407489e2bc7fe1204026a3b3f5ddca5155a15fb2e377eb6c1b54582d99b2efbd02fc2f34186524e14991e03165f
@@ -0,0 +1,181 @@
1
+ # As the name suggests, still is a copy of the rails/actionpack remote_ip middleware
2
+ # It's duplicated here, in case it's not already available (e.g. in older rails versions),
3
+ # because we don't want to add actionpack as dependecy.
4
+ #
5
+ # see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb
6
+
7
+ require 'ipaddr'
8
+
9
+ module LogjamAgent
10
+ module ActionDispatch
11
+ # This middleware calculates the IP address of the remote client that is
12
+ # making the request. It does this by checking various headers that could
13
+ # contain the address, and then picking the last-set address that is not
14
+ # on the list of trusted IPs. This follows the precedent set by e.g.
15
+ # {the Tomcat server}[https://issues.apache.org/bugzilla/show_bug.cgi?id=50453],
16
+ # with {reasoning explained at length}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection]
17
+ # by @gingerlime. A more detailed explanation of the algorithm is given
18
+ # at GetIp#calculate_ip.
19
+ #
20
+ # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
21
+ # requires. Some Rack servers simply drop preceding headers, and only report
22
+ # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
23
+ # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
24
+ # then you should test your Rack server to make sure your data is good.
25
+ #
26
+ # IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
27
+ # This middleware assumes that there is at least one proxy sitting around
28
+ # and setting headers with the client's remote IP address. If you don't use
29
+ # a proxy, because you are hosted on e.g. Heroku without SSL, any client can
30
+ # claim to have any IP address by setting the X-Forwarded-For header. If you
31
+ # care about that, then you need to explicitly drop or ignore those headers
32
+ # sometime before this middleware runs.
33
+ class RemoteIp
34
+ class IpSpoofAttackError < StandardError; end
35
+
36
+ # The default trusted IPs list simply includes IP addresses that are
37
+ # guaranteed by the IP specification to be private addresses. Those will
38
+ # not be the ultimate client IP in production, and so are discarded. See
39
+ # http://en.wikipedia.org/wiki/Private_network for details.
40
+ TRUSTED_PROXIES = [
41
+ "127.0.0.1", # localhost IPv4
42
+ "::1", # localhost IPv6
43
+ "fc00::/7", # private IPv6 range fc00::/7
44
+ "10.0.0.0/8", # private IPv4 range 10.x.x.x
45
+ "172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
46
+ "192.168.0.0/16", # private IPv4 range 192.168.x.x
47
+ ].map { |proxy| IPAddr.new(proxy) }
48
+
49
+ attr_reader :check_ip, :proxies
50
+
51
+ # Create a new +RemoteIp+ middleware instance.
52
+ #
53
+ # The +check_ip_spoofing+ option is on by default. When on, an exception
54
+ # is raised if it looks like the client is trying to lie about its own IP
55
+ # address. It makes sense to turn off this check on sites aimed at non-IP
56
+ # clients (like WAP devices), or behind proxies that set headers in an
57
+ # incorrect or confusing way (like AWS ELB).
58
+ #
59
+ # The +custom_proxies+ argument can take an Array of string, IPAddr, or
60
+ # Regexp objects which will be used instead of +TRUSTED_PROXIES+. If a
61
+ # single string, IPAddr, or Regexp object is provided, it will be used in
62
+ # addition to +TRUSTED_PROXIES+. Any proxy setup will put the value you
63
+ # want in the middle (or at the beginning) of the X-Forwarded-For list,
64
+ # with your proxy servers after it. If your proxies aren't removed, pass
65
+ # them in via the +custom_proxies+ parameter. That way, the middleware will
66
+ # ignore those IP addresses, and return the one that you want.
67
+ def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
68
+ @app = app
69
+ @check_ip = check_ip_spoofing
70
+ @proxies = if custom_proxies.blank?
71
+ TRUSTED_PROXIES
72
+ elsif custom_proxies.respond_to?(:any?)
73
+ custom_proxies
74
+ else
75
+ Array(custom_proxies) + TRUSTED_PROXIES
76
+ end
77
+ end
78
+
79
+ # Since the IP address may not be needed, we store the object here
80
+ # without calculating the IP to keep from slowing down the majority of
81
+ # requests. For those requests that do need to know the IP, the
82
+ # GetIp#calculate_ip method will calculate the memoized client IP address.
83
+ def call(env)
84
+ env["action_dispatch.remote_ip"] = GetIp.new(env, self)
85
+ @app.call(env)
86
+ end
87
+
88
+ # The GetIp class exists as a way to defer processing of the request data
89
+ # into an actual IP address. If the ActionDispatch::Request#remote_ip method
90
+ # is called, this class will calculate the value and then memoize it.
91
+ class GetIp
92
+ def initialize(env, middleware)
93
+ @env = env
94
+ @check_ip = middleware.check_ip
95
+ @proxies = middleware.proxies
96
+ end
97
+
98
+ # Sort through the various IP address headers, looking for the IP most
99
+ # likely to be the address of the actual remote client making this
100
+ # request.
101
+ #
102
+ # REMOTE_ADDR will be correct if the request is made directly against the
103
+ # Ruby process, on e.g. Heroku. When the request is proxied by another
104
+ # server like HAProxy or NGINX, the IP address that made the original
105
+ # request will be put in an X-Forwarded-For header. If there are multiple
106
+ # proxies, that header may contain a list of IPs. Other proxy services
107
+ # set the Client-Ip header instead, so we check that too.
108
+ #
109
+ # As discussed in {this post about Rails IP Spoofing}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/],
110
+ # while the first IP in the list is likely to be the "originating" IP,
111
+ # it could also have been set by the client maliciously.
112
+ #
113
+ # In order to find the first address that is (probably) accurate, we
114
+ # take the list of IPs, remove known and trusted proxies, and then take
115
+ # the last address left, which was presumably set by one of those proxies.
116
+ def calculate_ip
117
+ # Set by the Rack web server, this is a single value.
118
+ remote_addr = ips_from('REMOTE_ADDR').last
119
+
120
+ # Could be a CSV list and/or repeated headers that were concatenated.
121
+ client_ips = ips_from('HTTP_CLIENT_IP').reverse
122
+ forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
123
+
124
+ # +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
125
+ # If they are both set, it means that this request passed through two
126
+ # proxies with incompatible IP header conventions, and there is no way
127
+ # for us to determine which header is the right one after the fact.
128
+ # Since we have no idea, we give up and explode.
129
+ should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
130
+ if should_check_ip && !forwarded_ips.include?(client_ips.last)
131
+ # We don't know which came from the proxy, and which from the user
132
+ raise IpSpoofAttackError, "IP spoofing attack?! " +
133
+ "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
134
+ "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
135
+ end
136
+
137
+ # We assume these things about the IP headers:
138
+ #
139
+ # - X-Forwarded-For will be a list of IPs, one per proxy, or blank
140
+ # - Client-Ip is propagated from the outermost proxy, or is blank
141
+ # - REMOTE_ADDR will be the IP that made the request to Rack
142
+ ips = [forwarded_ips, client_ips, remote_addr].flatten.compact
143
+
144
+ # If every single IP option is in the trusted list, just return REMOTE_ADDR
145
+ filter_proxies(ips).first || remote_addr
146
+ end
147
+
148
+ # Memoizes the value returned by #calculate_ip and returns it for
149
+ # ActionDispatch::Request to use.
150
+ def to_s
151
+ @ip ||= calculate_ip
152
+ end
153
+
154
+ protected
155
+
156
+ def ips_from(header)
157
+ # Split the comma-separated list into an array of strings
158
+ ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
159
+ ips.select do |ip|
160
+ begin
161
+ # Only return IPs that are valid according to the IPAddr#new method
162
+ range = IPAddr.new(ip).to_range
163
+ # we want to make sure nobody is sneaking a netmask in
164
+ range.begin == range.end
165
+ rescue ArgumentError
166
+ nil
167
+ end
168
+ end
169
+ end
170
+
171
+ def filter_proxies(ips)
172
+ ips.reject do |ip|
173
+ @proxies.any? { |proxy| proxy === ip }
174
+ end
175
+ end
176
+
177
+ end
178
+
179
+ end
180
+ end
181
+ end
@@ -15,11 +15,6 @@ module LogjamAgent
15
15
  ensure
16
16
  headers = result[1]
17
17
  headers["X-Logjam-Request-Id"] = request.id
18
- # this cookie is intended for JS code, which can't read it from response headers.
19
- # it's superior to returning it with the html body, because that makes caching hard.
20
- if set_request_id_cookie?(request, env)
21
- ::Rack::Utils.set_cookie_header!(headers, "X-Logjam-Request-Id", {:value => request.id, :path => "/"})
22
- end
23
18
  unless (request_action = request.fields[:action]).blank?
24
19
  headers["X-Logjam-Request-Action"] = request_action
25
20
  end
@@ -42,9 +37,5 @@ module LogjamAgent
42
37
  def finish_request(env)
43
38
  LogjamAgent.finish_request(env["time_bandits.metrics"])
44
39
  end
45
-
46
- def set_request_id_cookie?(request, env)
47
- !(request.ignored? || env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest")
48
- end
49
40
  end
50
41
  end
@@ -77,7 +77,7 @@ module LogjamAgent
77
77
 
78
78
  logjam_request.start_time = start_time
79
79
  logjam_fields = logjam_request.fields
80
- ip = LogjamAgent.ip_obfuscator(request.ip)
80
+ ip = LogjamAgent.ip_obfuscator(env["action_dispatch.remote_ip"])
81
81
  logjam_fields.merge!(:ip => ip, :host => @hostname)
82
82
  logjam_fields.merge!(extract_request_info(request))
83
83
 
@@ -48,6 +48,14 @@ module LogjamAgent
48
48
  app.config.middleware.swap("TimeBandits::Rack::Logger", "LogjamAgent::Rack::Logger")
49
49
  app.config.middleware.insert_before("LogjamAgent::Rack::Logger", "LogjamAgent::Middleware")
50
50
 
51
+ if defined?(::ActionDispatch::RemoteIp)
52
+ app.config.middleware.delete ::ActionDispatch::RemoteIp
53
+ app.config.middleware.insert_before LogjamAgent::Middleware, ::ActionDispatch::RemoteIp, app.config.action_dispatch.ip_spoofing_check, app.config.action_dispatch.trusted_proxies
54
+ else
55
+ require 'logjam_agent/actionpack/lib/action_dispatch/middleware/remote_ip'
56
+ app.config.middleware.insert_before LogjamAgent::Middleware, LogjamAgent::ActionDispatch::RemoteIp
57
+ end
58
+
51
59
  # install a default error handler for forwarding errors
52
60
  log_dir = File.dirname(logjam_log_path(app))
53
61
  begin
@@ -79,38 +87,6 @@ module LogjamAgent
79
87
  end
80
88
  end
81
89
  end
82
-
83
- # patch rack so that the ip method returns the same result as rails remote_ip (modulo exceptions)
84
- app.config.after_initialize do
85
- if app.config.action_dispatch.trusted_proxies
86
- trusted_proxies = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|^::1$|^fd[0-9a-f]{2}:.+|^localhost$/i
87
- trusted_proxies = Regexp.union(trusted_proxies, app.config.action_dispatch.trusted_proxies)
88
- ::Rack::Request.class_eval <<-"EVA"
89
- def trusted_proxy?(ip)
90
- ip =~ #{trusted_proxies.inspect}
91
- end
92
- EVA
93
- end
94
-
95
- ::Rack::Request.class_eval <<-EVA
96
- def ip
97
- remote_addrs = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
98
- remote_addrs.reject! { |addr| trusted_proxy?(addr) }
99
-
100
- return remote_addrs.first if remote_addrs.any?
101
-
102
- forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
103
-
104
- if client_ip = @env['HTTP_TRUE_CLIENT_IP'] || @env['HTTP_CLIENT_IP']
105
- # If forwarded_ips doesn't include the client_ip, it might be an
106
- # ip spoofing attempt, so we ignore HTTP_CLIENT_IP
107
- return client_ip if forwarded_ips.include?(client_ip)
108
- end
109
-
110
- return forwarded_ips.reject { |ip| trusted_proxy?(ip) }.last || @env["REMOTE_ADDR"]
111
- end
112
- EVA
113
- end
114
90
  end
115
91
 
116
92
  # avoid garbled tempfile information in the logs
@@ -1,3 +1,3 @@
1
1
  module LogjamAgent
2
- VERSION = "0.18.0"
2
+ VERSION = "0.19.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logjam_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Kaes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-25 00:00:00.000000000 Z
11
+ date: 2016-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -120,6 +120,7 @@ files:
120
120
  - README.md
121
121
  - Rakefile
122
122
  - lib/logjam_agent.rb
123
+ - lib/logjam_agent/actionpack/lib/action_dispatch/middleware/remote_ip.rb
123
124
  - lib/logjam_agent/amqp_forwarder.rb
124
125
  - lib/logjam_agent/buffered_logger.rb
125
126
  - lib/logjam_agent/forwarders.rb