diff --git a/contrib/ip_blacklist/ip_blacklist.lua b/contrib/ip_blacklist/ip_blacklist.lua new file mode 100644 index 0000000..a3bc016 --- /dev/null +++ b/contrib/ip_blacklist/ip_blacklist.lua @@ -0,0 +1,147 @@ +--[[ + +¡ ¡ ¡ ALL GLORY TO GLORIA ! ! ! +=============================== +Settings on ip_tables_conf.lua + +--]] + +local _M = require "ip_blacklist_conf" +local log_info = _M.log_info +local log_err = _M.log_err +local redis = _M.redis_connect() +local redis_exists = _M.redis_exists +local redis_expire = _M.redis_expire +local redis_incr = _M.redis_incr +local redis_end = _M.redis_end +local key_count, key_lock, key_doc, time_range = _M.redis_keys() + + +if redis_exists(redis, key_lock) then -- is IP locked? + redis_expire(redis, key_lock, _M.time_lock) -- prolong the lock! + + if _M.mode == 'forbidden' then + redis_end(redis) + ngx.exit(ngx.HTTP_FORBIDDEN) + end + + if redis_exists(redis, key_doc) then --~ found content ? + local hash, err = redis:hmget(key_doc, "status", "content_type", "content_length", "body") + if err then + log_err(string.format("redis:hmget failed for key=[%s]: %s", key_doc, err)) + redis_end(redis) + ngx.exit(ngx.OK) + elseif not hash[1] then -- not status + redis:del(key_doc) + redis_end(redis) + ngx.exit(ngx.OK) + end + + log_info(string.format("locked request content 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" + else + log_info(string.format("locked request content not found on redis:[hmget '%s']", key_doc)) + redis_end(redis) + ngx.exit(ngx.HTTP_FORBIDDEN) + end +end + +--~ else -- counter block +local count = redis_incr(redis, key_count) + +if count == 1 then + redis_expire(redis, key_count, _M.time_range - time_range + 1) +end + + +if count < _M.req_limit then + log_info(string.format("allow request for key_count=[%s] count=[%s] ", key_count, count)) + redis_end(redis) + ngx.exit(ngx.OK) +end + +log_info(string.format("locked request for key_count=[%s] count=[%s] ", key_count, count)) +-- redis-cli lrange <_M.prefix> 0 -1 +if _M.log_lock then + local _, err = redis:rpush(_M.prefix, key_count) + if err then log_err(string.format("[redis:rpush] failed for key=[%s] and value[%s]: %s", _M.prefix, key_count, err)) end + log_info(string.format("logging the lock for key_count=[%s] ", key_count)) +end + +if _M.mode == 'count' then + log_info(string.format("mode count exit for key_count=[%s] count=[%s] ", key_count, count)) + redis_end(redis) + ngx.exit(ngx.OK) +end + +redis_incr(redis, key_lock) -- add IP to lock +redis_expire(redis, key_lock, _M.time_lock) -- for locking time +--~ end -- counter block + + +if _M.mode == 'nocapture' then + log_info(string.format("mode nocapture exit for key_lock=[%s] ", key_lock)) + redis_end(redis) + ngx.exit(ngx.OK) +end + + + +if redis_exists(redis, key_doc) then -- doc in Redis already + log_info(string.format("content already exists in [redis:hgetall '%s']", key_doc)) + redis_end(redis) + ngx.exit(ngx.OK) +end + +--[[ +Prepare store content for next locked requests +The problem: +location.capture and chunked content from Apache +--]] + +ngx.req.read_body() --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. + +log_info("location.capture=" .. ngx.var.uri) +local cap = ngx.location.capture(ngx.var.uri) +if cap.status >= 500 then + log_info("bad location.capture 50X " .. ngx.var.uri) + 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 + log_info(string.format("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) +end + +--~ local body = string.gsub(cap.body, "", "

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

") +local body = string.gsub(cap.body, "", "

WARN! Your request is locked!

") +local _, 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 err then + log_err(string.format("[redis:hmset] failed for key=[%s]: %s", key_doc, err)) +else + log_info(string.format("content [%s%s] stored in redis:[hmset '%s'] for next locked requests", ngx.var.host, ngx.var.uri, key_doc)) +end +redis_end(redis) +ngx.exit(ngx.OK) + + + +