diff --git a/samples/ip_blacklist/ip_blacklist.lua b/samples/ip_blacklist/ip_blacklist.lua new file mode 100644 index 0000000..6e22465 --- /dev/null +++ b/samples/ip_blacklist/ip_blacklist.lua @@ -0,0 +1,198 @@ +--[[ +¡ ¡ ¡ ALL GLORY TO GLORIA ! ! ! +--]] +local redis = ip_blacklist.redis:new() +if not redis then + ngx.log(ngx.ERR, "ip_blacklist: Failed new redis") + ngx.exit(ngx.OK) +end +--~ if ip_blacklist.debug then ngx.log(ngx.INFO, "ip_blacklist: success connect to redis") end +redis:set_timeout(ip_blacklist.redis_timeout) + +local connected, err = redis:connect(ip_blacklist.redis_host, ip_blacklist.redis_port) +if not connected then + ngx.log(ngx.ERR, string.format("ip_blacklist: could not connect to redis=[%s:%s]:", ip_blacklist.redis_host, ip_blacklist.redis_port), err) + ngx.exit(ngx.OK) +end + +redis_end = ip_blacklist.redis_end +--~ ключ счетчика = <временной интервал (- % колич секунд)>:: +local now = os.time() +local time_range = now % ip_blacklist.time_range +local key_count = ip_blacklist.join({ip_blacklist.prefix, 'count', now - time_range, ngx.var.remote_addr, ngx.var.uri}) +-- ключ кэшированного документа +local key_doc = ip_blacklist.join({ip_blacklist.prefix, 'doc', ngx.var.host, ngx.var.uri}) +-- ключ ловушки = +local key_lock = ip_blacklist.join({ip_blacklist.prefix, 'lock', ngx.var.remote_addr, ngx.var.uri}) +local key_lock_ex, err = redis:exists(key_lock) +if key_lock_ex == 1 then -- он уже в ловушке + if ip_blacklist.debug then ngx.log(ngx.INFO, string.format("ip_blacklist: found key_lock=[%s]", key_lock)) end + + local result, err = redis:expire(key_lock, ip_blacklist.time_lock) -- продлить бан + if not result then + ngx.log(ngx.ERR, string.format("ip_blacklist: redis:expire failed for key=[%s]:", key_lock), err) + redis_end(redis) + ngx.exit(ngx.OK) + end + + if ip_blacklist.mode == 'forbidden' then + redis_end(redis) + ngx.exit(ngx.HTTP_FORBIDDEN) + end + + local doc_ex, err = redis:exists(key_doc) + if err then + ngx.log(ngx.ERR, string.format("ip_blacklist: redis:exists failed for key=[%s]:", key_doc), err) + redis_end(redis) + ngx.exit(ngx.OK) + elseif doc_ex == 1 then --~ ага, отдача кэша + local hash, err = redis:hmget(key_doc, "status", "content_type", "content_length", "body") + if err then + ngx.log(ngx.ERR, string.format("ip_blacklist: redis:hmget failed for key=[%s]:", key_doc), err) + redis_end(redis) + ngx.exit(ngx.OK) + elseif not hash[1] then -- Нет статуса! + redis:del(key_doc) + redis_end(redis) + ngx.exit(ngx.OK) + end + + ngx.log(ngx.INFO, string.format("ip_blacklist: page from redis:hmget [%s]", key_doc)) + --~ for key,value in pairs(hash) do print(key,value) end + ngx.status = hash[1] --"status" + ngx.header.content_type = hash[2] -- "content_type" + ngx.header.content_length = hash[3] or string.len(hash[4]) --"content_length" + ngx.say(hash[4]) -- "body" + redis_end(redis) + ngx.exit(hash[1]) --"status" + end +else -- счетчик+попадание в ловушку + local count, err = redis:incr(key_count) + if not count then + ngx.log(ngx.ERR, string.format("ip_blacklist: [redis:incr] failed for key=[%s]:", key_count), err) + redis_end(redis) + ngx.exit(ngx.OK) + end + + if count == 1 then + local result, err = redis:expire(key_count, ip_blacklist.time_range - time_range + 1) -- 3-0 3-1 3-2 + if not result then + ngx.log(ngx.ERR, string.format("ip_blacklist: [redis:expire] failed for key=[%s]:", key_count), err) + redis_end(redis) + ngx.exit(ngx.OK) + end + end + + + if count < ip_blacklist.req_limit then + if ip_blacklist.debug then ngx.log(ngx.INFO, string.format("ip_blacklist: allow key_count=[%s] count=[%s] ", key_count, count)) end + redis_end(redis) + ngx.exit(ngx.OK) + end + + if ip_blacklist.debug then ngx.log(ngx.INFO, string.format("ip_blacklist: deny key_count=[%s] count=[%s] ", key_count, count)) end + -- лог попаданий в ловушку +-- посмотреть lrange ip_blacklist 0 -1 + local result, err = redis:rpush(ip_blacklist.prefix, key_count) + if err then ngx.log(ngx.ERR, string.format("ip_blacklist: [redis:rpush] failed for key=[%s] and value[%s]:", ip_blacklist.prefix, key_count), err) end + if ip_blacklist.debug then ngx.log(ngx.INFO, string.format("ip_blacklist: logging on lock-list=[%s] for key_count=[%s] ", ip_blacklist.prefix, key_count)) end + + if ip_blacklist.mode == 'count' then + if ip_blacklist.debug then ngx.log(ngx.INFO, string.format("ip_blacklist: mode count exit for key_count=[%s] count=[%s] ", key_count, count)) end + redis_end(redis) + ngx.exit(ngx.OK) + end + + local result, err = redis:incr(key_lock) -- ловушка + if not result then + ngx.log(ngx.ERR, string.format("ip_blacklist: [redis:incr] failed for key=[%s]:", key_lock), err) + redis_end(redis) + ngx.exit(ngx.OK) + end + local result, err = redis:expire(key_lock, ip_blacklist.time_lock) -- срок в ловушке + if not result then + ngx.log(ngx.ERR, string.format("ip_blacklist: redis:expire failed for key=[%s]:", key_lock), err) + redis_end(redis) + ngx.exit(ngx.OK) + end +end + + +if ip_blacklist.mode == 'nocapture' then + if ip_blacklist.debug then ngx.log(ngx.INFO, string.format("ip_blacklist: mode nocapture exit for key_lock=[%s] ", key_lock)) end + redis_end(redis) + ngx.exit(ngx.OK) +end + + +local doc_ex, err = redis:exists(key_doc) +if err then + ngx.log(ngx.ERR, string.format("ip_blacklist: redis:exists failed for key=[%s]:", key_doc), err) + redis_end(redis) + ngx.exit(ngx.OK) +elseif doc_ex == 1 then --~ ага, уже есть в кэше + if ip_blacklist.debug then ngx.log(ngx.INFO, string.format("ip_blacklist: already stored key_doc=[%s]", key_doc)) end + redis_end(redis) + ngx.exit(ngx.OK) +end + +--~ if ip_blacklist.mode == 'nocache' then ip_blacklist.deny_resp() end +--[[ +Кэшируем страницу для следующих заходов +Плохо забирает страницу, не полностью +--]] +--You should always read the request body (by either calling ngx.req.read_body or configuring lua_need_request_body on) before initiating a subrequest. +ngx.req.read_body() + +if ip_blacklist.debug then ngx.log(ngx.INFO, "ip_blacklist: location.capture=", ngx.var.uri) end +local cap = ngx.location.capture(ngx.var.uri) +if cap.status >= 500 then + redis_end(redis) + ngx.exit(cap.status) +end + +--~ for k, v in pairs(cap.header) do print(k,'=', v) end +if not (cap.status == ngx.HTTP_OK and cap.header['Content-Type'] and (string.match(cap.header['Content-Type'], 'text') or string.match(cap.header['Content-Type'], 'json'))) then + redis:decr(key_count) + ngx.log(ngx.INFO, string.format("ip_blacklist: skip cache non HTTP_OK [%s]; non text content type [%s]", cap.status, cap.header['Content-Type'])) + ngx.status = cap.status + for k, v in pairs(cap.header) do ngx.header[k] = v end + ngx.say(cap.body) + redis_end(redis) + ngx.exit(cap.status) +--~ ngx.exit(ngx.OK) +end + +local body = string.gsub(cap.body, "", "

Внимание! Вы находитесь в зоне выполнения регламентного обновления сайта. Пожалуйста, обновите страницу позже. Спасибо.

") +-- сохранить в кэше +local result, err = redis:hmset( + key_doc, + "status", cap.status, + "content_type", cap.header['Content-Type'], + "content_length", cap.header['Content-Length'] or string.len(body), + "body", body +) -- "content_length", cap.header['Content-Length'] +if not result then + ngx.log(ngx.ERR, string.format("ip_blacklist: [redis:hmset] failed for key=[%s]", key_doc), err) +else + ngx.log(ngx.INFO, string.format("ip_blacklist: stored in redis:hmset [%s] for next requests", key_doc)) +end +redis_end(redis) +ngx.exit(ngx.OK) + +--[[ +!!! Отдача кэша в ip_blacklist.from_redis() в следующем заходе ! ! ! + +ngx.status = cap.status -- ngx.HTTP_OK -- +ngx.header.content_type = cap.header['Content-Type']; +ngx.header.content_length = string.len(body); +--~ cap.header -- holds all the response headers of the subrequest and it is a normal Lua table. +ngx.say(body) +--~ ngx.log(ngx.INFO, "Deny: body", cap.body) +--~ return ngx.exit(ngx.HTTP_OK) +redis_end(redis) +ngx.exit(cap.status) +--]] + + +