Skip to content

Commit 5d9dc88

Browse files
authored
Merge pull request #307 from TheophileDiot/Restrict-access-IP-NET
[#285] Add the greylisting feature
2 parents 67d514b + 40863f2 commit 5d9dc88

File tree

3 files changed

+522
-0
lines changed

3 files changed

+522
-0
lines changed

core/greylist/greylist.lua

+247
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
local _M = {}
2+
_M.__index = _M
3+
4+
local utils = require "utils"
5+
local datastore = require "datastore"
6+
local logger = require "logger"
7+
local cjson = require "cjson"
8+
local ipmatcher = require "resty.ipmatcher"
9+
10+
function _M.new()
11+
local self = setmetatable({}, _M)
12+
return self, nil
13+
end
14+
15+
function _M:init()
16+
-- Check if init is needed
17+
local init_needed, err = utils.has_variable("USE_GREYLIST", "yes")
18+
if init_needed == nil then
19+
return false, err
20+
end
21+
if not init_needed then
22+
return true, "no service uses Greylist, skipping init"
23+
end
24+
-- Read greylists
25+
local greylists = {
26+
["IP"] = {},
27+
["RDNS"] = {},
28+
["ASN"] = {},
29+
["USER_AGENT"] = {},
30+
["URI"] = {}
31+
}
32+
local i = 0
33+
for kind, _ in pairs(greylists) do
34+
local f, err = io.open("/opt/bunkerweb/cache/greylist/" .. kind .. ".list", "r")
35+
if f then
36+
for line in f:lines() do
37+
table.insert(greylists[kind], line)
38+
i = i + 1
39+
end
40+
f:close()
41+
end
42+
end
43+
-- Load them into datastore
44+
local ok, err = datastore:set("plugin_greylist_list", cjson.encode(greylists))
45+
if not ok then
46+
return false, "can't store Greylist list into datastore : " .. err
47+
end
48+
return true, "successfully loaded " .. tostring(i) .. " greylisted IP/network/rDNS/ASN/User-Agent/URI"
49+
end
50+
51+
function _M:access()
52+
-- Check if access is needed
53+
local access_needed, err = utils.get_variable("USE_GREYLIST")
54+
if access_needed == nil then
55+
return false, err, false, nil
56+
end
57+
if access_needed ~= "yes" then
58+
return true, "Greylist not activated", false, nil
59+
end
60+
61+
-- Check the cache
62+
local cached_ip, err = self:is_in_cache("ip" .. ngx.var.remote_addr)
63+
if cached_ip and cached_ip ~= "ok" then
64+
return true, "IP is in greylist cache (info = " .. cached_ip .. ")", false, ngx.OK
65+
end
66+
local cached_uri, err = self:is_in_cache("uri" .. ngx.var.uri)
67+
if cached_uri and cached_uri ~= "ok" then
68+
return true, "URI is in greylist cache (info = " .. cached_uri .. ")", false, ngx.OK
69+
end
70+
local cached_ua = true
71+
if ngx.var.http_user_agent then
72+
cached_ua, err = self:is_in_cache("ua" .. ngx.var.http_user_agent)
73+
if cached_ua and cached_ua ~= "ok" then
74+
return true, "User-Agent is in greylist cache (info = " .. cached_ua .. ")", false, ngx.OK
75+
end
76+
end
77+
if cached_ip and cached_uri and cached_ua then
78+
return true, "full request is in greylist cache (not greylisted)", false, nil
79+
end
80+
81+
-- Get list
82+
local data, err = datastore:get("plugin_greylist_list")
83+
if not data then
84+
return false, "can't get Greylist list : " .. err, false, nil
85+
end
86+
local ok, greylists = pcall(cjson.decode, data)
87+
if not ok then
88+
return false, "error while decoding greylists : " .. greylists, false, nil
89+
end
90+
91+
-- Return value
92+
local ret, ret_err = true, "success"
93+
94+
-- Check if IP is in IP/net greylist
95+
local ip_net, err = utils.get_variable("GREYLIST_IP")
96+
if ip_net and ip_net ~= "" then
97+
for element in ip_net:gmatch("%S+") do
98+
table.insert(greylists["IP"], element)
99+
end
100+
end
101+
if not cached_ip then
102+
local ipm, err = ipmatcher.new(greylists["IP"])
103+
if not ipm then
104+
ret = false
105+
ret_err = "can't instantiate ipmatcher " .. err
106+
else
107+
if ipm:match(ngx.var.remote_addr) then
108+
self:add_to_cache("ip" .. ngx.var.remote_addr, "ip/net")
109+
return ret, "client IP " .. ngx.var.remote_addr .. " is in greylist", false, ngx.OK
110+
end
111+
end
112+
end
113+
114+
-- Check if rDNS is in greylist
115+
local rdns_global, err = utils.get_variable("GREYLIST_RDNS_GLOBAL")
116+
local check = true
117+
if not rdns_global then
118+
logger.log(ngx.ERR, "GREYLIST", "Error while getting GREYLIST_RDNS_GLOBAL variable : " .. err)
119+
elseif rdns_global == "yes" then
120+
check, err = utils.ip_is_global(ngx.var.remote_addr)
121+
if check == nil then
122+
logger.log(ngx.ERR, "GREYLIST", "Error while getting checking if IP is global : " .. err)
123+
end
124+
end
125+
if not cached_ip and check then
126+
local rdns, err = utils.get_rdns(ngx.var.remote_addr)
127+
if not rdns then
128+
ret = false
129+
ret_err = "error while trying to get reverse dns : " .. err
130+
else
131+
local rdns_list, err = utils.get_variable("GREYLIST_RDNS")
132+
if rdns_list and rdns_list ~= "" then
133+
for element in rdns_list:gmatch("%S+") do
134+
table.insert(greylists["RDNS"], element)
135+
end
136+
end
137+
for i, suffix in ipairs(greylists["RDNS"]) do
138+
if rdns:sub(- #suffix) == suffix then
139+
self:add_to_cache("ip" .. ngx.var.remote_addr, "rDNS " .. suffix)
140+
return ret, "client IP " .. ngx.var.remote_addr .. " is in greylist (info = rDNS " .. suffix .. ")", false, ngx.OK
141+
end
142+
end
143+
end
144+
end
145+
146+
-- Check if ASN is in greylist
147+
if not cached_ip then
148+
if utils.ip_is_global(ngx.var.remote_addr) then
149+
local asn, err = utils.get_asn(ngx.var.remote_addr)
150+
if not asn then
151+
ret = false
152+
ret_err = "error while trying to get asn number : " .. err
153+
else
154+
local asn_list, err = utils.get_variable("GREYLIST_ASN")
155+
if asn_list and asn_list ~= "" then
156+
for element in asn_list:gmatch("%S+") do
157+
table.insert(greylists["ASN"], element)
158+
end
159+
end
160+
for i, asn_bl in ipairs(greylists["ASN"]) do
161+
if tostring(asn) == asn_bl then
162+
self:add_to_cache("ip" .. ngx.var.remote_addr, "ASN " .. tostring(asn))
163+
return ret, "client IP " .. ngx.var.remote_addr .. " is in greylist (kind = ASN " .. tostring(asn) .. ")", false,
164+
ngx.OK
165+
end
166+
end
167+
end
168+
end
169+
end
170+
171+
-- IP is not greylisted
172+
local ok, err = self:add_to_cache("ip" .. ngx.var.remote_addr, "ok")
173+
if not ok then
174+
ret = false
175+
ret_err = err
176+
end
177+
178+
-- Check if User-Agent is in greylist
179+
if not cached_ua and ngx.var.http_user_agent then
180+
local ua_list, err = utils.get_variable("GREYLIST_USER_AGENT")
181+
if ua_list and ua_list ~= "" then
182+
for element in ua_list:gmatch("%S+") do
183+
table.insert(greylists["USER_AGENT"], element)
184+
end
185+
end
186+
for i, ua_bl in ipairs(greylists["USER_AGENT"]) do
187+
if ngx.var.http_user_agent:match(ua_bl) then
188+
self:add_to_cache("ua" .. ngx.var.http_user_agent, "UA " .. ua_bl)
189+
return ret, "client User-Agent " .. ngx.var.http_user_agent .. " is in greylist (matched " .. ua_bl .. ")", false,
190+
ngx.OK
191+
end
192+
end
193+
-- UA is not greylisted
194+
local ok, err = self:add_to_cache("ua" .. ngx.var.http_user_agent, "ok")
195+
if not ok then
196+
ret = false
197+
ret_err = err
198+
end
199+
end
200+
201+
-- Check if URI is in greylist
202+
if not cached_uri then
203+
local uri_list, err = utils.get_variable("GREYLIST_URI")
204+
if uri_list and uri_list ~= "" then
205+
for element in uri_list:gmatch("%S+") do
206+
table.insert(greylists["URI"], element)
207+
end
208+
end
209+
for i, uri_bl in ipairs(greylists["URI"]) do
210+
if ngx.var.uri:match(uri_bl) then
211+
self:add_to_cache("uri" .. ngx.var.uri, "URI " .. uri_bl)
212+
return ret, "client URI " .. ngx.var.uri .. " is in greylist (matched " .. uri_bl .. ")", false, ngx.OK
213+
end
214+
end
215+
end
216+
217+
-- URI is not greylisted
218+
local ok, err = self:add_to_cache("uri" .. ngx.var.uri, "ok")
219+
if not ok then
220+
ret = false
221+
ret_err = err
222+
end
223+
224+
return ret, "IP is not in list (error = " .. ret_err .. ")", true, utils.get_deny_status()
225+
end
226+
227+
function _M:is_in_cache(ele)
228+
local kind, err = datastore:get("plugin_greylist_cache_" .. ngx.var.server_name .. ele)
229+
if not kind then
230+
if err ~= "not found" then
231+
logger.log(ngx.ERR, "GREYLIST", "Error while accessing cache : " .. err)
232+
end
233+
return false, err
234+
end
235+
return kind, "success"
236+
end
237+
238+
function _M:add_to_cache(ele, kind)
239+
local ok, err = datastore:set("plugin_greylist_cache_" .. ngx.var.server_name .. ele, kind, 3600)
240+
if not ok then
241+
logger.log(ngx.ERR, "GREYLIST", "Error while adding element to cache : " .. err)
242+
return false, err
243+
end
244+
return true, "success"
245+
end
246+
247+
return _M

0 commit comments

Comments
 (0)