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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0dfe47e533d91f974d391af755f1bd8e148808cbfe86dd6755b839201282b47
|
4
|
+
data.tar.gz: bd7135922d96fa7789cc65dbd47635c4b1e84a4e7b27a6b4dde5a1a00cc15ac8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c1e66690ca97e6f9a9349f1960f92f5376175660bebc9a172eabe849af6806d4c5f278f1765a22b36ce75797a757bf9cb3719a58446aaa5fa8fe0356c348cf0
|
7
|
+
data.tar.gz: 35a3e86f1f3c63ec38c3d67adbfb952fa89a062dad78450bfd365f45c1a1607f689e104e2c7ef6faac3a00461e07022d041e5d2f667396ad81baa3af30be8acf
|
data/lib/wafris/configuration.rb
CHANGED
@@ -1,56 +1,140 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
|
3
2
|
require_relative 'version'
|
4
3
|
|
5
4
|
module Wafris
|
6
5
|
class Configuration
|
7
|
-
|
8
|
-
attr_accessor :
|
9
|
-
attr_accessor :
|
10
|
-
attr_accessor :
|
6
|
+
|
7
|
+
attr_accessor :api_key
|
8
|
+
attr_accessor :db_file_path
|
9
|
+
attr_accessor :db_file_name
|
10
|
+
attr_accessor :downsync_custom_rules_interval
|
11
|
+
attr_accessor :downsync_data_subscriptions_interval
|
12
|
+
attr_accessor :downsync_url
|
13
|
+
attr_accessor :upsync_url
|
14
|
+
attr_accessor :upsync_interval
|
15
|
+
attr_accessor :upsync_queue_limit
|
16
|
+
attr_accessor :upsync_status
|
17
|
+
attr_accessor :upsync_queue
|
18
|
+
attr_accessor :local_only
|
19
|
+
attr_accessor :last_upsync_timestamp
|
20
|
+
attr_accessor :max_body_size_mb
|
21
|
+
attr_accessor :rate_limiters
|
11
22
|
|
12
23
|
def initialize
|
13
|
-
@redis_pool_size = 20
|
14
|
-
@maxmemory = 25
|
15
|
-
@quiet_mode = false
|
16
|
-
end
|
17
24
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
25
|
+
# API Key - Required
|
26
|
+
if ENV['WAFRIS_API_KEY']
|
27
|
+
@api_key = ENV['WAFRIS_API_KEY']
|
28
|
+
else
|
29
|
+
unless @api_key
|
30
|
+
LogSuppressor.puts_log("Firewall disabled as neither local only or API key set")
|
31
|
+
end
|
32
|
+
end
|
22
33
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
) unless @quiet_mode
|
31
|
-
end
|
34
|
+
# DB FILE PATH LOCATION - Optional
|
35
|
+
if ENV['WAFRIS_DB_FILE_PATH']
|
36
|
+
@db_file_path = ENV['WAFRIS_DB_FILE_PATH']
|
37
|
+
else
|
38
|
+
#@db_file_path = Rails.root.join('tmp', 'wafris').to_s
|
39
|
+
@db_file_path = './tmp/wafris'
|
40
|
+
end
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
|
42
|
+
# Ensure that the db_file_path exists
|
43
|
+
unless File.directory?(@db_file_path)
|
44
|
+
LogSuppressor.puts_log("DB File Path does not exist - creating it now.")
|
45
|
+
FileUtils.mkdir_p(@db_file_path) unless File.exist?(@db_file_path)
|
46
|
+
end
|
47
|
+
|
48
|
+
# DB FILE NAME - For local
|
49
|
+
if ENV['WAFRIS_DB_FILE_NAME']
|
50
|
+
@db_file_name = ENV['WAFRIS_DB_FILE_NAME']
|
51
|
+
else
|
52
|
+
@db_file_name = 'wafris.db'
|
53
|
+
end
|
54
|
+
|
55
|
+
# DOWNSYNC
|
56
|
+
# Custom Rules are checked often (default 1 minute) - Optional
|
57
|
+
if ENV['WAFRIS_DOWNSYNC_CUSTOM_RULES_INTERVAL']
|
58
|
+
@downsync_custom_rules_interval = ENV['WAFRIS_DOWNSYNC_CUSTOM_RULES_INTERVAL'].to_i
|
59
|
+
else
|
60
|
+
@downsync_custom_rules_interval = 60
|
61
|
+
end
|
62
|
+
|
63
|
+
# Data Subscriptions are checked rarely (default 1 day) - Optional
|
64
|
+
if ENV['WAFRIS_DOWNSYNC_DATA_SUBSCRIPTIONS_INTERVAL']
|
65
|
+
@downsync_data_subscriptions_interval = ENV['WAFRIS_DOWNSYNC_DATA_SUBSCRIPTIONS_INTERVAL'].to_i
|
66
|
+
else
|
67
|
+
@downsync_data_subscriptions_interval = 60
|
68
|
+
end
|
69
|
+
|
70
|
+
# Set Downsync URL - Optional
|
71
|
+
# Used for both DataSubscription and CustomRules
|
72
|
+
if ENV['WAFRIS_DOWNSYNC_URL']
|
73
|
+
@downsync_url = ENV['WAFRIS_DOWNSYNC_URL']
|
74
|
+
else
|
75
|
+
@downsync_url = 'https://distributor.wafris.org/v2/downsync'
|
76
|
+
end
|
77
|
+
|
78
|
+
# UPSYNC - Optional
|
79
|
+
# Set Upsync URL
|
80
|
+
if ENV['WAFRIS_UPSYNC_URL']
|
81
|
+
@upsync_url = ENV['WAFRIS_UPSYNC_URL']
|
82
|
+
else
|
83
|
+
@upsync_url = 'https://collector.wafris.org/v2/upsync'
|
84
|
+
end
|
85
|
+
|
86
|
+
# Set Upsync Interval - Optional
|
87
|
+
if ENV['WAFRIS_UPSYNC_INTERVAL']
|
88
|
+
@upsync_interval = ENV['WAFRIS_UPSYNC_INTERVAL'].to_i
|
89
|
+
else
|
90
|
+
@upsync_interval = 10
|
91
|
+
end
|
92
|
+
|
93
|
+
# Set Upsync Queued Request Limit - Optional
|
94
|
+
if ENV['WAFRIS_UPSYNC_QUEUE_LIMIT']
|
95
|
+
@upsync_queue_limit = ENV['WAFRIS_UPSYNC_QUEUE_LIMIT'].to_i
|
96
|
+
else
|
97
|
+
@upsync_queue_limit = 250
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set Maximium Body Size for Requests - Optional (in Megabytes)
|
101
|
+
if ENV['WAFRIS_MAX_BODY_SIZE_MB'] && ENV['WAFRIS_MAX_BODY_SIZE_MB'].to_i > 0
|
102
|
+
@max_body_size_mb = ENV['WAFRIS_MAX_BODY_SIZE_MB'].to_i
|
103
|
+
else
|
104
|
+
@max_body_size_mb = 10
|
105
|
+
end
|
36
106
|
|
37
|
-
|
38
|
-
|
107
|
+
# Upsync Queue Defaults
|
108
|
+
@upsync_queue = []
|
109
|
+
@last_upsync_timestamp = Time.now.to_i
|
110
|
+
|
111
|
+
# Memory structure for rate limiting
|
112
|
+
@rate_limiters = {}
|
113
|
+
|
114
|
+
# Disable Upsync if Downsync API Key is invalid
|
115
|
+
# This prevents the client from sending upsync requests
|
116
|
+
# if the API key is known bad
|
117
|
+
@upsync_status = 'Disabled'
|
118
|
+
|
119
|
+
return true
|
120
|
+
|
39
121
|
end
|
40
122
|
|
41
|
-
|
123
|
+
def current_config
|
124
|
+
|
125
|
+
output = {}
|
126
|
+
|
127
|
+
instance_variables.each do |var|
|
128
|
+
output[var.to_s] = instance_variable_get(var)
|
129
|
+
end
|
130
|
+
|
131
|
+
return output
|
42
132
|
|
43
|
-
def read_lua_dist(filename)
|
44
|
-
File.read(
|
45
|
-
file_path(filename)
|
46
|
-
)
|
47
133
|
end
|
48
134
|
|
49
|
-
def
|
50
|
-
|
51
|
-
File.dirname(__FILE__),
|
52
|
-
"../lua/dist/#{filename}.lua"
|
53
|
-
)
|
135
|
+
def create_settings
|
136
|
+
@version = Wafris::VERSION
|
54
137
|
end
|
138
|
+
|
55
139
|
end
|
56
140
|
end
|
@@ -3,11 +3,12 @@
|
|
3
3
|
module Wafris
|
4
4
|
class LogSuppressor
|
5
5
|
def self.puts_log(message)
|
6
|
-
puts(message) unless suppress_logs?
|
6
|
+
puts("[Wafris] " + message) unless suppress_logs?
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.suppress_logs?
|
10
|
-
suppressed_environments.include?(current_environment)
|
10
|
+
suppressed_environments.include?(current_environment) ||
|
11
|
+
(ENV['WAFRIS_LOG_LEVEL'] && ENV['WAFRIS_LOG_LEVEL'] == 'silent')
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.suppressed_environments
|
data/lib/wafris/middleware.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
|
3
4
|
module Wafris
|
4
5
|
class Middleware
|
5
6
|
def initialize(app)
|
6
7
|
@app = app
|
7
8
|
end
|
8
9
|
|
10
|
+
|
9
11
|
def call(env)
|
12
|
+
|
10
13
|
user_defined_proxies = ENV['TRUSTED_PROXY_RANGES'].split(',') if ENV['TRUSTED_PROXY_RANGES']
|
11
14
|
|
12
15
|
valid_ipv4_octet = /\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/
|
@@ -25,34 +28,53 @@ module Wafris
|
|
25
28
|
Rack::Request.ip_filter = lambda { |ip| trusted_proxies.match?(ip) }
|
26
29
|
|
27
30
|
request = Rack::Request.new(env)
|
31
|
+
# Forcing UTF-8 encoding on all strings for Sqlite3 compatibility
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
# List of possible IP headers in order of priority
|
34
|
+
ip_headers = [
|
35
|
+
'HTTP_X_REAL_IP',
|
36
|
+
'HTTP_X_TRUE_CLIENT_IP',
|
37
|
+
'HTTP_FLY_CLIENT_IP',
|
38
|
+
'HTTP_CF_CONNECTING_IP'
|
39
|
+
]
|
40
|
+
|
41
|
+
# Find the first header that is present in the environment
|
42
|
+
ip_header = ip_headers.find { |header| env[header] }
|
43
|
+
|
44
|
+
# Use the found header or fallback to remote_ip if none of the headers are present
|
45
|
+
ip = (ip_header ? env[ip_header] : request.ip).force_encoding('UTF-8')
|
46
|
+
|
47
|
+
user_agent = request.user_agent ? request.user_agent.force_encoding('UTF-8') : nil
|
48
|
+
path = request.path.force_encoding('UTF-8')
|
49
|
+
parameters = Rack::Utils.build_query(request.params).force_encoding('UTF-8')
|
50
|
+
host = request.host.to_s.force_encoding('UTF-8')
|
51
|
+
request_method = String.new(request.request_method).force_encoding('UTF-8')
|
52
|
+
|
53
|
+
# Submitted for evaluation
|
54
|
+
headers = env.each_with_object({}) { |(k, v), h| h[k] = v.force_encoding('UTF-8') if k.start_with?('HTTP_') }
|
55
|
+
body = request.body.read
|
56
|
+
|
57
|
+
request_id = env.fetch('action_dispatch.request_id', SecureRandom.uuid.to_s)
|
58
|
+
request_timestamp = Time.now.utc.to_i
|
59
|
+
|
60
|
+
treatment = Wafris.evaluate(ip, user_agent, path, parameters, host, request_method, headers, body, request_id, request_timestamp)
|
61
|
+
|
62
|
+
# These values match what the client tests expect (200, 404, 403, 500
|
63
|
+
if treatment == 'Allowed' || treatment == 'Passed'
|
64
|
+
@app.call(env)
|
65
|
+
elsif treatment == 'Blocked'
|
66
|
+
[403, { 'content-type' => 'text/plain' }, ['Blocked']]
|
47
67
|
else
|
48
|
-
|
68
|
+
#ap request
|
69
|
+
[500, { 'content-type' => 'text/plain' }, ['Error']]
|
49
70
|
end
|
50
|
-
|
71
|
+
|
51
72
|
rescue StandardError => e
|
52
|
-
|
53
|
-
|
54
|
-
)
|
73
|
+
|
74
|
+
LogSuppressor.puts_log "[Wafris] Detailed Error: #{e.class} - #{e.message}"
|
75
|
+
LogSuppressor.puts_log "[Wafris] Backtrace: #{e.backtrace.join("\n")}"
|
55
76
|
@app.call(env)
|
77
|
+
|
56
78
|
end
|
57
79
|
end
|
58
80
|
end
|
data/lib/wafris/version.rb
CHANGED