wafris 1.0.0 → 1.1.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
  SHA256:
3
- metadata.gz: 1f51807b65d4eeeb7350206f2d739304f2208cac95ed472d98472c50afc25c1a
4
- data.tar.gz: e8a22ef9c9b710435c60367d46c2f72c162484bd0a10a139743f143fd7fa4252
3
+ metadata.gz: 919967ec8203e767f77202e17964dbcfd21f60422bb91bd61b806575886c5dbf
4
+ data.tar.gz: ffe9b975f6613e1742c90b6335e85f53769d439d32f014517487142a97edb37e
5
5
  SHA512:
6
- metadata.gz: b22cc90428fe61f9777bcc3e7a5e482023f791e72efd7229d70ca4b1070ba7d03b9e7de7b93fb328bbdb465192ba26475c707c8e7e938c9a03de9c23d91dc41a
7
- data.tar.gz: '0841925bb939e194c4c0a41a21be9f3300b590213a83b0ea71e66f5d752dbbb1e32eacba6b747b13417ceec46833ecf9a23e9594e592f1a9e4f6f88edd8e2495'
6
+ metadata.gz: 4efff1ae48ec5417bf9917c0a3b7f073064216ee718eed8246f0b993397454ab4296a2e27ac48a1289f31abe20ca277d32f4bc848978a7c4dd88d04ee08c4cdb
7
+ data.tar.gz: 274d14a646e0e706a940d16c53adb050045df1593056f012ba47666af402c37e38a055038fa2074a73e32e73cc49bcf5f5d943c16e7bff71bd3d79d47e8aaadc
@@ -1,5 +1,9 @@
1
- local use_timestamps_as_request_ids = false
1
+
2
+
3
+ local USE_TIMESTAMPS_AS_REQUEST_IDS = false
2
4
  local EXPIRATION_IN_SECONDS = 86400
5
+ local EXPIRATION_OFFSET_IN_SECONDS = 3600
6
+
3
7
 
4
8
  local function get_timebucket(timestamp_in_seconds)
5
9
  local startOfHourTimestamp = math.floor(timestamp_in_seconds / 3600) * 3600
@@ -7,6 +11,7 @@ local function get_timebucket(timestamp_in_seconds)
7
11
  end
8
12
 
9
13
  local function set_property_value_id_lookups(property_abbreviation, property_value)
14
+
10
15
  local value_key = property_abbreviation .. "V" .. property_value
11
16
  local property_id = redis.call("GET", value_key)
12
17
 
@@ -15,25 +20,29 @@ local function set_property_value_id_lookups(property_abbreviation, property_val
15
20
  redis.call("SET", value_key, property_id)
16
21
  redis.call("SET", property_abbreviation .. "I" .. property_id, property_value)
17
22
  else
18
- redis.call("EXPIRE", value_key, EXPIRATION_IN_SECONDS)
19
- redis.call("EXPIRE", property_abbreviation .. "I" .. property_id, EXPIRATION_IN_SECONDS)
23
+ redis.call("EXPIRE", value_key, EXPIRATION_IN_SECONDS + EXPIRATION_OFFSET_IN_SECONDS)
24
+ redis.call("EXPIRE", property_abbreviation .. "I" .. property_id, EXPIRATION_IN_SECONDS + EXPIRATION_OFFSET_IN_SECONDS)
20
25
  end
21
26
 
22
27
  return property_id
23
28
  end
24
29
 
25
30
  local function increment_leaderboard_for(property_abbreviation, property_id, timebucket)
31
+
26
32
  local key = property_abbreviation .. "L" .. timebucket
27
33
  redis.call("ZINCRBY", key, 1, property_id)
28
34
  redis.call("EXPIRE", key, EXPIRATION_IN_SECONDS)
29
35
  end
30
36
 
31
37
  local function set_property_to_requests_list(property_abbreviation, property_id, request_id, timebucket)
38
+
32
39
  local key = property_abbreviation .. "R" .. property_id .. "-" .. timebucket
33
40
  redis.call("LPUSH", key, request_id)
34
- redis.call("EXPIRE", key, EXPIRATION_IN_SECONDS)
41
+
42
+ redis.call("EXPIRE", key, EXPIRATION_IN_SECONDS + EXPIRATION_OFFSET_IN_SECONDS)
35
43
  end
36
44
 
45
+
37
46
  local function ip_in_hash(hash_name, ip_address)
38
47
  local found_ip = redis.call('HEXISTS', hash_name, ip_address)
39
48
 
@@ -45,7 +54,9 @@ local function ip_in_hash(hash_name, ip_address)
45
54
  end
46
55
 
47
56
  local function ip_in_cidr_range(cidr_set, ip_decimal_lexical)
57
+
48
58
  local higher_value = redis.call('ZRANGEBYLEX', cidr_set, '['..ip_decimal_lexical, '+', 'LIMIT', 0, 1)[1]
59
+
49
60
  local lower_value = redis.call('ZREVRANGEBYLEX', cidr_set, '['..ip_decimal_lexical, '-', 'LIMIT', 0, 1)[1]
50
61
 
51
62
  if not (higher_value and lower_value) then
@@ -64,6 +75,7 @@ end
64
75
 
65
76
  local function match_by_pattern(property_abbreviation, property_value)
66
77
  local hash_name = "rules-blocked-" .. property_abbreviation
78
+
67
79
  local patterns = redis.call('HKEYS', hash_name)
68
80
 
69
81
  for _, pattern in ipairs(patterns) do
@@ -76,10 +88,13 @@ local function match_by_pattern(property_abbreviation, property_value)
76
88
  end
77
89
 
78
90
  local function blocked_by_rate_limit(request_properties)
91
+
79
92
  local rate_limiting_rules_values = redis.call('HKEYS', 'rules-blocked-rate-limits')
80
93
 
81
94
  for i, rule_name in ipairs(rate_limiting_rules_values) do
95
+
82
96
  local conditions_hash = redis.call('HGETALL', rule_name .. "-conditions")
97
+
83
98
  local all_conditions_match = true
84
99
 
85
100
  for j = 1, #conditions_hash, 2 do
@@ -93,9 +108,13 @@ local function blocked_by_rate_limit(request_properties)
93
108
  end
94
109
 
95
110
  if all_conditions_match then
111
+
96
112
  local rule_settings_key = rule_name .. "-settings"
113
+
97
114
  local limit, time_period, limited_by, rule_id = unpack(redis.call('HMGET', rule_settings_key, 'limit', 'time-period', 'limited-by', 'rule-id'))
115
+
98
116
  local throttle_key = rule_name .. ":" .. limit .. "V" .. request_properties.ip
117
+
99
118
  local new_value = redis.call('INCR', throttle_key)
100
119
 
101
120
  if new_value == 1 then
@@ -113,6 +132,7 @@ end
113
132
 
114
133
  local function check_rules(functions_to_check)
115
134
  for _, check in ipairs(functions_to_check) do
135
+
116
136
  local rule = check.func(unpack(check.args))
117
137
  local category = check.category
118
138
 
@@ -170,13 +190,17 @@ local request = {
170
190
  ["method_id"] = set_property_value_id_lookups("m", ARGV[8])
171
191
  }
172
192
 
173
- local current_timebucket = get_timebucket(request.ts_in_seconds)
193
+
194
+
195
+ local current_timebucket = get_timebucket(request.ts_in_seconds)
196
+
174
197
  local blocked_rule = false
175
198
  local blocked_category = nil
176
199
  local treatment = "p"
200
+
177
201
  local stream_id
178
202
 
179
- if use_timestamps_as_request_ids == true then
203
+ if USE_TIMESTAMPS_AS_REQUEST_IDS == true then
180
204
  stream_id = request.ts_in_milliseconds
181
205
  else
182
206
  stream_id = "*"
@@ -209,6 +233,7 @@ local request = {
209
233
 
210
234
  table.insert(stream_args, "ar")
211
235
  table.insert(stream_args, allowed_rule)
236
+
212
237
  else
213
238
  blocked_rule, blocked_category = check_blocks(request)
214
239
  end
@@ -14,8 +14,10 @@ module Wafris
14
14
  )
15
15
  @redis_pool_size = 20
16
16
 
17
- # TODO: update HUB with the REDIS_URL on startup
18
- create_settings if ENV['REDIS_URL']
17
+ puts "[Wafris] attempting firewall connection via REDIS_URL."
18
+ create_settings
19
+ rescue Redis::CannotConnectError
20
+ puts "[Wafris] firewall disabled. Cannot connect to REDIS_URL. Will attempt Wafris.configure if it exists."
19
21
  end
20
22
 
21
23
  def connection_pool
@@ -23,24 +25,12 @@ module Wafris
23
25
  ConnectionPool.new(size: redis_pool_size) { redis }
24
26
  end
25
27
 
26
- def enabled?
27
- redis.ping
28
-
29
- return true
30
- rescue Redis::CannotConnectError
31
- raise <<~CONNECTION_ERROR
32
- Wafris cannot connect to Redis.
33
-
34
- The current Redis instance points to a connection that
35
- cannot be pinged.
36
- CONNECTION_ERROR
37
- end
38
-
39
28
  def create_settings
40
29
  redis.hset('waf-settings',
41
30
  'version', Wafris::VERSION,
42
31
  'client', 'ruby',
43
32
  'redis-host', 'heroku')
33
+ puts "[Wafris] firewall enabled. Connected to Redis. Ready to process requests. Set rules at: https://wafris.org/hub"
44
34
  end
45
35
 
46
36
  def core_sha
@@ -26,12 +26,15 @@ module Wafris
26
26
 
27
27
  request = Rack::Request.new(env)
28
28
 
29
- if Wafris.configuration.enabled? && Wafris.allow_request?(request)
29
+ if Wafris.allow_request?(request)
30
30
  @app.call(env)
31
31
  else
32
32
  puts 'blocked'
33
33
  [403, {}, ['Blocked']]
34
34
  end
35
+ rescue StandardError => e
36
+ puts "[Wafris] Redis connection error: #{e.message}. Request passed without rules check."
37
+ @app.call(env)
35
38
  end
36
39
  end
37
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wafris
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/wafris.rb CHANGED
@@ -13,7 +13,10 @@ module Wafris
13
13
  class << self
14
14
  def configure
15
15
  yield configuration
16
+ puts "[Wafris] attempting firewall connection via Wafris.configure initializer."
16
17
  configuration.create_settings
18
+ rescue Redis::CannotConnectError
19
+ puts "[Wafris] firewall disabled. Cannot connect via Wafris.configure. Please check your configuration settings."
17
20
  end
18
21
 
19
22
  def configuration
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wafris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micahel Buckbee
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-08-31 00:00:00.000000000 Z
12
+ date: 2023-09-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: connection_pool