wafris 0.0.1 → 0.1.2

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: dd571cbf8934f0b14384e1b85d42d5a70ffaaa98f17e8fcfd3545553b6b1d6af
4
- data.tar.gz: a5a053621c83c0ed2892a8aca08fc26439e6e0cff95bcdbf8787b35c0ad1eaf3
3
+ metadata.gz: 05707fe625f71f24a02f26585d89356156b1dc83887195f1042a47856d3c86cf
4
+ data.tar.gz: 05e798e3298518a0310f1cdf6a68426b2b12f9c066acc74e78833987e26f2e31
5
5
  SHA512:
6
- metadata.gz: 339e8b1fee46d79287716e20edfc24b80c720a82f12d07485508c0beb16c9813f4f35530b7473d0711d75a6e316e97236d7912024b984e96524649021d58be19
7
- data.tar.gz: c674ca5de599c5f0bb7f7bfb6beba19d4bec38411f66cddf27897c4ecafe9a3486e723b30e39a7cb63472106ca62483ee7ffc5b98fd31dd331d7eae82efc13df
6
+ metadata.gz: b41d4ee2190f5a88e263ef6905ffe43e6727923df4f3f1a600f203d10ea2010409e95c02c965cc2161fce47d5d541aaa1217beeb05097134fcaecdcfcd4305f1
7
+ data.tar.gz: dd940d846b7892a4607c78767f2ca364c62ab33782deb76b863d8b393f1a5c82134f0b3f6c76a438f9993e7d7702df67899d9e6485bc5491e03fcc3e02a38c1e
@@ -0,0 +1,81 @@
1
+ -- Compiled from:
2
+ -- src/get_time_buckets.lua
3
+
4
+ -- Code was pulled from https://otland.net/threads/how-convert-timestamp-to-date-type.251657/
5
+ -- An alternate solution is https://gist.github.com/markuman/e96d04139cd8acc33604
6
+ local function get_time_bucket_from_timestamp(unix_time_milliseconds)
7
+ local function calculate_years_number_of_days(yr)
8
+ return (yr % 4 == 0 and (yr % 100 ~= 0 or yr % 400 == 0)) and 366 or 365
9
+ end
10
+
11
+ local function get_year_and_day_number(year, days)
12
+ while days >= calculate_years_number_of_days(year) do
13
+ days = days - calculate_years_number_of_days(year)
14
+ year = year + 1
15
+ end
16
+ return year, days
17
+ end
18
+
19
+ local function get_month_and_month_day(days, year)
20
+ local days_in_each_month = {
21
+ 31,
22
+ (calculate_years_number_of_days(year) == 366 and 29 or 28),
23
+ 31,
24
+ 30,
25
+ 31,
26
+ 30,
27
+ 31,
28
+ 31,
29
+ 30,
30
+ 31,
31
+ 30,
32
+ 31,
33
+ }
34
+
35
+ for month = 1, #days_in_each_month do
36
+ if days - days_in_each_month[month] <= 0 then
37
+ return month, days
38
+ end
39
+ days = days - days_in_each_month[month]
40
+ end
41
+ end
42
+
43
+ local unix_time = unix_time_milliseconds / 1000
44
+ local year = 1970
45
+ local days = math.ceil(unix_time / 86400)
46
+ local month = nil
47
+
48
+ year, days = get_year_and_day_number(year, days)
49
+ month, days = get_month_and_month_day(days, year)
50
+ local hours = math.floor(unix_time / 3600 % 24)
51
+ -- local minutes, seconds = math.floor(unix_time / 60 % 60), math.floor(unix_time % 60)
52
+ -- hours = hours > 12 and hours - 12 or hours == 0 and 12 or hours
53
+ return string.format("%04d-%02d-%02d-%02d", year, month, days, hours)
54
+ end
55
+
56
+ local function get_time_buckets(unix_time_milliseconds)
57
+ local time_buckets = {}
58
+
59
+ for i = 23, 0, -1 do
60
+ table.insert(time_buckets, get_time_bucket_from_timestamp(unix_time_milliseconds - (1000 * 60 * 60 * i)))
61
+ end
62
+ return time_buckets
63
+ end
64
+
65
+ local function num_requests(time_bucket)
66
+ local request_keys = redis.call("KEYS", "unique-requests:" .. time_bucket)
67
+ redis.call("PFMERGE", "merged_unique-requests", unpack(request_keys))
68
+ return redis.call("PFCOUNT", "merged_unique-requests")
69
+ end
70
+
71
+ local graph_data = {}
72
+ local unix_time_milliseconds = ARGV[1]
73
+ local time_buckets = get_time_buckets(unix_time_milliseconds)
74
+ -- use the get_time_buckets method to get each time bucket and
75
+ -- the associated count for that bucket
76
+ for bucket in pairs(time_buckets) do
77
+ table.insert(graph_data, time_buckets[bucket])
78
+ table.insert(graph_data, num_requests(time_buckets[bucket]))
79
+ end
80
+
81
+ return graph_data
@@ -0,0 +1,96 @@
1
+ local function get_time_bucket_from_timestamp(unix_time_milliseconds)
2
+ local function calculate_years_number_of_days(yr)
3
+ return (yr % 4 == 0 and (yr % 100 ~= 0 or yr % 400 == 0)) and 366 or 365
4
+ end
5
+
6
+ local function get_year_and_day_number(year, days)
7
+ while days >= calculate_years_number_of_days(year) do
8
+ days = days - calculate_years_number_of_days(year)
9
+ year = year + 1
10
+ end
11
+ return year, days
12
+ end
13
+
14
+ local function get_month_and_month_day(days, year)
15
+ local days_in_each_month = {
16
+ 31,
17
+ (calculate_years_number_of_days(year) == 366 and 29 or 28),
18
+ 31,
19
+ 30,
20
+ 31,
21
+ 30,
22
+ 31,
23
+ 31,
24
+ 30,
25
+ 31,
26
+ 30,
27
+ 31,
28
+ }
29
+
30
+ for month = 1, #days_in_each_month do
31
+ if days - days_in_each_month[month] <= 0 then
32
+ return month, days
33
+ end
34
+ days = days - days_in_each_month[month]
35
+ end
36
+ end
37
+
38
+ local unix_time = unix_time_milliseconds / 1000
39
+ local year = 1970
40
+ local days = math.ceil(unix_time / 86400)
41
+ local month = nil
42
+
43
+ year, days = get_year_and_day_number(year, days)
44
+ month, days = get_month_and_month_day(days, year)
45
+ local hours = math.floor(unix_time / 3600 % 24)
46
+ -- local minutes, seconds = math.floor(unix_time / 60 % 60), math.floor(unix_time % 60)
47
+ -- hours = hours > 12 and hours - 12 or hours == 0 and 12 or hours
48
+ return string.format("%04d-%02d-%02d-%02d", year, month, days, hours)
49
+ end
50
+
51
+ -- For: Relationship of IP to time of Request (Stream)
52
+ local function get_request_id(timestamp, ip, max_requests)
53
+ timestamp = timestamp or "*"
54
+ local request_id = redis.call("XADD", "ip-requests-stream", "MAXLEN", "~", max_requests, timestamp, "ip", ip)
55
+ return request_id
56
+ end
57
+
58
+ local function add_to_HLL_request_count(timebucket, request_id)
59
+ redis.call("PFADD", "unique-requests:" .. timebucket, request_id)
60
+ end
61
+
62
+ -- For: Leaderboard of IPs with Request count as score
63
+ local function increment_timebucket_for_ip(timebucket, ip)
64
+ redis.call("ZINCRBY", "ip-leader-sset:" .. timebucket, 1, ip)
65
+ end
66
+
67
+ -- Configuration
68
+ local max_requests = 100000
69
+ local max_requests_per_ip = 10000
70
+
71
+ local ip = ARGV[1]
72
+ local ip_to_decimal = ARGV[2]
73
+ local unix_time_milliseconds = ARGV[3]
74
+ local unix_time = ARGV[3] / 1000
75
+
76
+ -- Initialize local variables
77
+ local request_id = get_request_id(nil, ip, max_requests)
78
+ local current_timebucket = get_time_bucket_from_timestamp(unix_time_milliseconds)
79
+
80
+ -- GRAPH DATA COLLECTION
81
+ add_to_HLL_request_count(current_timebucket, request_id)
82
+
83
+ -- LEADERBOARD DATA COLLECTION
84
+ increment_timebucket_for_ip(current_timebucket, ip)
85
+
86
+ -- BLOCKING LOGIC
87
+ -- Safelist Range Check
88
+ if next(redis.call("ZRANGEBYSCORE", "allowed_ranges", ip_to_decimal, "+inf", "LIMIT", 0, 1)) then
89
+ return "Allowed"
90
+ -- Blocklist Range Check
91
+ elseif next(redis.call("ZRANGEBYSCORE", "blocked_ranges", ip_to_decimal, "+inf", "LIMIT", 0, 1)) then
92
+ return "Blocked"
93
+ -- No Matches
94
+ else
95
+ return "Not found"
96
+ end
@@ -0,0 +1,58 @@
1
+ function get_time_bucket_from_timestamp(unix_time_milliseconds)
2
+ local function calculate_years_number_of_days(yr)
3
+ return (yr % 4 == 0 and (yr % 100 ~= 0 or yr % 400 == 0)) and 366 or 365
4
+ end
5
+
6
+ local function get_year_and_day_number(year, days)
7
+ while days >= calculate_years_number_of_days(year) do
8
+ days = days - calculate_years_number_of_days(year)
9
+ year = year + 1
10
+ end
11
+ return year, days
12
+ end
13
+
14
+ local function get_month_and_month_day(days, year)
15
+ local days_in_each_month = {
16
+ 31,
17
+ (calculate_years_number_of_days(year) == 366 and 29 or 28),
18
+ 31,
19
+ 30,
20
+ 31,
21
+ 30,
22
+ 31,
23
+ 31,
24
+ 30,
25
+ 31,
26
+ 30,
27
+ 31,
28
+ }
29
+
30
+ for month = 1, #days_in_each_month do
31
+ if days - days_in_each_month[month] <= 0 then
32
+ return month, days
33
+ end
34
+ days = days - days_in_each_month[month]
35
+ end
36
+ end
37
+
38
+ local unix_time = unix_time_milliseconds / 1000
39
+ local year = 1970
40
+ local days = math.ceil(unix_time / 86400)
41
+ local month = nil
42
+
43
+ year, days = get_year_and_day_number(year, days)
44
+ month, days = get_month_and_month_day(days, year)
45
+ local hours = math.floor(unix_time / 3600 % 24)
46
+ -- local minutes, seconds = math.floor(unix_time / 60 % 60), math.floor(unix_time % 60)
47
+ -- hours = hours > 12 and hours - 12 or hours == 0 and 12 or hours
48
+ return string.format("%04d-%02d-%02d-%02d", year, month, days, hours)
49
+ end
50
+
51
+ function get_time_buckets(unix_time_milliseconds)
52
+ local time_buckets = {}
53
+
54
+ for i = 23, 0, -1 do
55
+ table.insert(time_buckets, get_time_bucket_from_timestamp(unix_time_milliseconds - (1000 * 60 * 60 * i)))
56
+ end
57
+ return time_buckets
58
+ end
@@ -0,0 +1,14 @@
1
+ local function num_requests(start_time, end_time)
2
+ local request_keys = redis.call('KEYS', 'unique-requests:*')
3
+ redis.call('PFMERGE', 'merged_unique-requests', unpack(request_keys))
4
+ return redis.call('PFCOUNT', 'merged_unique-requests')
5
+ end
6
+
7
+ local function unique_ips(start_time, end_time)
8
+ local ip_keys = redis.call('KEYS', 'unique-ips:*')
9
+ redis.call('PFMERGE', 'merged_unique-ips', unpack(ip_keys))
10
+ return redis.call('PFCOUNT', 'merged_unique-ips')
11
+ end
12
+
13
+ redis.debug("Request count: ", num_requests(0, 10000000))
14
+ redis.debug("IP request count: ", unique_ips(0, 10000000))
@@ -0,0 +1,104 @@
1
+ -- Template strings below are replaced with generated
2
+ -- data from the ip_data_generator.rb script
3
+ -- local ipArray = { }
4
+ -- local timestampArray = { }
5
+ -- redis.debug("Timestamp count: ", #timestampArray)
6
+
7
+ local function get_time_bucket_from_timestamp(unix_time_milliseconds)
8
+ local function calculate_years_number_of_days(yr)
9
+ return (yr % 4 == 0 and (yr % 100 ~= 0 or yr % 400 == 0)) and 366 or 365
10
+ end
11
+
12
+ local function get_year_and_day_number(year, days)
13
+ while days >= calculate_years_number_of_days(year) do
14
+ days = days - calculate_years_number_of_days(year)
15
+ year = year + 1
16
+ end
17
+ return year, days
18
+ end
19
+
20
+ local function get_month_and_month_day(days, year)
21
+ local days_in_each_month = {
22
+ 31,
23
+ (calculate_years_number_of_days(year) == 366 and 29 or 28),
24
+ 31,
25
+ 30,
26
+ 31,
27
+ 30,
28
+ 31,
29
+ 31,
30
+ 30,
31
+ 31,
32
+ 30,
33
+ 31,
34
+ }
35
+
36
+ for month = 1, #days_in_each_month do
37
+ if days - days_in_each_month[month] <= 0 then
38
+ return month, days
39
+ end
40
+ days = days - days_in_each_month[month]
41
+ end
42
+ end
43
+
44
+ local unix_time = unix_time_milliseconds / 1000
45
+ local year = 1970
46
+ local days = math.ceil(unix_time / 86400)
47
+ local month = nil
48
+
49
+ year, days = get_year_and_day_number(year, days)
50
+ month, days = get_month_and_month_day(days, year)
51
+ local hours = math.floor(unix_time / 3600 % 24)
52
+ -- local minutes, seconds = math.floor(unix_time / 60 % 60), math.floor(unix_time % 60)
53
+ -- hours = hours > 12 and hours - 12 or hours == 0 and 12 or hours
54
+ return string.format("%04d-%02d-%02d-%02d", year, month, days, hours)
55
+ end
56
+
57
+ -- For: Relationship of IP to time of Request (Stream)
58
+ local function get_request_id(timestamp, ip, max_requests)
59
+ timestamp = timestamp or "*"
60
+ local request_id = redis.call("XADD", "ip-requests-stream", "MAXLEN", "~", max_requests, timestamp, "ip", ip)
61
+ return request_id
62
+ end
63
+
64
+ local function add_to_HLL_request_count(timebucket, request_id)
65
+ redis.call("PFADD", "unique-requests:" .. timebucket, request_id)
66
+ end
67
+
68
+ -- Configuration
69
+ local max_requests = 100000
70
+ local max_requests_per_ip = 10000
71
+
72
+ -- Interior of this for loop is what should go into wafris_core.lua
73
+ for i = 1, #timestampArray do
74
+ -- Setup
75
+ local ip = ipArray[math.random(#ipArray)]
76
+ local timestamp = timestampArray[i]
77
+
78
+ local request_id = get_request_id(timestamp, ip, max_requests)
79
+
80
+ -- GRAPH DATA COLLECTION
81
+ local current_timebucket = get_time_bucket_from_timestamp(timestamp)
82
+ add_to_HLL_request_count(current_timebucket, request_id)
83
+
84
+ -- For: Looking up Requests an IP has made (Stream) / time of request
85
+ local ip_stream_key = "ip-stream:" .. ip
86
+ local ip_stream_id =
87
+ redis.call("XADD", ip_stream_key, "MAXLEN", "~", max_requests_per_ip, "*", "request_id", request_id)
88
+
89
+ -- For: Precalc of Number of Requests (Key)
90
+ local requests_count_key = "requests-count:" .. current_timebucket
91
+ redis.call("INCR", requests_count_key)
92
+
93
+ -- For: Precalc of Number of Requests from an IP (Key)
94
+ local ips_count_bucket_key = "ips-count:" .. ip .. ":" .. current_timebucket
95
+ redis.call("INCR", ips_count_bucket_key)
96
+
97
+ -- For: Precalc of Number of Unique IPs making Requests (HLL)
98
+ local ips_count_hll_key = "unique-ips:" .. current_timebucket
99
+ redis.call("PFADD", ips_count_hll_key, ip)
100
+
101
+ -- For: Leaderboard of IPs with Request count as score
102
+ local ip_leaderboard_sset_key = "ip-leader-sset:" .. current_timebucket
103
+ redis.call("ZINCRBY", ip_leaderboard_sset_key, 1, ip)
104
+ end
@@ -0,0 +1,40 @@
1
+ -- Code was pulled from https://otland.net/threads/how-convert-timestamp-to-date-type.251657/
2
+ -- An alternate solution is https://gist.github.com/markuman/e96d04139cd8acc33604
3
+ local function get_time_bucket_from_timestamp(unix_time_milliseconds)
4
+ local function calculate_years_number_of_days(yr)
5
+ return (yr % 4 == 0 and (yr % 100 ~= 0 or yr % 400 == 0)) and 366 or 365
6
+ end
7
+
8
+ local function get_year_and_day_number(year, days)
9
+ while days >= calculate_years_number_of_days(year) do
10
+ days = days - calculate_years_number_of_days(year)
11
+ year = year + 1
12
+ end
13
+ return year, days
14
+ end
15
+
16
+ local function get_month_and_month_day(days, year)
17
+ local days_in_each_month = {
18
+ 31,
19
+ (calculate_years_number_of_days(year) == 366 and 29 or 28),
20
+ 31, 30, 31,30,31,31,30,31,30,31
21
+ }
22
+
23
+ for month = 1, #days_in_each_month do
24
+ if days - days_in_each_month[month] <= 0 then return month, days end
25
+ days = days - days_in_each_month[month]
26
+ end
27
+ end
28
+
29
+ local unix_time = unix_time_milliseconds / 1000
30
+ local year = 1970
31
+ local days = math.ceil(unix_time/86400)
32
+ local month = nil
33
+
34
+ year, days = get_year_and_day_number(year, days)
35
+ month, days = get_month_and_month_day(days, year)
36
+ local hours = math.floor(unix_time / 3600 % 24)
37
+ -- local minutes, seconds = math.floor(unix_time / 60 % 60), math.floor(unix_time % 60)
38
+ -- hours = hours > 12 and hours - 12 or hours == 0 and 12 or hours
39
+ return string.format("%04d-%02d-%02d-%02d", year, month, days, hours)
40
+ end
@@ -28,15 +28,29 @@ module Wafris
28
28
  CONNECTION_ERROR
29
29
  end
30
30
 
31
- def script_sha
32
- @script_sha ||= redis.script(:load, wafris_core)
31
+ def core_sha
32
+ @core_sha ||= redis.script(:load, wafris_core)
33
33
  end
34
34
 
35
35
  def wafris_core
36
+ read_lua_dist("wafris_core")
37
+ end
38
+
39
+ def graph_sha
40
+ @graph_sha ||= redis.script(:load, wafris_graph)
41
+ end
42
+
43
+ def wafris_graph
44
+ read_lua_dist("get_graph_data")
45
+ end
46
+
47
+ private
48
+
49
+ def read_lua_dist(filename)
36
50
  File.read(
37
51
  File.join(
38
52
  File.dirname(__FILE__),
39
- 'wafris_core.lua'
53
+ "../lua/dist/#{filename}.lua"
40
54
  )
41
55
  )
42
56
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wafris
4
- VERSION = "0.0.1"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/wafris.rb CHANGED
@@ -31,14 +31,13 @@ module Wafris
31
31
 
32
32
  def allow_request?(request)
33
33
  configuration.connection_pool.with do |conn|
34
- time = Time.now
34
+ time = Time.now.to_f * 1000
35
35
  status = conn.evalsha(
36
- configuration.script_sha,
36
+ configuration.core_sha,
37
37
  argv: [
38
38
  request.ip,
39
39
  IPAddr.new(request.ip).to_i,
40
- time.to_i,
41
- "all-ips:#{time.strftime('%Y-%m-%d')}:#{time.hour}"
40
+ time.to_i
42
41
  ]
43
42
  )
44
43
 
@@ -49,5 +48,61 @@ module Wafris
49
48
  end
50
49
  end
51
50
  end
51
+
52
+ def add_block(ip)
53
+ configuration.connection_pool.with do |conn|
54
+ conn.zadd(
55
+ 'blocked_ranges',
56
+ IPAddr.new(ip).to_i,
57
+ ip
58
+ )
59
+ end
60
+ end
61
+
62
+ def remove_block(ip)
63
+ configuration.connection_pool.with do |conn|
64
+ conn.zrem(
65
+ 'blocked_ranges',
66
+ ip
67
+ )
68
+ end
69
+ end
70
+
71
+ def request_buckets(_now)
72
+ graph_data = []
73
+ configuration.connection_pool.with do |conn|
74
+ time = Time.now.to_f * 1000
75
+ graph_data = conn.evalsha(
76
+ configuration.graph_sha,
77
+ argv: [
78
+ time.to_i
79
+ ]
80
+ )
81
+ end
82
+
83
+ return graph_data
84
+ end
85
+
86
+ def ips_with_num_requests
87
+ configuration.connection_pool.with do |conn|
88
+ return conn.zunion(
89
+ *leader_timebuckets,
90
+ 0, -1, with_scores: true
91
+ )
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def leader_timebuckets
98
+ timebuckets = []
99
+
100
+ time = Time.now.utc
101
+ 24.times do |hours|
102
+ timebuckets << "ip-leader-sset:#{(time - 60 * 60 * hours).strftime("%Y-%m-%d-%H")}"
103
+ end
104
+
105
+ return timebuckets
106
+ end
52
107
  end
53
108
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wafris
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micahel Buckbee
8
8
  - Ryan Castillo
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-02-06 00:00:00.000000000 Z
12
+ date: 2023-04-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: connection_pool
@@ -179,23 +179,28 @@ dependencies:
179
179
  - - ">="
180
180
  - !ruby/object:Gem::Version
181
181
  version: 13.0.6
182
- description:
183
- email:
182
+ description:
183
+ email:
184
184
  executables: []
185
185
  extensions: []
186
186
  extra_rdoc_files: []
187
187
  files:
188
+ - lib/lua/dist/get_graph_data.lua
189
+ - lib/lua/dist/wafris_core.lua
190
+ - lib/lua/src/get_time_buckets.lua
191
+ - lib/lua/src/queries.lua
192
+ - lib/lua/src/seeds/data_load.lua
193
+ - lib/lua/src/time_bucket.lua
188
194
  - lib/wafris.rb
189
195
  - lib/wafris/configuration.rb
190
196
  - lib/wafris/middleware.rb
191
197
  - lib/wafris/railtie.rb
192
198
  - lib/wafris/version.rb
193
- - lib/wafris/wafris_core.lua
194
- homepage:
199
+ homepage:
195
200
  licenses:
196
- - MIT
201
+ - Elastic-2.0
197
202
  metadata: {}
198
- post_install_message:
203
+ post_install_message:
199
204
  rdoc_options: []
200
205
  require_paths:
201
206
  - lib
@@ -211,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
216
  version: '0'
212
217
  requirements: []
213
218
  rubygems_version: 3.3.26
214
- signing_key:
219
+ signing_key:
215
220
  specification_version: 4
216
221
  summary: Web application firewall for Rack apps
217
222
  test_files: []
@@ -1,42 +0,0 @@
1
- local LAST_REQUESTS_TIME = 'last_requests_time'
2
- local TWENTY_FOUR_HOURS = 86400
3
-
4
- local ip = ARGV[1]
5
- local ip_to_decimal = ARGV[2]
6
- local unix_time = ARGV[3]
7
- local expire_time = unix_time - TWENTY_FOUR_HOURS
8
- local ip_request_string = "ip-requests-" .. ip
9
- local hour_bucket = ARGV[4]
10
-
11
- -- LEADERBOARD DATA COLLECTION
12
- -- Add IP to last_requests_time key by integer timestamp
13
- -- ZADD last_requets_time 1661356145 '192.168.1.1'
14
- redis.call('ZADD', LAST_REQUESTS_TIME, unix_time, ip)
15
- -- Remove IP from last_requests_time if it has been there for 24 hours
16
- -- ZREMRANGEBYSCORE last_requests_time 0 (1661356145 - 86400)
17
- redis.call('ZREMRANGEBYSCORE', LAST_REQUESTS_TIME, 0, expire_time)
18
- -- Add IP to ip-requests-<ip> for leaderboard tracking
19
- -- LPUSH ip-requests-192.168.1.1 1661356145
20
- redis.call('LPUSH', ip_request_string, unix_time)
21
- -- Have the key expire in 24 hours
22
- -- EXPIRE ip-requests-192.168.1.1 86400
23
- redis.call('EXPIRE', ip_request_string, TWENTY_FOUR_HOURS)
24
-
25
- -- GRAPH DATA COLLECTION
26
- -- Increment counter for hourly buckets
27
- -- INC all-ips:2022-10-01:12
28
- redis.call('INCR', hour_bucket)
29
- -- EXPIRE all-ips:2022-10-01:12 86400
30
- redis.call('EXPIRE', hour_bucket, TWENTY_FOUR_HOURS)
31
-
32
- -- BLOCKING LOGIC
33
- -- Safelist Range Check
34
- if next(redis.call('ZRANGEBYSCORE', 'allowed_ranges', ip_to_decimal, "+inf", "LIMIT", 0, 1)) then
35
- return 'Allowed'
36
- -- Blocklist Range Check
37
- elseif next(redis.call('ZRANGEBYSCORE', 'blocked_ranges', ip_to_decimal, "+inf", "LIMIT", 0, 1)) then
38
- return 'Blocked'
39
- -- No Matches
40
- else
41
- return 'Not found'
42
- end