1
1
local cjson = require (' cjson' )
2
2
local http = require (' resty.http' )
3
+ local iputils = require (' resty.iputils' )
3
4
local lrucache = require (' resty.lrucache' )
4
5
local statsd = require (' resty.statsd' )
5
6
@@ -18,13 +19,12 @@ function _M.new(options)
18
19
iprepd_url = iprepd_url :sub (1 , - 2 )
19
20
end
20
21
21
- local cache_ttl = options .cache_ttl or 30
22
+ local cache_buffer_count = options .cache_buffer_count or 200
22
23
23
24
local iprepd_threshold = options .threshold or fatal_error (' Need to pass in a threshold' )
24
25
local iprepd_api_key = options .api_key or fatal_error (' Need to pass in an api_key' )
25
26
26
- -- TODO: Make configurable?
27
- local cache , err = lrucache .new (200 )
27
+ local cache , err = lrucache .new (cache_buffer_count )
28
28
if not cache then
29
29
fatal_error (' failed to create the cache: ' .. (err or ' unknown' ))
30
30
end
@@ -34,33 +34,82 @@ function _M.new(options)
34
34
statsd_client = statsd
35
35
end
36
36
37
+ local whitelist = nil
38
+ local whitelist_list = options .whitelist or nil
39
+ if whitelist_list then
40
+ whitelist = iputils .parse_cidrs (whitelist_list )
41
+ end
42
+
37
43
local self = {
38
44
url = iprepd_url ,
45
+ timeout = options .timeout or 10 ,
39
46
threshold = iprepd_threshold ,
40
47
api_key_hdr = {
41
48
[' Authorization' ] = ' APIKey ' .. iprepd_api_key ,
42
49
},
43
- cache_ttl = cache_ttl ,
44
- timeout = options .timeout or 10 ,
45
50
cache = cache ,
51
+ cache_ttl = options .cache_ttl or 30 ,
46
52
cache_errors = options .cache_errors or 0 ,
47
53
statsd = statsd_client ,
48
54
statsd_host = options .statsd_host ,
49
55
statsd_port = options .statsd_port or 8125 ,
50
56
statsd_max_buffer_count = options .statsd_max_buffer_count or 100 ,
57
+ statsd_flush_timer = options .statsd_flush_timer or 5 ,
58
+ dont_block = options .dont_block or 0 ,
59
+ whitelist = whitelist ,
51
60
}
52
61
53
62
return setmetatable (self , mt )
54
63
end
55
64
56
65
function _M .check (self , ip )
57
- local httpc = http .new ()
58
- -- set timeout in ms
59
- httpc :set_timeout (self .timeout )
66
+ ngx .req .set_header (' X-Foxsec-IP-Reputation-Below-Threshold' , ' false' )
67
+ ngx .req .set_header (' X-Foxsec-Block' , ' false' )
68
+ if self .whitelist then
69
+ if iputils .ip_in_cidrs (ip , self .whitelist ) then
70
+ return
71
+ end
72
+ end
60
73
61
- -- Get reputation for ip
74
+ local reputation = self :get_reputation (ip )
75
+ if reputation then
76
+ ngx .req .set_header (' X-Foxsec-IP-Reputation' , tostring (reputation ))
77
+ if reputation <= self .threshold then
78
+ ngx .req .set_header (' X-Foxsec-IP-Reputation-Below-Threshold' , ' true' )
79
+ ngx .req .set_header (' X-Foxsec-Block' , ' true' )
80
+ if self .statsd then
81
+ self .statsd .incr (" iprepd.status.below_threshold" )
82
+ end
83
+
84
+ if self .dont_block == 1 then
85
+ ngx .log (ngx .ERR , ip .. ' is below threshold with a reputation of ' .. reputation )
86
+ else
87
+ ngx .log (ngx .ERR , ip .. ' rejected with a reputation of ' .. reputation )
88
+ if self .statsd then
89
+ self .statsd .incr (" iprepd.status.rejected" )
90
+ end
91
+ ngx .exit (ngx .HTTP_FORBIDDEN )
92
+ end
93
+ else
94
+ if self .statsd then
95
+ self .statsd .incr (" iprepd.status.accepted" )
96
+ end
97
+ end
98
+
99
+ return
100
+ end
101
+
102
+ if self .statsd then
103
+ self .statsd .incr (" iprepd.status.accepted" )
104
+ end
105
+ end
106
+
107
+ function _M .get_reputation (self , ip )
62
108
local reputation = self .cache :get (ip )
109
+
63
110
if not reputation then
111
+ local httpc = http .new ()
112
+ httpc :set_timeout (self .timeout )
64
113
local resp , err = httpc :request_uri (self .url .. ' /' .. ip , {
65
114
method = " GET" ,
66
115
headers = self .api_key_hdr ,
@@ -70,7 +119,7 @@ function _M.check(self, ip)
70
119
self .statsd .incr (" iprepd.err.timeout" )
71
120
end
72
121
ngx .log (ngx .ERR , ' Error with request to iprepd: ' .. err )
73
- return
122
+ return nil
74
123
end
75
124
76
125
-- If the IP was found
@@ -85,26 +134,17 @@ function _M.check(self, ip)
85
134
self .cache :set (ip , 100 , self .cache_ttl )
86
135
else
87
136
ngx .log (ngx .ERR , ' iprepd responded with a ' .. resp .status .. ' http status code' )
137
+ if self .statsd then
138
+ self .statsd .incr (" iprepd.err." .. resp .status )
139
+ end
88
140
if self .cache_errors == 1 then
89
141
ngx .log (ngx .ERR , ' cache_errors is enabled, setting reputation of ' .. ip .. ' to 100 within the cache' )
90
142
self .cache :set (ip , 100 , self .cache_ttl )
91
143
end
92
144
end
93
145
end
94
146
95
- -- check reputation against threshold
96
- if reputation and reputation <= self .threshold then
97
- -- return 403 and log rejections
98
- ngx .log (ngx .ERR , ip .. ' rejected with a reputation of ' .. reputation )
99
- if self .statsd then
100
- self .statsd .incr (" iprepd.status.rejected" )
101
- end
102
- ngx .exit (ngx .HTTP_FORBIDDEN )
103
- else
104
- if self .statsd then
105
- self .statsd .incr (" iprepd.status.accepted" )
106
- end
107
- end
147
+ return reputation
108
148
end
109
149
110
150
function _M .flush_stats (self )
@@ -120,7 +160,7 @@ function _M.async_flush_stats(premature, self)
120
160
end
121
161
122
162
function _M .config_flush_timer (self )
123
- ngx .timer .every (5 , self .async_flush_stats , self )
163
+ ngx .timer .every (self . statsd_flush_timer , self .async_flush_stats , self )
124
164
end
125
165
126
166
return _M
0 commit comments