wafris 1.0.0 → 1.1.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
  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