logjam_agent 0.18.0 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 758acadd75c92e8f90ec5048473e2f5790d708bb
|
4
|
+
data.tar.gz: b63d83423a36353e614eca9e0ac25716b498f327
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(
|
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
|
|
data/lib/logjam_agent/railtie.rb
CHANGED
@@ -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
|
data/lib/logjam_agent/version.rb
CHANGED
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.
|
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-
|
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
|