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 +4 -4
- data/lib/lua/dist/wafris_core.lua +31 -6
- data/lib/wafris/configuration.rb +5 -15
- data/lib/wafris/middleware.rb +4 -1
- data/lib/wafris/version.rb +1 -1
- data/lib/wafris.rb +3 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 919967ec8203e767f77202e17964dbcfd21f60422bb91bd61b806575886c5dbf
|
4
|
+
data.tar.gz: ffe9b975f6613e1742c90b6335e85f53769d439d32f014517487142a97edb37e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4efff1ae48ec5417bf9917c0a3b7f073064216ee718eed8246f0b993397454ab4296a2e27ac48a1289f31abe20ca277d32f4bc848978a7c4dd88d04ee08c4cdb
|
7
|
+
data.tar.gz: 274d14a646e0e706a940d16c53adb050045df1593056f012ba47666af402c37e38a055038fa2074a73e32e73cc49bcf5f5d943c16e7bff71bd3d79d47e8aaadc
|
@@ -1,5 +1,9 @@
|
|
1
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
data/lib/wafris/configuration.rb
CHANGED
@@ -14,8 +14,10 @@ module Wafris
|
|
14
14
|
)
|
15
15
|
@redis_pool_size = 20
|
16
16
|
|
17
|
-
|
18
|
-
create_settings
|
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
|
data/lib/wafris/middleware.rb
CHANGED
@@ -26,12 +26,15 @@ module Wafris
|
|
26
26
|
|
27
27
|
request = Rack::Request.new(env)
|
28
28
|
|
29
|
-
if Wafris.
|
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
|
data/lib/wafris/version.rb
CHANGED
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.
|
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-
|
12
|
+
date: 2023-09-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: connection_pool
|