@@ -35,6 +35,10 @@ local vars_schema = {
35
35
type = " array" ,
36
36
}
37
37
38
+ local lrucache = core .lrucache .new ({
39
+ ttl = 300 , count = 1024
40
+ })
41
+
38
42
local match_schema = {
39
43
type = " array" ,
40
44
items = {
@@ -48,8 +52,11 @@ local match_schema = {
48
52
local plugin_schema = {
49
53
type = " object" ,
50
54
properties = {
51
- -- TODO: we should add a configuration "mode" here
52
- -- It can be one of off, block and monitor
55
+ mode = {
56
+ type = " string" ,
57
+ enum = { " off" , " monitor" , " block" , nil },
58
+ default = nil ,
59
+ },
53
60
match = match_schema ,
54
61
append_waf_resp_header = {
55
62
type = " boolean" ,
@@ -79,6 +86,9 @@ local plugin_schema = {
79
86
},
80
87
keepalive_timeout = {
81
88
type = " integer" ,
89
+ },
90
+ real_client_ip = {
91
+ type = " boolean"
82
92
}
83
93
},
84
94
},
@@ -88,6 +98,11 @@ local plugin_schema = {
88
98
local metadata_schema = {
89
99
type = " object" ,
90
100
properties = {
101
+ mode = {
102
+ type = " string" ,
103
+ enum = { " off" , " monitor" , " block" , nil },
104
+ default = nil ,
105
+ },
91
106
nodes = {
92
107
type = " array" ,
93
108
items = {
@@ -136,8 +151,10 @@ local metadata_schema = {
136
151
type = " integer" ,
137
152
default = 60000 -- milliseconds
138
153
},
139
- -- TODO: we need a configuration to enable/disable the real client ip
140
- -- the real client ip is calculated by APISIX
154
+ real_client_ip = {
155
+ type = " boolean" ,
156
+ default = true
157
+ }
141
158
},
142
159
default = {},
143
160
},
@@ -163,6 +180,7 @@ local HEADER_CHAITIN_WAF_ACTION = "X-APISIX-CHAITIN-WAF-ACTION"
163
180
local HEADER_CHAITIN_WAF_SERVER = " X-APISIX-CHAITIN-WAF-SERVER"
164
181
local blocked_message = [[ {"code": %s, "success":false, ]] ..
165
182
[[ "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "%s"}]]
183
+ local warning_message = " chaitin-waf monitor mode: request would have been rejected, event_id: "
166
184
167
185
168
186
function _M .check_schema (conf , schema_type )
@@ -224,33 +242,37 @@ end
224
242
225
243
226
244
local function check_match (conf , ctx )
227
- local match_passed = true
245
+ if not conf .match or # conf .match == 0 then
246
+ return true
247
+ end
228
248
229
- if conf .match then
230
- for _ , match in ipairs (conf .match ) do
231
- -- todo: use lrucache to cache the result
232
- local exp , err = expr .new (match .vars )
233
- if err then
234
- local msg = " failed to create match expression for " ..
249
+ for _ , match in ipairs (conf .match ) do
250
+ local cache_key = tostring (match .vars )
251
+
252
+ local exp , err = lrucache (cache_key , nil , function (vars )
253
+ return expr .new (vars )
254
+ end , match .vars )
255
+
256
+ if not exp then
257
+ local msg = " failed to create match expression for " ..
235
258
tostring (match .vars ) .. " , err: " .. tostring (err )
236
- core .log .error (msg )
237
- return false , msg
238
- end
259
+ return false , msg
260
+ end
239
261
240
- match_passed = exp :eval (ctx .var )
241
- if match_passed then
242
- break
243
- end
262
+ local matched = exp :eval (ctx .var )
263
+ if matched then
264
+ return true
244
265
end
245
266
end
246
267
247
- return match_passed , nil
268
+ return false
248
269
end
249
270
250
271
251
272
local function get_conf (conf , metadata )
252
273
local t = {
253
274
mode = " block" ,
275
+ real_client_ip = true ,
254
276
}
255
277
256
278
if metadata .config then
@@ -260,6 +282,7 @@ local function get_conf(conf, metadata)
260
282
t .req_body_size = metadata .config .req_body_size
261
283
t .keepalive_size = metadata .config .keepalive_size
262
284
t .keepalive_timeout = metadata .config .keepalive_timeout
285
+ t .real_client_ip = metadata .config .real_client_ip or t .real_client_ip
263
286
end
264
287
265
288
if conf .config then
@@ -269,27 +292,18 @@ local function get_conf(conf, metadata)
269
292
t .req_body_size = conf .config .req_body_size
270
293
t .keepalive_size = conf .config .keepalive_size
271
294
t .keepalive_timeout = conf .config .keepalive_timeout
295
+ t .real_client_ip = conf .config .real_client_ip or t .real_client_ip
272
296
end
273
297
298
+ t .mode = conf .mode or metadata .mode or t .mode
299
+
274
300
return t
275
301
end
276
302
277
303
278
304
local function do_access (conf , ctx )
279
305
local extra_headers = {}
280
306
281
- local match , err = check_match (conf , ctx )
282
- if not match then
283
- if err then
284
- extra_headers [HEADER_CHAITIN_WAF ] = " err"
285
- extra_headers [HEADER_CHAITIN_WAF_ERROR ] = tostring (err )
286
- return 500 , nil , extra_headers
287
- else
288
- extra_headers [HEADER_CHAITIN_WAF ] = " no"
289
- return nil , nil , extra_headers
290
- end
291
- end
292
-
293
307
local metadata = plugin .plugin_metadata (plugin_name )
294
308
if not core .table .try_read_attr (metadata , " value" , " nodes" ) then
295
309
extra_headers [HEADER_CHAITIN_WAF ] = " err"
@@ -306,43 +320,77 @@ local function do_access(conf, ctx)
306
320
end
307
321
308
322
core .log .info (" picked chaitin-waf server: " , host , " :" , port )
309
-
310
323
local t = get_conf (conf , metadata .value )
311
324
t .host = host
312
325
t .port = port
313
326
314
327
extra_headers [HEADER_CHAITIN_WAF_SERVER ] = host
315
- extra_headers [HEADER_CHAITIN_WAF ] = " yes"
328
+
329
+ local mode = t .mode or " block"
330
+ if mode == " off" then
331
+ extra_headers [HEADER_CHAITIN_WAF ] = " off"
332
+ return nil , nil , extra_headers
333
+ end
334
+
335
+ local match , err = check_match (conf , ctx )
336
+ if not match then
337
+ if err then
338
+ extra_headers [HEADER_CHAITIN_WAF ] = " err"
339
+ extra_headers [HEADER_CHAITIN_WAF_ERROR ] = tostring (err )
340
+ return 500 , nil , extra_headers
341
+ else
342
+ extra_headers [HEADER_CHAITIN_WAF ] = " no"
343
+ return nil , nil , extra_headers
344
+ end
345
+ end
346
+
347
+ if t .real_client_ip then
348
+ t .client_ip = ctx .var .http_x_forwarded_for or ctx .var .remote_addr
349
+ else
350
+ t .client_ip = ctx .var .remote_addr
351
+ end
316
352
317
353
local start_time = ngx_now () * 1000
318
354
local ok , err , result = t1k .do_access (t , false )
355
+
356
+ extra_headers [HEADER_CHAITIN_WAF_TIME ] = ngx_now () * 1000 - start_time
357
+
319
358
if not ok then
320
359
extra_headers [HEADER_CHAITIN_WAF ] = " waf-err"
321
360
local err_msg = tostring (err )
322
361
if core .string .find (err_msg , " timeout" ) then
323
362
extra_headers [HEADER_CHAITIN_WAF ] = " timeout"
324
363
end
325
364
extra_headers [HEADER_CHAITIN_WAF_ERROR ] = tostring (err )
365
+
366
+ if mode == " monitor" then
367
+ core .log .warn (" chaitin-waf monitor mode: detected waf error - " , err_msg )
368
+ return nil , nil , extra_headers
369
+ end
370
+
371
+ return 500 , nil , extra_headers
326
372
else
373
+ extra_headers [HEADER_CHAITIN_WAF ] = " yes"
327
374
extra_headers [HEADER_CHAITIN_WAF_ACTION ] = " pass"
328
375
end
329
- extra_headers [HEADER_CHAITIN_WAF_TIME ] = ngx_now () * 1000 - start_time
330
376
331
377
local code = 200
332
378
extra_headers [HEADER_CHAITIN_WAF_STATUS ] = code
333
- if result and result .status and result .status ~= 200 then
334
- if result .event_id then
335
- code = result .status
336
- extra_headers [HEADER_CHAITIN_WAF_STATUS ] = code
337
- extra_headers [HEADER_CHAITIN_WAF_ACTION ] = " reject"
338
-
339
- core .log .error (" request rejected by chaitin-waf, event_id: " .. result .event_id )
340
- return tonumber (code ), fmt (blocked_message , code ,
341
- result .event_id ) .. " \n " , extra_headers
379
+
380
+ if result and result .status and result .status ~= 200 and result .event_id then
381
+ extra_headers [HEADER_CHAITIN_WAF_STATUS ] = result .status
382
+ extra_headers [HEADER_CHAITIN_WAF_ACTION ] = " reject"
383
+
384
+ if mode == " monitor" then
385
+ core .log .warn (warning_message , result .event_id )
386
+ return nil , nil , extra_headers
342
387
end
343
- end
344
- if not ok then
345
- extra_headers [HEADER_CHAITIN_WAF_STATUS ] = nil
388
+
389
+ core .log .error (" request rejected by chaitin-waf, event_id: " .. result .event_id )
390
+
391
+ return tonumber (result .status ),
392
+ fmt (blocked_message , result .status , result .event_id ) .. " \n " ,
393
+ extra_headers
346
394
end
347
395
348
396
return nil , nil , extra_headers
0 commit comments