Sindbad~EG File Manager
--[[
This file is kind of our own API to expose Nginx internal and to do stuff on the Nginx level.
Pseudo API doc :
GET /internal?domain=toto.com&action=burst : will purge all cache (vhost, ssl) related to the toto.com domain
GET /internal?action=warmup : will warm up the Vhost/SSL cache for all domains stored on Redis
GET /internal?action=purge : will purge all the Vhost/SSL cache, for all domains
GET /internal?action=export&type={ip,domain}&threshold=1000 : Export counter from nginx internal
GET /internal?action=debug : retrieve some debug info about nginx/lua internal TODO
--]]
local cjson = require "cjson"
local o2debug = require "lib/o2switch_debug"
local o2utils = require "lib/o2switch_utils"
local o2redis = require "lib/o2switch_redis"
local o2ssl = require "lib/o2switch_ssl"
local o2counter = require "lib/o2switch_counter"
local ban = require "lib/o2switch_ban"
local cacheWrapper = require 'lib/o2switch_cache_wrapper'
local ngx = ngx
local ipairs = ipairs
local type = type
local tonumber = tonumber
local argAction = ngx.var.arg_action
local argDomain = ngx.var.arg_domain
local argType = ngx.var.arg_type
local argThreshold = ngx.var.arg_threshold or 1000
local argThreshold = tonumber(argThreshold) ~= nil and tonumber(argThreshold) or 1000
ngx.status = ngx.HTTP_OK
ngx.header.content_type = "application/json; charset=utf-8"
-- --o2debug.debug("Internal API : Request received");
-- ?action={burst, warmup, purge, export, debug}
if not argAction then
--o2debug.debug("Internal API : Action missing");
ngx.say(cjson.encode({
status = false,
reason = 'action missing'
}))
return ngx.exit(ngx.HTTP_OK)
end
-- We only support some action, we check that
if argAction ~= 'burst' and argAction ~= 'warmup' and argAction ~= 'purge'
and argAction ~= 'export' and argAction ~= 'debug' then
--o2debug.debug("Internal API : Action not supported");
ngx.say(cjson.encode({
status = false,
reason = 'action not supported'
}))
return ngx.exit(ngx.HTTP_OK)
end
-- 'burst' action needs the 'domain' parameter
if argAction == 'burst' then
-- ?domain=toto.com
if not argDomain then
--o2debug.debug("Internal API : Burst requested but the domain is missing");
ngx.say(cjson.encode({
status = false,
reason = 'domain missing'
}))
return ngx.exit(ngx.HTTP_OK)
end
end
-- 'export' action needs the 'type' parameter
if argAction == 'burst' then
-- ?domain=toto.com
if not argDomain then
--o2debug.debug("Internal API : Burst requested but the domain is missing");
ngx.say(cjson.encode({
status = false,
reason = 'domain missing'
}))
return ngx.exit(ngx.HTTP_OK)
end
end
if argAction == 'export' then
-- ?type=ip
local validType = {
['ip'] = true,
['domain'] = true,
['ban'] = true,
}
if type(argType) ~= 'string' or not validType[argType] then
--o2debug.debug("Internal API : type not provided or unsupported");
ngx.say(cjson.encode({
status = false,
reason = 'type missing or invalid type'
}))
return ngx.exit(ngx.HTTP_OK)
end
end
--[[
Purge all of the internal cache
GET /internal?action=purge
--]]
if argAction == 'purge' then
local ok, err = cacheWrapper.purgeCache()
if not ok then
--o2debug.debug("Internal API : Purge error" .. (err or "no err msg"));
ngx.say(cjson.encode({
status = false,
reason = "Can't purge. " .. err
}))
return ngx.exit(ngx.HTTP_OK)
end
--o2debug.debug("Internal API : Purged OK");
ngx.say(cjson.encode({
status = true,
reason = "Ok. Purged"
}))
return ngx.exit(ngx.HTTP_OK)
end
--[[
Warmup cache : Vhost & SSL
GET /internal?action=warmup
--]]
if argAction == 'warmup' then
local red = o2redis.getRedisInstance()
if not red then
--o2debug.debug("Internal API : Warmup requested but cant connect to redis");
ngx.say(cjson.encode({
status = false,
reason = "Can't connect to redis"
}))
return ngx.exit(ngx.HTTP_OK)
end
local res, err = red:keys("*.*")
if err then
--o2debug.debug("Internal API : Cant retrieve the keys from redis");
ngx.say(cjson.encode({
status = false,
reason = "Cant retrieve the keys from redis"
}))
return ngx.exit(ngx.HTTP_OK)
end
local ok, err = cacheWrapper.purgeCache()
for k, name in ipairs(res) do
if type(name) == 'string' then
-- Warmup the vhosts cache
local data, err = cacheWrapper.get(name, o2redis.getFromRedis, name)
-- Warmup the SSL cache
if type(data) == 'table' and (data[5] ~= nil or data[6] ~= nil) then
cacheWrapper.get(name .. '_crt', o2ssl.getCrt, name)
cacheWrapper.get(name .. '_key', o2ssl.getKey, name)
end
end
end
--o2debug.debug("Internal API : cache warmed up");
ngx.say(cjson.encode({
status = true,
reason = "OpenResty internal cache warmed'up!"
}))
local ok, err = cacheWrapper.executeInvalidation()
return ngx.exit(ngx.HTTP_OK)
end
--[[
Burst the cache for one specific domain
GET /internal?domain=toto.com&action=burst
--]]
if argAction == 'burst' then
local domain = o2utils.extractDomain(argDomain)
if type(domain) ~= 'string' then
ngx.say(cjson.encode({
status = false,
reason = "The name provided is not a string"
}))
return ngx.exit(ngx.HTTP_OK)
end
-- Vhost (proxy_pass rules etc...)
local ok, err = cacheWrapper.deleteFromCache(domain)
if err then
--o2debug.debug("Internal API : Cant purge for domain " .. domain);
ngx.say(cjson.encode({
status = false,
reason = "Can't purge the vhost cache for " .. domain .. ". Error received " .. err
}))
return ngx.exit(ngx.HTTP_OK)
end
-- SSL certificates
local ok, err = cacheWrapper.deleteFromCache(domain .. '_crt')
local ok, err = cacheWrapper.deleteFromCache(domain .. '_key')
local ok, err = cacheWrapper.deleteFromCache(domain .. '_ocsp')
--o2debug.debug("Internal API : Purge OK " .. domain);
ngx.say(cjson.encode({
status = true,
reason = "Nginx internal mlcache purged for " .. domain
}))
return ngx.exit(ngx.HTTP_OK)
end
--[[
Export data from Nginx internal memory, like the IP counter & domain counter
Careful : It's expensive as it used a get_keys() call that use a global lock for all workers !
GET /internal?action=export&type={ip,domain}&threshold=1000
--]]
if argAction == 'export' then
local result, err = nil, nil
if argType == 'ip' then
result, err = o2counter.exportDataFromCounter('i', function(v) return v >= argThreshold end)
elseif argType == 'domain' then
result, err = o2counter.exportDataFromCounter('d', function(v) return v >= argThreshold end)
elseif argType == 'ban' then
result, err = ban.exportBlacklistedIp()
end
if err ~= nil or type(result) ~= 'table' then
ngx.say(cjson.encode({
status = false,
reason = "Error " .. (err or 'no err msg')
}))
ngx.exit(ngx.HTTP_OK)
end
ngx.say(cjson.encode({
status = true,
result = result
}))
ngx.exit(ngx.HTTP_OK)
end
-- We should't arrive here.
ngx.say(cjson.encode({
status = false,
reason = "Unexpected error : we should not get here"
}))
return ngx.exit(ngx.HTTP_OK)
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists