wafris 1.1.11 → 2.0.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 +4 -4
- data/lib/wafris/configuration.rb +121 -37
- data/lib/wafris/log_suppressor.rb +3 -2
- data/lib/wafris/middleware.rb +45 -23
- data/lib/wafris/version.rb +1 -1
- data/lib/wafris.rb +582 -38
- metadata +44 -19
- data/lib/lua/dist/wafris_core.lua +0 -305
data/lib/wafris.rb
CHANGED
@@ -1,61 +1,605 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
|
4
4
|
require 'rails'
|
5
|
-
require '
|
5
|
+
require 'sqlite3'
|
6
|
+
require 'ipaddr'
|
7
|
+
require 'httparty'
|
8
|
+
require 'awesome_print'
|
6
9
|
|
7
10
|
require 'wafris/configuration'
|
8
11
|
require 'wafris/middleware'
|
9
12
|
require 'wafris/log_suppressor'
|
10
13
|
|
11
14
|
require 'wafris/railtie' if defined?(Rails::Railtie)
|
15
|
+
ActiveSupport::Deprecation.behavior = :silence
|
12
16
|
|
13
17
|
module Wafris
|
14
18
|
class << self
|
15
19
|
attr_accessor :configuration
|
16
20
|
|
17
21
|
def configure
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
"[Wafris] block is required to configure Wafris. More info can be found at: https://github.com/Wafris/wafris-rb"
|
29
|
-
)
|
30
|
-
rescue StandardError => e
|
31
|
-
LogSuppressor.puts_log(
|
32
|
-
"[Wafris] firewall disabled due to: #{e.message}. Cannot connect via Wafris.configure. Please check your configuration settings. More info can be found at: https://github.com/Wafris/wafris-rb"
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
def allow_request?(request)
|
37
|
-
configuration.connection_pool.with do |conn|
|
38
|
-
time = Time.now.utc.to_i * 1000
|
39
|
-
status = conn.evalsha(
|
40
|
-
configuration.core_sha,
|
41
|
-
argv: [
|
42
|
-
request.ip,
|
43
|
-
IPAddr.new(request.ip).to_i,
|
44
|
-
time,
|
45
|
-
request.user_agent,
|
46
|
-
request.path,
|
47
|
-
request.query_string,
|
48
|
-
request.host,
|
49
|
-
request.request_method
|
50
|
-
]
|
51
|
-
)
|
22
|
+
begin
|
23
|
+
self.configuration ||= Wafris::Configuration.new
|
24
|
+
yield(configuration)
|
25
|
+
|
26
|
+
LogSuppressor.puts_log("[Wafris] Configuration settings created.")
|
27
|
+
configuration.create_settings
|
28
|
+
|
29
|
+
rescue StandardError => e
|
30
|
+
puts "[Wafris] firewall disabled due to: #{e.message}. Cannot connect via Wafris.configure. Please check your configuration settings. More info can be found at: https://github.com/Wafris/wafris-rb"
|
31
|
+
end
|
52
32
|
|
53
|
-
|
33
|
+
end
|
34
|
+
|
35
|
+
def zero_pad(number, length)
|
36
|
+
number.to_s.rjust(length, "0")
|
37
|
+
end
|
38
|
+
|
39
|
+
def ip_to_decimal_lexical_string(ip)
|
40
|
+
num = 0
|
41
|
+
|
42
|
+
if ip.include?(":")
|
43
|
+
ip = IPAddr.new(ip).to_string
|
44
|
+
hex = ip.delete(":")
|
45
|
+
(0...hex.length).step(4) do |i|
|
46
|
+
chunk = hex[i, 4].to_i(16)
|
47
|
+
num = num * (2**16) + chunk
|
48
|
+
end
|
49
|
+
elsif ip.include?(".")
|
50
|
+
ip.split(".").each do |chunk|
|
51
|
+
num = num * 256 + chunk.to_i
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
str = num.to_s
|
56
|
+
zero_pad(str, 39)
|
57
|
+
end
|
58
|
+
|
59
|
+
def ip_in_cidr_range(ip_address, table_name, db_connection)
|
60
|
+
lexical_address = ip_to_decimal_lexical_string(ip_address)
|
61
|
+
higher_value = db_connection.get_first_value("SELECT * FROM #{table_name} WHERE member > ? ORDER BY member ASC", [lexical_address])
|
62
|
+
lower_value = db_connection.get_first_value("SELECT * FROM #{table_name} WHERE member < ? ORDER BY member DESC", [lexical_address])
|
63
|
+
|
64
|
+
if higher_value.nil? || lower_value.nil?
|
65
|
+
return nil
|
66
|
+
else
|
67
|
+
higher_compare = higher_value.split("-").last
|
68
|
+
lower_compare = lower_value.split("-").last
|
69
|
+
|
70
|
+
if higher_compare == lower_compare
|
71
|
+
return lower_compare
|
72
|
+
else
|
73
|
+
return nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_country_code(ip, db_connection)
|
79
|
+
country_code = ip_in_cidr_range(ip, 'country_ip_ranges', db_connection)
|
80
|
+
|
81
|
+
if country_code
|
82
|
+
country_code = country_code.split("_").first.split("G").last
|
83
|
+
return country_code
|
84
|
+
else
|
85
|
+
return "ZZ"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def substring_match(request_property, table_name, db_connection)
|
90
|
+
result = db_connection.execute("SELECT entries FROM #{table_name}")
|
91
|
+
result.flatten.each do |entry|
|
92
|
+
if request_property.include?(entry)
|
93
|
+
return entry
|
94
|
+
end
|
95
|
+
end
|
96
|
+
return false
|
97
|
+
end
|
98
|
+
|
99
|
+
def exact_match(request_property, table_name, db_connection)
|
100
|
+
result = db_connection.execute("SELECT entries FROM #{table_name} WHERE entries = ?", [request_property])
|
101
|
+
return result.any?
|
102
|
+
end
|
103
|
+
|
104
|
+
def check_rate_limit(ip, path, method, db_connection)
|
105
|
+
|
106
|
+
# Correctly format the SQL query with placeholders
|
107
|
+
limiters = db_connection.execute("SELECT * FROM blocked_rate_limits WHERE path = ? AND method = ?", [path, method])
|
108
|
+
|
109
|
+
# If no rate limiters are matched
|
110
|
+
if limiters.empty?
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
|
114
|
+
current_timestamp = Time.now.to_i
|
115
|
+
|
116
|
+
# If any rate limiters are matched
|
117
|
+
# This implementation will block the request on any of the rate limiters
|
118
|
+
limiters.each do |limiter|
|
119
|
+
|
120
|
+
# Limiter array mapping
|
121
|
+
# 0: id
|
122
|
+
# 1: path
|
123
|
+
# 2: method
|
124
|
+
# 3: interval
|
125
|
+
# 4: max_count
|
126
|
+
# 5: rule_id
|
127
|
+
|
128
|
+
interval = limiter[3]
|
129
|
+
max_count = limiter[4]
|
130
|
+
rule_id = limiter[5]
|
131
|
+
|
132
|
+
# Expire old timestamps
|
133
|
+
@configuration.rate_limiters.each do |ip, timestamps|
|
134
|
+
# Removes timestamps older than the interval
|
135
|
+
|
136
|
+
@configuration.rate_limiters[ip] = timestamps.select { |timestamp| timestamp > current_timestamp - interval }
|
137
|
+
|
138
|
+
# Remove the IP if there are no more timestamps for the IP
|
139
|
+
@configuration.rate_limiters.delete(ip) if @configuration.rate_limiters[ip].empty?
|
140
|
+
end
|
141
|
+
|
142
|
+
# Check if the IP+Method is rate limited
|
143
|
+
|
144
|
+
if @configuration.rate_limiters[ip] && @configuration.rate_limiters[ip].length >= max_count
|
145
|
+
# Request is rate limited
|
146
|
+
|
147
|
+
|
148
|
+
return rule_id
|
149
|
+
|
150
|
+
else
|
151
|
+
# Request is not rate limited, so add the current timestamp
|
152
|
+
if @configuration.rate_limiters[ip]
|
153
|
+
@configuration.rate_limiters[ip] << current_timestamp
|
154
|
+
else
|
155
|
+
@configuration.rate_limiters[ip] = [current_timestamp]
|
156
|
+
end
|
157
|
+
|
54
158
|
return false
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
def send_upsync_requests(requests_array)
|
166
|
+
|
167
|
+
begin
|
168
|
+
|
169
|
+
headers = {'Content-Type' => 'application/json'}
|
170
|
+
|
171
|
+
if Rails && Rails.application
|
172
|
+
framework = "Rails v#{Rails::VERSION::STRING}"
|
173
|
+
else
|
174
|
+
framework = "Rack v#{Rack::VERSION::STRING}"
|
175
|
+
end
|
176
|
+
|
177
|
+
body = {
|
178
|
+
meta: {
|
179
|
+
version: Wafris::VERSION,
|
180
|
+
client: 'wafris-rb',
|
181
|
+
framework: framework
|
182
|
+
},
|
183
|
+
batch: requests_array
|
184
|
+
}.to_json
|
185
|
+
|
186
|
+
url_and_api_key = @configuration.upsync_url + '/' + @configuration.api_key
|
187
|
+
|
188
|
+
response = HTTParty.post(url_and_api_key,
|
189
|
+
:body => body,
|
190
|
+
:headers => headers,
|
191
|
+
:timeout => 300)
|
192
|
+
|
193
|
+
if response.code == 200
|
194
|
+
@configuration.upsync_status = 'Complete'
|
195
|
+
else
|
196
|
+
LogSuppressor.puts_log("Upsync Error. HTTP Response: #{response.code}")
|
197
|
+
end
|
198
|
+
rescue HTTParty::Error => e
|
199
|
+
LogSuppressor.puts_log("Upsync Error. Failed to send upsync requests: #{e.message}")
|
200
|
+
end
|
201
|
+
return true
|
202
|
+
end
|
203
|
+
|
204
|
+
# This method is used to queue upsync requests. It takes in several parameters including
|
205
|
+
# ip, user_agent, path, parameters, host, method, treatment, category, and rule.
|
206
|
+
#
|
207
|
+
# The 'treatment' parameter represents the action taken on the request, which can be
|
208
|
+
# 'Allowed', 'Blocked', or 'Passed'.
|
209
|
+
#
|
210
|
+
# The 'category' parameter represents the category of the rule that was matched, such as
|
211
|
+
# 'blocked_ip', 'allowed_cidr', etc.
|
212
|
+
#
|
213
|
+
# The 'rule' parameter represents the specific rule that was matched within the category
|
214
|
+
# ex: '192.23.5.4', 'SemRush', etc.
|
215
|
+
def queue_upsync_request(ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp)
|
216
|
+
|
217
|
+
if @configuration.upsync_status != 'Disabled' || @configuration.upsync_status != 'Uploading'
|
218
|
+
@configuration.upsync_status = 'Uploading'
|
219
|
+
|
220
|
+
# Add request to the queue
|
221
|
+
request = [ip, user_agent, path, parameters, host, method, treatment, category, rule, request_id, request_timestamp]
|
222
|
+
@configuration.upsync_queue << request
|
223
|
+
|
224
|
+
# If the queue is full, send the requests to the upsync server
|
225
|
+
if @configuration.upsync_queue.length >= @configuration.upsync_queue_limit || (Time.now.to_i - @configuration.last_upsync_timestamp) >= @configuration.upsync_interval
|
226
|
+
requests_array = @configuration.upsync_queue
|
227
|
+
@configuration.upsync_queue = []
|
228
|
+
@configuration.last_upsync_timestamp = Time.now.to_i
|
229
|
+
|
230
|
+
send_upsync_requests(requests_array)
|
231
|
+
end
|
232
|
+
|
233
|
+
@configuration.upsync_status = 'Enabled'
|
234
|
+
# Return the treatment - used to return 403 or 200
|
235
|
+
|
236
|
+
message = "Request #{treatment}"
|
237
|
+
message += " | Category: #{category}" unless category.blank?
|
238
|
+
message += " | Rule: #{rule}" unless rule.blank?
|
239
|
+
LogSuppressor.puts_log(message)
|
240
|
+
|
241
|
+
return treatment
|
242
|
+
else
|
243
|
+
@configuration.upsync_status = 'Enabled'
|
244
|
+
return "Passed"
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
# Pulls the latest rules from the server
|
250
|
+
def downsync_db(db_rule_category, current_filename = nil)
|
251
|
+
|
252
|
+
lockfile_path = "#{@configuration.db_file_path}/#{db_rule_category}.lockfile"
|
253
|
+
|
254
|
+
# Ensure the directory exists before attempting to open the lockfile
|
255
|
+
FileUtils.mkdir_p(@configuration.db_file_path) unless Dir.exist?(@configuration.db_file_path)
|
256
|
+
|
257
|
+
# Attempt to create a lockfile with exclusive access; skip if it exists
|
258
|
+
begin
|
259
|
+
lockfile = File.open(lockfile_path, File::RDWR|File::CREAT|File::EXCL)
|
260
|
+
rescue Errno::EEXIST
|
261
|
+
LogSuppressor.puts_log("[Wafris][Downsync] Lockfile already exists, skipping downsync.")
|
262
|
+
return
|
263
|
+
rescue Exception => e
|
264
|
+
LogSuppressor.puts_log("[Wafris][Downsync] Error creating lockfile: #{e.message}")
|
265
|
+
end
|
266
|
+
|
267
|
+
begin
|
268
|
+
# Actual Downsync operations
|
269
|
+
filename = ""
|
270
|
+
|
271
|
+
if Rails && Rails.application
|
272
|
+
framework = "Rails v#{Rails::VERSION::STRING}"
|
55
273
|
else
|
56
|
-
|
274
|
+
framework = "Rack v#{Rack::VERSION::STRING}"
|
57
275
|
end
|
276
|
+
|
277
|
+
data = {
|
278
|
+
client_db: current_filename,
|
279
|
+
process_id: Process.pid,
|
280
|
+
hostname: Socket.gethostname,
|
281
|
+
version: Wafris::VERSION,
|
282
|
+
client: 'wafris-rb',
|
283
|
+
framework: framework
|
284
|
+
}
|
285
|
+
|
286
|
+
# Check server for new rules including process id
|
287
|
+
#puts "Downloading from #{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?current_version=#{current_filename}&process_id=#{Process.pid}"
|
288
|
+
uri = "#{@configuration.downsync_url}/#{db_rule_category}/#{@configuration.api_key}?#{data.to_query}"
|
289
|
+
|
290
|
+
response = HTTParty.get(
|
291
|
+
uri,
|
292
|
+
follow_redirects: true, # Enable following redirects
|
293
|
+
max_redirects: 2 # Maximum number of redirects to follow
|
294
|
+
)
|
295
|
+
|
296
|
+
# TODO: What to do if timeout
|
297
|
+
# TODO: What to do if error
|
298
|
+
|
299
|
+
if response.code == 401
|
300
|
+
@configuration.upsync_status = 'Disabled'
|
301
|
+
LogSuppressor.puts_log("[Wafris][Downsync] Unauthorized: Bad or missing API key")
|
302
|
+
LogSuppressor.puts_log("[Wafris][Downsync] API Key: #{@configuration.api_key}")
|
303
|
+
filename = current_filename
|
304
|
+
|
305
|
+
elsif response.code == 304
|
306
|
+
@configuration.upsync_status = 'Enabled'
|
307
|
+
LogSuppressor.puts_log("[Wafris][Downsync] No new rules to download")
|
308
|
+
|
309
|
+
filename = current_filename
|
310
|
+
|
311
|
+
elsif response.code == 200
|
312
|
+
@configuration.upsync_status = 'Enabled'
|
313
|
+
|
314
|
+
if current_filename
|
315
|
+
old_file_name = current_filename
|
316
|
+
end
|
317
|
+
|
318
|
+
# Extract the filename from the response
|
319
|
+
content_disposition = response.headers['content-disposition']
|
320
|
+
filename = content_disposition.split('filename=')[1].strip
|
321
|
+
|
322
|
+
# Save the body of the response to a new SQLite file
|
323
|
+
File.open(@configuration.db_file_path + "/" + filename, 'wb') { |file| file.write(response.body) }
|
324
|
+
|
325
|
+
# Write the filename into the db_category.modfile
|
326
|
+
File.open("#{@configuration.db_file_path}/#{db_rule_category}.modfile", 'w') { |file| file.write(filename) }
|
327
|
+
|
328
|
+
# Sanity check that the downloaded db file has tables
|
329
|
+
# not empty or corrupted
|
330
|
+
db = SQLite3::Database.new @configuration.db_file_path + "/" + filename
|
331
|
+
if db.execute("SELECT name FROM sqlite_master WHERE type='table';").any?
|
332
|
+
# Remove the old database file
|
333
|
+
if old_file_name
|
334
|
+
if File.exist?(@configuration.db_file_path + "/" + old_file_name)
|
335
|
+
File.delete(@configuration.db_file_path + "/" + old_file_name)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# DB file is bad or empty so keep using whatever we have now
|
340
|
+
else
|
341
|
+
filename = old_file_name
|
342
|
+
LogSuppressor.puts_log("[Wafris][Downsync] DB Error - No tables exist in the db file #{@configuration.db_file_path}/#{filename}")
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
rescue Exception => e
|
349
|
+
LogSuppressor.puts_log("[Wafris][Downsync] Error downloading rules: #{e.message}")
|
350
|
+
|
351
|
+
# This gets set even if the API key is bad or other issues
|
352
|
+
# to prevent hammering the distribution server on every request
|
353
|
+
ensure
|
354
|
+
|
355
|
+
# Reset the modified time of the modfile
|
356
|
+
unless File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
357
|
+
File.new("#{@configuration.db_file_path}/#{db_rule_category}.modfile", 'w')
|
358
|
+
end
|
359
|
+
|
360
|
+
# Set the modified time of the modfile to the current time
|
361
|
+
File.utime(Time.now, Time.now, "#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
362
|
+
|
363
|
+
# Ensure the lockfile is removed after operations
|
364
|
+
lockfile.close
|
365
|
+
File.delete(lockfile_path)
|
366
|
+
end
|
367
|
+
|
368
|
+
return filename
|
369
|
+
|
370
|
+
end
|
371
|
+
|
372
|
+
# Returns the current database file,
|
373
|
+
# if the file is older than the interval, it will download the latest db
|
374
|
+
# if the file doesn't exist, it will download the latest db
|
375
|
+
# if the lockfile exists, it will return the current db
|
376
|
+
def current_db(db_rule_category)
|
377
|
+
|
378
|
+
if db_rule_category == 'custom_rules'
|
379
|
+
interval = @configuration.downsync_custom_rules_interval
|
380
|
+
else
|
381
|
+
interval = @configuration.downsync_data_subscriptions_interval
|
382
|
+
end
|
383
|
+
|
384
|
+
# Checks for existing current modfile, which contains the current db filename
|
385
|
+
if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.modfile")
|
386
|
+
|
387
|
+
LogSuppressor.puts_log("[Wafris][Downsync] Modfile exists, skipping downsync")
|
388
|
+
|
389
|
+
# Get last Modified Time and current database file name
|
390
|
+
last_db_synctime = File.mtime("#{@configuration.db_file_path}/#{db_rule_category}.modfile").to_i
|
391
|
+
returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
|
392
|
+
|
393
|
+
LogSuppressor.puts_log("[Wafris][Downsync] Modfile Last Modified Time: #{last_db_synctime}")
|
394
|
+
LogSuppressor.puts_log("[Wafris][Downsync] DB in Modfile: #{returned_db}")
|
395
|
+
|
396
|
+
# Check if the db file is older than the interval
|
397
|
+
if (Time.now.to_i - last_db_synctime) > interval
|
398
|
+
|
399
|
+
LogSuppressor.puts_log("[Wafris][Downsync] DB is older than the interval")
|
400
|
+
|
401
|
+
# Make sure that another process isn't already downloading the rules
|
402
|
+
if !File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
|
403
|
+
returned_db = downsync_db(db_rule_category, returned_db)
|
404
|
+
end
|
405
|
+
|
406
|
+
return returned_db
|
407
|
+
|
408
|
+
# Current db is up to date
|
409
|
+
else
|
410
|
+
|
411
|
+
LogSuppressor.puts_log("[Wafris][Downsync] DB is up to date")
|
412
|
+
|
413
|
+
returned_db = File.read("#{@configuration.db_file_path}/#{db_rule_category}.modfile").strip
|
414
|
+
|
415
|
+
# If the modfile is empty (no db file name), return nil
|
416
|
+
# this can happen if the the api key is bad
|
417
|
+
if returned_db == ''
|
418
|
+
return ''
|
419
|
+
else
|
420
|
+
return returned_db
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
# No modfile exists, so download the latest db
|
426
|
+
else
|
427
|
+
|
428
|
+
LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
|
429
|
+
|
430
|
+
# Make sure that another process isn't already downloading the rules
|
431
|
+
if File.exist?("#{@configuration.db_file_path}/#{db_rule_category}.lockfile")
|
432
|
+
LogSuppressor.puts_log("[Wafris][Downsync] Lockfile exists, skipping downsync")
|
433
|
+
# Lockfile exists, but no modfile with a db filename
|
434
|
+
return nil
|
435
|
+
else
|
436
|
+
|
437
|
+
LogSuppressor.puts_log("[Wafris][Downsync] No modfile exists, downloading latest db")
|
438
|
+
# No modfile exists, so download the latest db
|
439
|
+
returned_db = downsync_db(db_rule_category, nil)
|
440
|
+
|
441
|
+
if returned_db.nil?
|
442
|
+
return nil
|
443
|
+
else
|
444
|
+
return returned_db
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
448
|
+
|
449
|
+
end
|
450
|
+
|
451
|
+
end
|
452
|
+
|
453
|
+
# This is the main loop that evaluates the request
|
454
|
+
# as well as sorts out when downsync and upsync should be called
|
455
|
+
def evaluate(ip, user_agent, path, parameters, host, method, headers, body, request_id, request_timestamp)
|
456
|
+
@configuration ||= Wafris::Configuration.new
|
457
|
+
|
458
|
+
if @configuration.api_key.nil?
|
459
|
+
return "Passed"
|
460
|
+
else
|
461
|
+
|
462
|
+
rules_db_filename = current_db('custom_rules')
|
463
|
+
data_subscriptions_db_filename = current_db('data_subscriptions')
|
464
|
+
|
465
|
+
if rules_db_filename.to_s.strip != '' && data_subscriptions_db_filename.strip.to_s.strip != ''
|
466
|
+
|
467
|
+
rules_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{rules_db_filename}"
|
468
|
+
data_subscriptions_db = SQLite3::Database.new "#{@configuration.db_file_path}/#{data_subscriptions_db_filename}"
|
469
|
+
|
470
|
+
# Allowed IPs
|
471
|
+
if exact_match(ip, 'allowed_ips', rules_db)
|
472
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ai', ip, request_id, request_timestamp)
|
473
|
+
end
|
474
|
+
|
475
|
+
# Allowed CIDR Ranges
|
476
|
+
if ip_in_cidr_range(ip, 'allowed_cidr_ranges', rules_db)
|
477
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Allowed', 'ac', ip, request_id, request_timestamp)
|
478
|
+
end
|
479
|
+
|
480
|
+
# Blocked IPs
|
481
|
+
if exact_match(ip, 'blocked_ips', rules_db)
|
482
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bi', ip, request_id, request_timestamp)
|
483
|
+
end
|
484
|
+
|
485
|
+
# Blocked CIDR Ranges
|
486
|
+
if ip_in_cidr_range(ip, 'blocked_cidr_ranges', rules_db)
|
487
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bc', ip, request_id, request_timestamp)
|
488
|
+
end
|
489
|
+
|
490
|
+
# Blocked Country Codes
|
491
|
+
country_code = get_country_code(ip, data_subscriptions_db)
|
492
|
+
if exact_match(country_code, 'blocked_country_codes', rules_db)
|
493
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "G_#{country_code}", request_id, request_timestamp)
|
494
|
+
end
|
495
|
+
|
496
|
+
# Blocked Reputation IP Ranges
|
497
|
+
if ip_in_cidr_range(ip, 'reputation_ip_ranges', data_subscriptions_db)
|
498
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bs', "R", request_id, request_timestamp)
|
499
|
+
end
|
500
|
+
|
501
|
+
# Blocked User Agents
|
502
|
+
user_agent_match = substring_match(user_agent, 'blocked_user_agents', rules_db)
|
503
|
+
if user_agent_match
|
504
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bu', user_agent_match, request_id, request_timestamp)
|
505
|
+
end
|
506
|
+
|
507
|
+
# Blocked Paths
|
508
|
+
path_match = substring_match(path, 'blocked_paths', rules_db)
|
509
|
+
if path_match
|
510
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bp', path_match, request_id, request_timestamp)
|
511
|
+
end
|
512
|
+
|
513
|
+
# Blocked Parameters
|
514
|
+
parameters_match = substring_match(parameters, 'blocked_parameters', rules_db)
|
515
|
+
if parameters_match
|
516
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'ba', parameters_match, request_id, request_timestamp)
|
517
|
+
end
|
518
|
+
|
519
|
+
# Blocked Hosts
|
520
|
+
if exact_match(host, 'blocked_hosts', rules_db)
|
521
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bh', host, request_id, request_timestamp)
|
522
|
+
end
|
523
|
+
|
524
|
+
# Blocked Methods
|
525
|
+
if exact_match(method, 'blocked_methods', rules_db)
|
526
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'bm', method, request_id, request_timestamp)
|
527
|
+
end
|
528
|
+
|
529
|
+
# Rate Limiting
|
530
|
+
rule_id = check_rate_limit(ip, path, method, rules_db)
|
531
|
+
if rule_id
|
532
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Blocked', 'brl', rule_id, request_id, request_timestamp)
|
533
|
+
end
|
534
|
+
|
535
|
+
end
|
536
|
+
|
537
|
+
# Passed if no allow or block rules matched
|
538
|
+
return queue_upsync_request(ip, user_agent, path, parameters, host, method, 'Passed', 'passed', '-', request_id, request_timestamp)
|
539
|
+
|
540
|
+
end # end api_key.nil?
|
541
|
+
end # end evaluate
|
542
|
+
|
543
|
+
def debug(api_key)
|
544
|
+
|
545
|
+
if ENV['WAFRIS_API_KEY']
|
546
|
+
puts "Wafris API Key environment variable is set."
|
547
|
+
puts " - API Key: #{ENV['WAFRIS_API_KEY']}"
|
548
|
+
else
|
549
|
+
puts "Wafris API Key environment variable is not set."
|
58
550
|
end
|
551
|
+
|
552
|
+
puts "\n"
|
553
|
+
puts "Wafris Configuration:"
|
554
|
+
|
555
|
+
Wafris.configure do |config|
|
556
|
+
config.api_key = api_key
|
557
|
+
end
|
558
|
+
|
559
|
+
settings = Wafris.configuration
|
560
|
+
|
561
|
+
settings.instance_variables.each do |ivar|
|
562
|
+
puts " - #{ivar}: #{Wafris.configuration.instance_variable_get(ivar)}"
|
563
|
+
end
|
564
|
+
|
565
|
+
puts "\n"
|
566
|
+
if File.exist?(settings.db_file_path + "/" + "custom_rules.lockfile")
|
567
|
+
puts "Custom Rules Lockfile: #{settings.db_file_path}/custom_rules.lockfile exists"
|
568
|
+
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "custom_rules.lockfile")}"
|
569
|
+
else
|
570
|
+
puts "Custom Rules Lockfile: #{settings.db_file_path}/custom_rules.lockfile does not exist."
|
571
|
+
end
|
572
|
+
|
573
|
+
puts "\n"
|
574
|
+
if File.exist?(settings.db_file_path + "/" + "custom_rules.modfile")
|
575
|
+
puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile exists"
|
576
|
+
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "custom_rules.modfile")}"
|
577
|
+
puts " - Contents: #{File.open(settings.db_file_path + "/" + "custom_rules.modfile", 'r').read}"
|
578
|
+
else
|
579
|
+
puts "Custom Rules Modfile: #{settings.db_file_path}/custom_rules.modfile does not exist."
|
580
|
+
end
|
581
|
+
|
582
|
+
puts "\n"
|
583
|
+
if File.exist?(settings.db_file_path + "/" + "data_subscriptions.lockfile")
|
584
|
+
puts "Data Subscriptions Lockfile: #{settings.db_file_path}/data_subscriptions.lockfile exists"
|
585
|
+
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "data_subscriptions.lockfile")}"
|
586
|
+
else
|
587
|
+
puts "Data Subscriptions Lockfile: #{settings.db_file_path}/data_subscriptions.lockfile does not exist."
|
588
|
+
end
|
589
|
+
|
590
|
+
puts "\n"
|
591
|
+
if File.exist?(settings.db_file_path + "/" + "data_subscriptions.modfile")
|
592
|
+
puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile exists"
|
593
|
+
puts " - Last Modified Time: #{File.mtime(settings.db_file_path + "/" + "data_subscriptions.modfile")}"
|
594
|
+
puts " - Contents: #{File.open(settings.db_file_path + "/" + "data_subscriptions.modfile", 'r').read}"
|
595
|
+
else
|
596
|
+
puts "Data Subscriptions Modfile: #{settings.db_file_path}/data_subscriptions.modfile does not exist."
|
597
|
+
end
|
598
|
+
|
599
|
+
|
600
|
+
|
601
|
+
return true
|
59
602
|
end
|
603
|
+
|
60
604
|
end
|
61
605
|
end
|