mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Merge remote-tracking branch 'origin/dev' into ui
This commit is contained in:
commit
66fa2df6ce
76 changed files with 5503 additions and 949 deletions
15
.github/workflows/tests-ui-linux.yml
vendored
15
.github/workflows/tests-ui-linux.yml
vendored
|
|
@ -72,28 +72,21 @@ jobs:
|
|||
echo "127.0.0.1 www.example.com" | sudo tee -a /etc/hosts
|
||||
echo "127.0.0.1 app1.example.com" | sudo tee -a /etc/hosts
|
||||
# BunkerWeb
|
||||
echo "SERVER_NAME=www.example.com" | sudo tee /etc/bunkerweb/variables.env
|
||||
echo "SERVER_NAME=" | sudo tee /etc/bunkerweb/variables.env
|
||||
echo "HTTP_PORT=80" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "HTTPS_PORT=443" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo 'DNS_RESOLVERS=9.9.9.9 8.8.8.8 8.8.4.4' | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo 'API_LISTEN_IP=127.0.0.1' | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "MULTISITE=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "LOG_LEVEL=info" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "USE_BUNKERNET=no" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "USE_BLACKLIST=no" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "SEND_ANONYMOUS_REPORT=no" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "DISABLE_DEFAULT_SERVER=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "USE_CLIENT_CACHE=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "USE_GZIP=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "DATASTORE_MEMORY_SIZE=384m" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "www.example.com_USE_UI=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "www.example.com_SERVE_FILES=no" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "www.example.com_USE_REVERSE_PROXY=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "www.example.com_REVERSE_PROXY_URL=/admin" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "www.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:7000" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "www.example.com_INTERCEPTED_ERROR_CODES=400 405 413 429 500 501 502 503 504" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
|
||||
echo "ADMIN_USERNAME=admin" | sudo tee /etc/bunkerweb/ui.env
|
||||
echo "ADMIN_PASSWORD=S\$cr3tP@ssw0rd" | sudo tee -a /etc/bunkerweb/ui.env
|
||||
echo "UI_HOST=http://127.0.0.1:7000" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
sudo touch /etc/bunkerweb/ui.env
|
||||
|
||||
sudo chown nginx:nginx /etc/bunkerweb/variables.env /etc/bunkerweb/ui.env
|
||||
sudo chmod 777 /etc/bunkerweb/variables.env /etc/bunkerweb/ui.env
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@
|
|||
- [FEATURE] Add support for fallback Referrer-Policies
|
||||
- [FEATURE] Add profile page to web ui and the possibility to activate the 2FA
|
||||
- [FEATURE] Add setting REVERSE_PROXY_INCLUDES to manually add "include" directives in the reverse proxies
|
||||
- [FEATURE] Add support for Redis Sentinel
|
||||
- [FEATURE] Add support for tls in Ingress definition
|
||||
- [MISC] Fallback to default HTTPS certificate to prevent errors
|
||||
- [MISC] Various internal improvements in LUA code
|
||||
- [MISC] Updated Python Docker image to 3.12.1-alpine3.18 in Dockerfiles
|
||||
- [DEPS] Updated ModSecurity to v3.0.11
|
||||
|
||||
|
|
|
|||
|
|
@ -205,11 +205,13 @@ STREAM support :white_check_mark:
|
|||
|
||||
Choose custom certificate for HTTPS.
|
||||
|
||||
| Setting |Default| Context |Multiple| Description |
|
||||
|-----------------|-------|---------|--------|--------------------------------------------------------------------------------|
|
||||
|`USE_CUSTOM_SSL` |`no` |multisite|no |Use custom HTTPS certificate. |
|
||||
|`CUSTOM_SSL_CERT`| |multisite|no |Full path of the certificate or bundle file (must be readable by the scheduler).|
|
||||
|`CUSTOM_SSL_KEY` | |multisite|no |Full path of the key file (must be readable by the scheduler). |
|
||||
| Setting |Default| Context |Multiple| Description |
|
||||
|----------------------|-------|---------|--------|--------------------------------------------------------------------------------|
|
||||
|`USE_CUSTOM_SSL` |`no` |multisite|no |Use custom HTTPS certificate. |
|
||||
|`CUSTOM_SSL_CERT` | |multisite|no |Full path of the certificate or bundle file (must be readable by the scheduler).|
|
||||
|`CUSTOM_SSL_KEY` | |multisite|no |Full path of the key file (must be readable by the scheduler). |
|
||||
|`CUSTOM_SSL_CERT_DATA`| |multisite|no |Certificate data encoded in base64. |
|
||||
|`CUSTOM_SSL_KEY_DATA` | |multisite|no |Key data encoded in base64. |
|
||||
|
||||
### DB
|
||||
|
||||
|
|
@ -425,16 +427,22 @@ STREAM support :white_check_mark:
|
|||
|
||||
Redis server configuration when using BunkerWeb in cluster mode.
|
||||
|
||||
| Setting |Default|Context|Multiple| Description |
|
||||
|----------------------|-------|-------|--------|------------------------------------------------------------------|
|
||||
|`USE_REDIS` |`no` |global |no |Activate Redis. |
|
||||
|`REDIS_HOST` | |global |no |Redis server IP or hostname. |
|
||||
|`REDIS_PORT` |`6379` |global |no |Redis server port. |
|
||||
|`REDIS_DATABASE` |`0` |global |no |Redis database number. |
|
||||
|`REDIS_SSL` |`no` |global |no |Use SSL/TLS connection with Redis server. |
|
||||
|`REDIS_TIMEOUT` |`1000` |global |no |Redis server timeout (in ms) for connect, read and write. |
|
||||
|`REDIS_KEEPALIVE_IDLE`|`30000`|global |no |Max idle time (in ms) before closing redis connection in the pool.|
|
||||
|`REDIS_KEEPALIVE_POOL`|`10` |global |no |Max number of redis connection(s) kept in the pool. |
|
||||
| Setting |Default|Context|Multiple| Description |
|
||||
|-------------------------|-------|-------|--------|-------------------------------------------------------------------|
|
||||
|`USE_REDIS` |`no` |global |no |Activate Redis. |
|
||||
|`REDIS_HOST` | |global |no |Redis server IP or hostname. |
|
||||
|`REDIS_PORT` |`6379` |global |no |Redis server port. |
|
||||
|`REDIS_DATABASE` |`0` |global |no |Redis database number. |
|
||||
|`REDIS_SSL` |`no` |global |no |Use SSL/TLS connection with Redis server. |
|
||||
|`REDIS_TIMEOUT` |`1000` |global |no |Redis server timeout (in ms) for connect, read and write. |
|
||||
|`REDIS_KEEPALIVE_IDLE` |`30000`|global |no |Max idle time (in ms) before closing redis connection in the pool. |
|
||||
|`REDIS_KEEPALIVE_POOL` |`10` |global |no |Max number of redis connection(s) kept in the pool. |
|
||||
|`REDIS_USERNAME` | |global |no |Redis username used in AUTH command. |
|
||||
|`REDIS_PASSWORD` | |global |no |Redis password used in AUTH command. |
|
||||
|`REDIS_SENTINEL_HOSTS` | |global |no |Redis sentinel hosts with format host:[port] separated with spaces.|
|
||||
|`REDIS_SENTINEL_USERNAME`| |global |no |Redis sentinel username. |
|
||||
|`REDIS_SENTINEL_PASSWORD`| |global |no |Redis sentinel password. |
|
||||
|`REDIS_SENTINEL_MASTER` | |global |no |Redis sentinel master name. |
|
||||
|
||||
### Reverse proxy
|
||||
|
||||
|
|
@ -469,6 +477,7 @@ Manage reverse proxy configurations.
|
|||
|`REVERSE_PROXY_CONNECT_TIMEOUT` |`60s` |multisite|yes |Timeout when connecting to the proxied resource. |
|
||||
|`REVERSE_PROXY_READ_TIMEOUT` |`60s` |multisite|yes |Timeout when reading from the proxied resource. |
|
||||
|`REVERSE_PROXY_SEND_TIMEOUT` |`60s` |multisite|yes |Timeout when sending to the proxied resource. |
|
||||
|`REVERSE_PROXY_INCLUDES` | |multisite|yes |Additional configuration to include in the location block, separated with spaces. |
|
||||
|
||||
### Reverse scan
|
||||
|
||||
|
|
@ -541,3 +550,4 @@ Allow access based on internal and external IP/network/rDNS/ASN whitelists.
|
|||
|`WHITELIST_USER_AGENT_URLS`| |global |no |List of URLs, separated with spaces, containing good User-Agent to whitelist. |
|
||||
|`WHITELIST_URI` | |multisite|no |List of URI (PCRE regex), separated with spaces, to whitelist. |
|
||||
|`WHITELIST_URI_URLS` | |global |no |List of URLs, separated with spaces, containing bad URI to whitelist. |
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,52 @@
|
|||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local process = require "ngx.process"
|
||||
local rsignal = require "resty.signal"
|
||||
local upload = require "resty.upload"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
|
||||
local api = class("api")
|
||||
|
||||
local datastore = cdatastore:new()
|
||||
local logger = clogger:new("API")
|
||||
|
||||
local get_variable = utils.get_variable
|
||||
local is_ip_in_networks = utils.is_ip_in_networks
|
||||
-- local run = shell.run
|
||||
local NOTICE = ngx.NOTICE
|
||||
local ERR = ngx.ERR
|
||||
local HTTP_OK = ngx.HTTP_OK
|
||||
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
|
||||
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
|
||||
local HTTP_NOT_FOUND = ngx.HTTP_NOT_FOUND
|
||||
local kill = rsignal.kill
|
||||
local get_master_pid = process.get_master_pid
|
||||
local execute = os.execute
|
||||
local open = io.open
|
||||
local read_body = ngx_req.read_body
|
||||
local get_body_data = ngx_req.get_body_data
|
||||
local get_body_file = ngx_req.get_body_file
|
||||
local decode = cjson.decode
|
||||
local encode = cjson.encode
|
||||
local floor = math.floor
|
||||
local match = string.match
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
|
||||
api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
|
||||
|
||||
function api:initialize()
|
||||
self.datastore = datastore:new()
|
||||
self.logger = logger:new("API")
|
||||
self.ctx = ngx.ctx
|
||||
local data, err = utils.get_variable("API_WHITELIST_IP", false)
|
||||
function api:initialize(ctx)
|
||||
self.ctx = ctx
|
||||
local data, err = get_variable("API_WHITELIST_IP", false)
|
||||
self.ips = {}
|
||||
if not data then
|
||||
self.logger.log(ngx.ERR, "can't get API_WHITELIST_IP variable : " .. err)
|
||||
logger:log(ERR, "can't get API_WHITELIST_IP variable : " .. err)
|
||||
else
|
||||
for ip in data:gmatch("%S+") do
|
||||
table.insert(self.ips, ip)
|
||||
|
|
@ -28,23 +56,23 @@ end
|
|||
|
||||
-- luacheck: ignore 212
|
||||
function api:log_cmd(cmd, status, stdout, stderr)
|
||||
local level = ngx.NOTICE
|
||||
local level = NOTICE
|
||||
local prefix = "success"
|
||||
if status ~= 0 then
|
||||
level = ngx.ERR
|
||||
level = ERR
|
||||
prefix = "error"
|
||||
end
|
||||
self.logger:log(level, prefix .. " while running command " .. cmd)
|
||||
self.logger:log(level, "stdout = " .. stdout)
|
||||
self.logger:log(level, "stdout = " .. stderr)
|
||||
logger:log(level, prefix .. " while running command " .. cmd)
|
||||
logger:log(level, "stdout = " .. stdout)
|
||||
logger:log(level, "stdout = " .. stderr)
|
||||
end
|
||||
|
||||
-- TODO : use this if we switch to OpenResty
|
||||
function api:cmd(cmd)
|
||||
-- Non-blocking command
|
||||
-- luacheck: ignore 113
|
||||
local ok, stdout, stderr, reason, status = shell.run(cmd, nil, 10000)
|
||||
self.logger:log_cmd(cmd, status, stdout, stderr)
|
||||
local ok, stdout, stderr, reason, status = run(cmd, nil, 10000)
|
||||
self:log_cmd(cmd, status, stdout, stderr)
|
||||
-- Timeout
|
||||
if ok == nil then
|
||||
return nil, reason
|
||||
|
|
@ -62,25 +90,30 @@ function api:response(http_status, api_status, msg)
|
|||
end
|
||||
|
||||
api.global.GET["^/ping$"] = function(self)
|
||||
return self:response(ngx.HTTP_OK, "success", "pong")
|
||||
return self:response(HTTP_OK, "success", "pong")
|
||||
end
|
||||
|
||||
api.global.POST["^/reload$"] = function(self)
|
||||
-- Send HUP signal to master process
|
||||
local ok, err = rsignal.kill(process.get_master_pid(), "HUP")
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
-- Check config
|
||||
local status = execute("nginx -t")
|
||||
if status ~= 0 then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "config check failed")
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", "reload successful")
|
||||
-- Send HUP signal to master process
|
||||
local ok, err = kill(get_master_pid(), "HUP")
|
||||
if not ok then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
end
|
||||
return self:response(HTTP_OK, "success", "reload successful")
|
||||
end
|
||||
|
||||
api.global.POST["^/stop$"] = function(self)
|
||||
-- Send QUIT signal to master process
|
||||
local ok, err = rsignal.kill(process.get_master_pid(), "QUIT")
|
||||
local ok, err = kill(get_master_pid(), "QUIT")
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", "stop successful")
|
||||
return self:response(HTTP_OK, "success", "stop successful")
|
||||
end
|
||||
|
||||
api.global.POST["^/confs$"] = function(self)
|
||||
|
|
@ -99,16 +132,19 @@ api.global.POST["^/confs$"] = function(self)
|
|||
end
|
||||
local form, err = upload:new(4096)
|
||||
if not form then
|
||||
return self:response(ngx.HTTP_BAD_REQUEST, "error", err)
|
||||
return self:response(HTTP_BAD_REQUEST, "error", err)
|
||||
end
|
||||
form:set_timeout(1000)
|
||||
local file = io.open(tmp, "w+")
|
||||
local file, err = open(tmp, "w+")
|
||||
if not file then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", err)
|
||||
end
|
||||
while true do
|
||||
-- luacheck: ignore 421
|
||||
local typ, res, err = form:read()
|
||||
if not typ then
|
||||
file:close()
|
||||
return self:response(ngx.HTTP_BAD_REQUEST, "error", err)
|
||||
return self:response(HTTP_BAD_REQUEST, "error", err)
|
||||
end
|
||||
if typ == "eof" then
|
||||
break
|
||||
|
|
@ -124,12 +160,12 @@ api.global.POST["^/confs$"] = function(self)
|
|||
"tar xzf " .. tmp .. " -C " .. destination,
|
||||
}
|
||||
for _, cmd in ipairs(cmds) do
|
||||
local status = os.execute(cmd)
|
||||
local status = execute(cmd)
|
||||
if status ~= 0 then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||
end
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", "saved data at " .. destination)
|
||||
return self:response(HTTP_OK, "success", "saved data at " .. destination)
|
||||
end
|
||||
|
||||
api.global.POST["^/data$"] = api.global.POST["^/confs$"]
|
||||
|
|
@ -141,80 +177,86 @@ api.global.POST["^/custom_configs$"] = api.global.POST["^/confs$"]
|
|||
api.global.POST["^/plugins$"] = api.global.POST["^/confs$"]
|
||||
|
||||
api.global.POST["^/unban$"] = function(self)
|
||||
ngx.req.read_body()
|
||||
local data = ngx.req.get_body_data()
|
||||
read_body()
|
||||
local data = get_body_data()
|
||||
if not data then
|
||||
local data_file = ngx.req.get_body_file()
|
||||
local data_file = get_body_file()
|
||||
if data_file then
|
||||
local file = io.open(data_file)
|
||||
local file, err = open(data_file)
|
||||
if not file then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", err)
|
||||
end
|
||||
data = file:read("*a")
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
local ok, ip = pcall(cjson.decode, data)
|
||||
local ok, ip = pcall(decode, data)
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
end
|
||||
self.datastore:delete("bans_ip_" .. ip["ip"])
|
||||
return self:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " unbanned")
|
||||
datastore:delete("bans_ip_" .. ip["ip"])
|
||||
return self:response(HTTP_OK, "success", "ip " .. ip["ip"] .. " unbanned")
|
||||
end
|
||||
|
||||
api.global.POST["^/ban$"] = function(self)
|
||||
ngx.req.read_body()
|
||||
local data = ngx.req.get_body_data()
|
||||
read_body()
|
||||
local data = get_body_data()
|
||||
if not data then
|
||||
local data_file = ngx.req.get_body_file()
|
||||
local data_file = get_body_file()
|
||||
if data_file then
|
||||
local file = io.open(data_file)
|
||||
local file, err = io.open(data_file)
|
||||
if not file then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", err)
|
||||
end
|
||||
data = file:read("*a")
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
local ok, ip = pcall(cjson.decode, data)
|
||||
local ok, ip = pcall(decode, data)
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
end
|
||||
self.datastore:set("bans_ip_" .. ip["ip"], "manual", ip["exp"])
|
||||
return self:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " banned")
|
||||
datastore:set("bans_ip_" .. ip["ip"], "manual", ip["exp"])
|
||||
return self:response(HTTP_OK, "success", "ip " .. ip["ip"] .. " banned")
|
||||
end
|
||||
|
||||
api.global.GET["^/bans$"] = function(self)
|
||||
local data = {}
|
||||
for _, k in ipairs(self.datastore:keys()) do
|
||||
for _, k in ipairs(datastore:keys()) do
|
||||
if k:find("^bans_ip_") then
|
||||
local reason, err = self.datastore:get(k)
|
||||
local reason, err = datastore:get(k)
|
||||
if err then
|
||||
return self:response(
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
HTTP_INTERNAL_SERVER_ERROR,
|
||||
"error",
|
||||
"can't access " .. k .. " from datastore : " .. reason
|
||||
)
|
||||
end
|
||||
local ok, ttl = self.datastore:ttl(k)
|
||||
local ok, ttl = datastore:ttl(k)
|
||||
if not ok then
|
||||
return self:response(
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
HTTP_INTERNAL_SERVER_ERROR,
|
||||
"error",
|
||||
"can't access ttl " .. k .. " from datastore : " .. ttl
|
||||
)
|
||||
end
|
||||
local ban = { ip = k:sub(9, #k), reason = reason, exp = math.floor(ttl) }
|
||||
local ban = { ip = k:sub(9, #k), reason = reason, exp = floor(ttl) }
|
||||
table.insert(data, ban)
|
||||
end
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", data)
|
||||
return self:response(HTTP_OK, "success", data)
|
||||
end
|
||||
|
||||
api.global.GET["^/variables$"] = function(self)
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't access variables from datastore : " .. err)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't access variables from datastore : " .. err)
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", variables)
|
||||
return self:response(HTTP_OK, "success", variables)
|
||||
end
|
||||
|
||||
function api:is_allowed_ip()
|
||||
if utils.is_ip_in_networks(self.ctx.bw.remote_addr, self.ips) then
|
||||
if is_ip_in_networks(self.ctx.bw.remote_addr, self.ips) then
|
||||
return true, "ok"
|
||||
end
|
||||
return false, "IP is not in API_WHITELIST_IP"
|
||||
|
|
@ -223,10 +265,10 @@ end
|
|||
function api:do_api_call()
|
||||
if self.global[self.ctx.bw.request_method] ~= nil then
|
||||
for uri, api_fun in pairs(self.global[self.ctx.bw.request_method]) do
|
||||
if string.match(self.ctx.bw.uri, uri) then
|
||||
if match(self.ctx.bw.uri, uri) then
|
||||
local status, resp = api_fun(self)
|
||||
local ret = true
|
||||
if status ~= ngx.HTTP_OK then
|
||||
if status ~= HTTP_OK then
|
||||
ret = false
|
||||
end
|
||||
if #resp["msg"] == 0 then
|
||||
|
|
@ -235,26 +277,36 @@ function api:do_api_call()
|
|||
resp["data"] = resp["msg"]
|
||||
resp["msg"] = resp["status"]
|
||||
end
|
||||
return ret, resp["msg"], status, cjson.encode(resp)
|
||||
return ret, resp["msg"], status, encode(resp)
|
||||
end
|
||||
end
|
||||
end
|
||||
local list, err = self.datastore:get("plugins", true)
|
||||
local list, err = datastore:get("plugins", true)
|
||||
if not list then
|
||||
local _, resp = self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't list loaded plugins : " .. err)
|
||||
return false, resp["msg"], ngx.HTTP_INTERNAL_SERVER_ERROR, cjson.encode(resp)
|
||||
local _, resp = self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't list loaded plugins : " .. err)
|
||||
return false, resp["msg"], HTTP_INTERNAL_SERVER_ERROR, encode(resp)
|
||||
end
|
||||
for _, plugin in ipairs(list) do
|
||||
if pcall(require, plugin.id .. "/" .. plugin.id) then
|
||||
local plugin_lua = require(plugin.id .. "/" .. plugin.id)
|
||||
if plugin_lua.api ~= nil then
|
||||
local matched, status, resp = plugin_lua:api(self.ctx)
|
||||
if matched then
|
||||
local ret = true
|
||||
if status ~= ngx.HTTP_OK then
|
||||
ret = false
|
||||
local plugin_lua, err = require_plugin(plugin.id)
|
||||
if plugin_lua and plugin_lua.api ~= nil then
|
||||
local ok, plugin_obj = new_plugin(plugin_lua, self.ctx)
|
||||
if not ok then
|
||||
logger:log(ERR, "can't instantiate " .. plugin.id .. " : " .. plugin_obj)
|
||||
else
|
||||
local ok, ret = call_plugin(plugin_obj, "api")
|
||||
if not ok then
|
||||
logger:log(ERR, "error while executing " .. plugin.id .. ":api() : " .. ret)
|
||||
else
|
||||
if ret.ret then
|
||||
local resp = {}
|
||||
if ret.status == HTTP_OK then
|
||||
resp["status"] = "success"
|
||||
else
|
||||
resp["status"] = "error"
|
||||
end
|
||||
resp["msg"] = ret.msg
|
||||
return ret.status == HTTP_OK, resp["status"], ret.status, encode(resp)
|
||||
end
|
||||
return ret, resp["msg"], status, cjson.encode(resp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -262,7 +314,7 @@ function api:do_api_call()
|
|||
local resp = {}
|
||||
resp["status"] = "error"
|
||||
resp["msg"] = "not found"
|
||||
return false, "error", ngx.HTTP_NOT_FOUND, cjson.encode(resp)
|
||||
return false, "error", HTTP_NOT_FOUND, encode(resp)
|
||||
end
|
||||
|
||||
return api
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
local ngx = ngx
|
||||
local class = require "middleclass"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local mlcache = require "resty.mlcache"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = class("cachestore")
|
||||
|
||||
local logger = clogger:new("CACHESTORE")
|
||||
|
||||
local subsystem = ngx.config.subsystem
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local null = ngx.null
|
||||
local get_ctx_obj = utils.get_ctx_obj
|
||||
local is_cosocket_available = utils.is_cosocket_available
|
||||
|
||||
-- Instantiate mlcache object at module level (which will be cached when running init phase)
|
||||
-- TODO : custom settings
|
||||
local shm = "cachestore"
|
||||
local ipc_shm = "cachestore_ipc"
|
||||
local shm_miss = "cachestore_miss"
|
||||
local shm_locks = "cachestore_locks"
|
||||
if not ngx.shared.cachestore then
|
||||
if subsystem == "stream" then
|
||||
shm = "cachestore_stream"
|
||||
ipc_shm = "cachestore_ipc_stream"
|
||||
shm_miss = "cachestore_miss_stream"
|
||||
|
|
@ -33,22 +43,18 @@ local cache, err = mlcache.new("cachestore", shm, {
|
|||
},
|
||||
ipc_shm = ipc_shm,
|
||||
})
|
||||
local module_logger = logger:new("CACHESTORE")
|
||||
if not cache then
|
||||
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
|
||||
logger:log(ERR, "can't instantiate mlcache : " .. err)
|
||||
end
|
||||
|
||||
function cachestore:initialize(use_redis, new_cs, ctx)
|
||||
self.ctx = ctx
|
||||
self.cache = cache
|
||||
function cachestore:initialize(use_redis, ctx, pool)
|
||||
self.use_redis = use_redis or false
|
||||
self.logger = module_logger
|
||||
if new_cs then
|
||||
self.clusterstore = clusterstore:new(false)
|
||||
self.shared_cs = false
|
||||
else
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore", self.ctx)
|
||||
self.shared_cs = true
|
||||
if self.use_redis then
|
||||
if ctx then
|
||||
self.clusterstore = get_ctx_obj("clusterstore", ctx)
|
||||
else
|
||||
self.clusterstore = clusterstore:new(pool)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -57,8 +63,7 @@ function cachestore:get(key)
|
|||
local callback = function(key, cs)
|
||||
-- Connect to redis
|
||||
-- luacheck: ignore 431
|
||||
local clusterstore = cs or require "bunkerweb.clusterstore":new(false)
|
||||
local ok, err, _ = clusterstore:connect()
|
||||
local ok, err, _ = cs:connect()
|
||||
if not ok then
|
||||
return nil, "can't connect to redis : " .. err, nil
|
||||
end
|
||||
|
|
@ -76,14 +81,14 @@ function cachestore:get(key)
|
|||
end
|
||||
return {ret_get, ret_ttl}
|
||||
]]
|
||||
local ret, err = clusterstore:call("eval", redis_script, 1, key)
|
||||
local ret, err = cs:call("eval", redis_script, 1, key)
|
||||
if not ret then
|
||||
clusterstore:close()
|
||||
cs:close()
|
||||
return nil, err, nil
|
||||
end
|
||||
-- Extract values
|
||||
clusterstore:close()
|
||||
if ret[1] == ngx.null then
|
||||
cs:close()
|
||||
if ret[1] == null then
|
||||
ret[1] = nil
|
||||
ret[2] = -1
|
||||
elseif ret[2] < 0 then
|
||||
|
|
@ -96,35 +101,31 @@ function cachestore:get(key)
|
|||
end
|
||||
-- luacheck: ignore 431
|
||||
local value, err, hit_level
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local cs = nil
|
||||
if self.shared_cs then
|
||||
cs = self.clusterstore
|
||||
end
|
||||
value, err, hit_level = self.cache:get(key, nil, callback, key, cs)
|
||||
if self.use_redis and is_cosocket_available() then
|
||||
value, err, hit_level = cache:get(key, nil, callback, key, self.clusterstore)
|
||||
else
|
||||
value, err, hit_level = self.cache:get(key, nil, callback_no_miss)
|
||||
value, err, hit_level = cache:get(key, nil, callback_no_miss)
|
||||
end
|
||||
if value == nil and err ~= nil then
|
||||
return false, err
|
||||
end
|
||||
self.logger:log(ngx.INFO, "hit level for " .. key .. " = " .. tostring(hit_level))
|
||||
logger:log(INFO, "hit level for " .. key .. " = " .. tostring(hit_level))
|
||||
return true, value
|
||||
end
|
||||
|
||||
function cachestore:set(key, value, ex)
|
||||
-- luacheck: ignore 431
|
||||
local ok, err
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
if self.use_redis and is_cosocket_available() then
|
||||
ok, err = self:set_redis(key, value, ex)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
end
|
||||
end
|
||||
if ex then
|
||||
ok, err = self.cache:set(key, { ttl = ex }, value)
|
||||
ok, err = cache:set(key, { ttl = ex }, value)
|
||||
else
|
||||
ok, err = self.cache:set(key, nil, value)
|
||||
ok, err = cache:set(key, nil, value)
|
||||
end
|
||||
if not ok then
|
||||
return false, err
|
||||
|
|
@ -153,13 +154,13 @@ end
|
|||
function cachestore:delete(key)
|
||||
-- luacheck: ignore 431
|
||||
local ok, err
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
if self.use_redis and is_cosocket_available() then
|
||||
ok, err = self:del_redis(key)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
end
|
||||
end
|
||||
ok, err = self.cache:delete(key)
|
||||
ok, err = cache:delete(key)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
|
|
@ -184,7 +185,11 @@ function cachestore:del_redis(key)
|
|||
end
|
||||
|
||||
function cachestore:purge()
|
||||
return self.cache:purge(true)
|
||||
return cache:purge(true)
|
||||
end
|
||||
|
||||
function cachestore:update()
|
||||
return cache:update()
|
||||
end
|
||||
|
||||
return cachestore
|
||||
|
|
|
|||
|
|
@ -1,15 +1,24 @@
|
|||
local ngx = ngx
|
||||
local class = require "middleclass"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local redis = require "resty.redis"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local rc = require "resty.redis.connector"
|
||||
local rs = require("resty.redis.sentinel")
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local clusterstore = class("clusterstore")
|
||||
|
||||
local logger = clogger:new("CLUSTERSTORE")
|
||||
|
||||
local get_variable = utils.get_variable
|
||||
local is_cosocket_available = utils.is_cosocket_available
|
||||
local ERR = ngx.ERR
|
||||
local tonumber = tonumber
|
||||
local random = math.random
|
||||
|
||||
function clusterstore:initialize(pool)
|
||||
-- Instantiate logger
|
||||
self.logger = logger:new("CLUSTERSTORE")
|
||||
-- Get variables
|
||||
local variables = {
|
||||
["USE_REDIS"] = "",
|
||||
["REDIS_HOST"] = "",
|
||||
["REDIS_PORT"] = "",
|
||||
["REDIS_DATABASE"] = "",
|
||||
|
|
@ -17,98 +26,150 @@ function clusterstore:initialize(pool)
|
|||
["REDIS_TIMEOUT"] = "",
|
||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = "",
|
||||
["REDIS_USERNAME"] = "",
|
||||
["REDIS_PASSWORD"] = "",
|
||||
["REDIS_SENTINEL_HOSTS"] = "",
|
||||
["REDIS_SENTINEL_USERNAME"] = "",
|
||||
["REDIS_SENTINEL_PASSWORD"] = "",
|
||||
["REDIS_SENTINEL_MASTER"] = ""
|
||||
}
|
||||
-- Set them for later user
|
||||
-- Set them for later use
|
||||
self.variables = {}
|
||||
for k, _ in pairs(variables) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
local value, err = get_variable(k, false)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
end
|
||||
self.variables[k] = value
|
||||
end
|
||||
-- Don't instantiate a redis object for now
|
||||
self.redis_client = nil
|
||||
self.pool = pool == nil or pool
|
||||
end
|
||||
|
||||
function clusterstore:connect()
|
||||
-- Check if we are already connected
|
||||
if self.redis_client then
|
||||
return true, "already connected", self.redis_client:get_reused_times()
|
||||
-- Don't go further if redis is not used
|
||||
if self.variables["USE_REDIS"] ~= "yes" then
|
||||
return
|
||||
end
|
||||
-- Instantiate object
|
||||
local redis_client, err = redis:new()
|
||||
if redis_client == nil then
|
||||
return false, err
|
||||
end
|
||||
-- Set timeouts
|
||||
redis_client:set_timeout(tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||
-- Connect
|
||||
-- Compute options
|
||||
local options = {
|
||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||
connect_timeout = tonumber(self.variables["REDIS_TIMEOUT"]),
|
||||
read_timeout = tonumber(self.variables["REDIS_TIMEOUT"]),
|
||||
send_timeout = tonumber(self.variables["REDIS_TIMEOUT"]),
|
||||
keepalive_timeout = tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]),
|
||||
keepalive_poolsize = tonumber(self.variables["REDIS_KEEPALIVE_POOL"]),
|
||||
connection_options = {
|
||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||
|
||||
},
|
||||
host = self.variables["REDIS_HOST"],
|
||||
port = tonumber(self.variables["REDIS_PORT"]),
|
||||
db = tonumber(self.variables["REDIS_DATABASE"]),
|
||||
username = self.variables["REDIS_USERNAME"],
|
||||
password = self.variables["REDIS_PASSWORD"],
|
||||
sentinel_username = self.variables["REDIS_SENTINEL_USERNAME"],
|
||||
sentinel_password = self.variables["REDIS_SENTINEL_PASSWORD"],
|
||||
master_name = self.variables["REDIS_SENTINEL_MASTER"],
|
||||
role = "master",
|
||||
sentinels = {}
|
||||
}
|
||||
if self.pool then
|
||||
options.pool = "bw-redis"
|
||||
options.pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
if pool == nil or pool then
|
||||
options.connection_options.pool = "bw-redis"
|
||||
options.connection_options.pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
end
|
||||
local ok, err = redis_client:connect(self.variables["REDIS_HOST"], tonumber(self.variables["REDIS_PORT"]), options)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
self.redis_client = redis_client
|
||||
-- Select database if needed
|
||||
local times, err = self.redis_client:get_reused_times()
|
||||
if err then
|
||||
self:close()
|
||||
return false, err
|
||||
end
|
||||
if times == 0 then
|
||||
-- luacheck: ignore 421
|
||||
local _, err = self.redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
||||
if err then
|
||||
self:close()
|
||||
return false, err
|
||||
if self.variables["REDIS_SENTINEL_HOSTS"] ~= "" then
|
||||
for sentinel_host in self.variables["REDIS_SENTINEL_HOSTS"]:gmatch("%S+") do
|
||||
local shost, sport = sentinel_host:match("([^:]+):?(%d*)")
|
||||
if sport == "" then
|
||||
sport = 26379
|
||||
else
|
||||
sport = tonumber(sport)
|
||||
end
|
||||
table.insert(options.sentinel, {host = shost, port = sport})
|
||||
end
|
||||
end
|
||||
return true, "success", times
|
||||
self.options = options
|
||||
-- Instantiate object
|
||||
if is_cosocket_available() then
|
||||
local redis_connector, err = rc.new(self.options)
|
||||
self.redis_connector = redis_connector
|
||||
if self.redis_connector == nil then
|
||||
logger:log(ERR, "can't instantiate redis object : " .. err)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function clusterstore:connect(readonly)
|
||||
-- Check if connector is created
|
||||
if not self.redis_connector then
|
||||
return false, "connector is not instantiated"
|
||||
end
|
||||
-- Disconnect if needed
|
||||
if self.redis_client then
|
||||
self:close()
|
||||
end
|
||||
-- Connect to sentinels if needed
|
||||
local redis_client, err
|
||||
if #self.options.sentinels > 0 then
|
||||
local redis_sentinel, err = self.redis_connector:connect()
|
||||
if not redis_sentinel then
|
||||
return false, "error while connecting to sentinels : " .. err
|
||||
end
|
||||
if readonly then
|
||||
redis_clients, err = rs.get_slaves(redis_sentinel, self.options.master_name)
|
||||
if redis_clients then
|
||||
redis_client = redis_clients[random(#redis_clients)]
|
||||
else
|
||||
redis_client = nil
|
||||
end
|
||||
else
|
||||
redis_client, err = rs.get_master(redis_sentinel, self.options.master_name)
|
||||
end
|
||||
-- Classic connection
|
||||
else
|
||||
redis_client, err = self.redis_connector:connect()
|
||||
end
|
||||
self.redis_client = redis_client
|
||||
if not self.redis_client then
|
||||
return false, "error while getting redis client : " .. err
|
||||
end
|
||||
-- Everything went well
|
||||
return true, "success", self.redis_client:get_reused_times()
|
||||
end
|
||||
|
||||
function clusterstore:close()
|
||||
if self.redis_client then
|
||||
-- Equivalent to close but keep a pool of connections
|
||||
if self.pool then
|
||||
local ok, err = self.redis_client:set_keepalive(
|
||||
tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]),
|
||||
tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
)
|
||||
self.redis_client = nil
|
||||
if not ok then
|
||||
require("bunkerweb.logger"):new("clusterstore-close"):log(ngx.ERR, err)
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
-- Close
|
||||
local ok, err = self.redis_client:close()
|
||||
self.redis_client.redis_client = nil
|
||||
return ok, err
|
||||
-- Check if connected is created
|
||||
if not self.redis_connector then
|
||||
return false, "connector is not instantiated"
|
||||
end
|
||||
return false, "not connected"
|
||||
-- Check if client is created
|
||||
if not self.redis_client then
|
||||
return false, "client is not instantiated"
|
||||
end
|
||||
-- Pool case
|
||||
local ok, err
|
||||
if self.pool then
|
||||
ok, err = self.redis_connector:set_keepalive(self.redis_client)
|
||||
-- No pool
|
||||
else
|
||||
ok, err = self.redis_client:close()
|
||||
end
|
||||
self.redis_client = nil
|
||||
if err then
|
||||
logger:log(ERR, "error while closing redis_client : " .. err)
|
||||
end
|
||||
return ok ~= nil, err
|
||||
end
|
||||
|
||||
function clusterstore:call(method, ...)
|
||||
-- Check if we are connected
|
||||
-- Check if client is created
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
return false, "client is not instantiated"
|
||||
end
|
||||
-- Call method
|
||||
return self.redis_client[method](self.redis_client, ...)
|
||||
end
|
||||
|
||||
function clusterstore:multi(calls)
|
||||
-- Check if we are connected
|
||||
-- Check if client is created
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
return false, "client is not instantiated"
|
||||
end
|
||||
-- Start transaction
|
||||
local ok, err = self.redis_client:multi()
|
||||
|
|
@ -121,7 +182,7 @@ function clusterstore:multi(calls)
|
|||
local args = unpack(call[2])
|
||||
ok, err = self.redis_client[method](self.redis_client, args)
|
||||
if not ok then
|
||||
return false, method + "() failed : " .. err
|
||||
return false, method .. "() failed : " .. err
|
||||
end
|
||||
end
|
||||
-- Exec transaction
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
local ngx = ngx
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local lrucache = require "resty.lrucache"
|
||||
local datastore = class("datastore")
|
||||
|
||||
local lru, err = lrucache.new(100000)
|
||||
local logger = clogger:new("DATASTORE")
|
||||
|
||||
local ERR = ngx.ERR
|
||||
local subsystem = ngx.config.subsystem
|
||||
local shared = ngx.shared
|
||||
|
||||
local lru, err_lru = lrucache.new(100000)
|
||||
if not lru then
|
||||
require "bunkerweb.logger"
|
||||
:new("DATASTORE")
|
||||
:log(ngx.ERR, "failed to instantiate LRU cache : " .. (err or "unknown error"))
|
||||
logger:log(ERR, "failed to instantiate LRU cache : " .. err_lru)
|
||||
end
|
||||
|
||||
function datastore:initialize()
|
||||
self.dict = ngx.shared.datastore
|
||||
if not self.dict then
|
||||
self.dict = ngx.shared.datastore_stream
|
||||
if subsystem == "http" then
|
||||
self.dict = shared.datastore
|
||||
else
|
||||
self.dict = shared.datastore_stream
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -20,6 +27,9 @@ function datastore:get(key, worker)
|
|||
-- luacheck: ignore 431
|
||||
local value, err
|
||||
if worker then
|
||||
if not lru then
|
||||
return nil, "lru is not instantiated"
|
||||
end
|
||||
value, err = lru:get(key)
|
||||
return value, err or "not found"
|
||||
end
|
||||
|
|
@ -32,6 +42,9 @@ end
|
|||
|
||||
function datastore:set(key, value, exptime, worker)
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
lru:set(key, value, exptime)
|
||||
return true, "success"
|
||||
end
|
||||
|
|
@ -41,6 +54,9 @@ end
|
|||
|
||||
function datastore:delete(key, worker)
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
lru:delete(key)
|
||||
return true, "success"
|
||||
end
|
||||
|
|
@ -50,6 +66,9 @@ end
|
|||
|
||||
function datastore:keys(worker)
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
return lru:keys(0)
|
||||
end
|
||||
return self.dict:get_keys(0)
|
||||
|
|
@ -70,6 +89,9 @@ end
|
|||
function datastore:delete_all(pattern, worker)
|
||||
local keys
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
keys = lru:keys(0)
|
||||
else
|
||||
keys = self.dict:get_keys(0)
|
||||
|
|
@ -84,6 +106,9 @@ end
|
|||
|
||||
-- luacheck: ignore 212
|
||||
function datastore:flush_lru()
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
lru:flush_all()
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,35 @@
|
|||
local ngx = ngx
|
||||
local cjson = require "cjson"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local bwctx = require "bunkerweb.ctx"
|
||||
local base = require "resty.core.base"
|
||||
|
||||
local open = io.open
|
||||
local decode = cjson.decode
|
||||
local encode = cjson.encode
|
||||
local tostring = tostring
|
||||
local get_phases = utils.get_phases
|
||||
local get_request = base.get_request
|
||||
local apply_ref = bwctx.apply_ref
|
||||
local stash_ref = bwctx.stash_ref
|
||||
local subsystem = ngx.config.subsystem
|
||||
local var = ngx.var
|
||||
local req = ngx.req
|
||||
local ip_is_global = utils.ip_is_global
|
||||
local is_ipv4 = utils.is_ipv4
|
||||
local is_ipv6 = utils.is_ipv6
|
||||
local get_variable = utils.get_variable
|
||||
|
||||
local helpers = {}
|
||||
|
||||
helpers.load_plugin = function(json)
|
||||
-- Open file
|
||||
local file, err, nb = io.open(json, "r")
|
||||
local file, err, nb = open(json, "r")
|
||||
if not file then
|
||||
return false, "can't load JSON at " .. json .. " : " .. err .. " (nb = " .. tostring(nb) .. ")"
|
||||
end
|
||||
-- Decode JSON
|
||||
local ok, plugin = pcall(cjson.decode, file:read("*a"))
|
||||
local ok, plugin = pcall(decode, file:read("*a"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid JSON at " .. json .. " : " .. err
|
||||
|
|
@ -26,7 +43,7 @@ helpers.load_plugin = function(json)
|
|||
end
|
||||
end
|
||||
if #missing_fields > 0 then
|
||||
return false, "missing field(s) " .. cjson.encode(missing_fields) .. " for JSON at " .. json
|
||||
return false, "missing field(s) " .. encode(missing_fields) .. " for JSON at " .. json
|
||||
end
|
||||
-- Try require
|
||||
local plugin_lua, err = helpers.require_plugin(plugin.id)
|
||||
|
|
@ -34,7 +51,7 @@ helpers.load_plugin = function(json)
|
|||
return false, err
|
||||
end
|
||||
-- Fill phases
|
||||
local phases = utils.get_phases()
|
||||
local phases = get_phases()
|
||||
plugin.phases = {}
|
||||
if plugin_lua then
|
||||
for _, phase in ipairs(phases) do
|
||||
|
|
@ -49,11 +66,11 @@ end
|
|||
|
||||
helpers.order_plugins = function(plugins)
|
||||
-- Extract orders
|
||||
local file, err, nb = io.open("/usr/share/bunkerweb/core/order.json", "r")
|
||||
local file, err, nb = open("/usr/share/bunkerweb/core/order.json", "r")
|
||||
if not file then
|
||||
return false, err .. " (nb = " .. tostring(nb) .. ")"
|
||||
end
|
||||
local ok, orders = pcall(cjson.decode, file:read("*a"))
|
||||
local ok, orders = pcall(decode, file:read("*a"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid order.json : " .. err
|
||||
|
|
@ -68,7 +85,7 @@ helpers.order_plugins = function(plugins)
|
|||
end
|
||||
-- Order result
|
||||
local result_orders = {}
|
||||
for _, phase in ipairs(utils.get_phases()) do
|
||||
for _, phase in ipairs(get_phases()) do
|
||||
result_orders[phase] = {}
|
||||
end
|
||||
-- Fill order first
|
||||
|
|
@ -82,7 +99,7 @@ helpers.order_plugins = function(plugins)
|
|||
end
|
||||
end
|
||||
-- Then append missing plugins to the end
|
||||
for _, phase in ipairs(utils.get_phases()) do
|
||||
for _, phase in ipairs(get_phases()) do
|
||||
for id, plugin in pairs(plugins_phases) do
|
||||
if plugin[phase] then
|
||||
table.insert(result_orders[phase], id)
|
||||
|
|
@ -141,7 +158,7 @@ helpers.call_plugin = function(plugin, method)
|
|||
end
|
||||
end
|
||||
if #missing_values > 0 then
|
||||
return false, "missing required return value(s) : " .. cjson.encode(missing_values)
|
||||
return false, "missing required return value(s) : " .. encode(missing_values)
|
||||
end
|
||||
-- Return
|
||||
return true, ret
|
||||
|
|
@ -151,64 +168,63 @@ helpers.fill_ctx = function()
|
|||
-- Return errors as table
|
||||
local errors = {}
|
||||
-- Try to load saved ctx
|
||||
if base.get_request() then
|
||||
bwctx.apply_ref()
|
||||
local request = get_request()
|
||||
if request then
|
||||
apply_ref()
|
||||
end
|
||||
local ctx = ngx.ctx
|
||||
-- Check if ctx is already filled
|
||||
if not ctx.bw then
|
||||
-- Instantiate bw table
|
||||
local data = {}
|
||||
-- Common vars
|
||||
data.kind = "http"
|
||||
if ngx.shared.datastore_stream then
|
||||
data.kind = "stream"
|
||||
if request then
|
||||
-- Common vars
|
||||
data.kind = "http"
|
||||
if subsystem == "stream" then
|
||||
data.kind = "stream"
|
||||
end
|
||||
data.remote_addr = var.remote_addr
|
||||
data.server_name = var.server_name
|
||||
if data.kind == "http" then
|
||||
data.uri = var.uri
|
||||
data.request_uri = var.request_uri
|
||||
data.request_method = var.request_method
|
||||
data.http_user_agent = var.http_user_agent
|
||||
data.http_host = var.http_host
|
||||
data.http_content_type = var.http_content_type
|
||||
data.http_content_length = var.http_content_length
|
||||
data.http_origin = var.http_origin
|
||||
data.http_version = req.http_version()
|
||||
data.scheme = var.scheme
|
||||
end
|
||||
-- IP data : global
|
||||
local ip_global, err = ip_is_global(data.remote_addr)
|
||||
if ip_global == nil then
|
||||
table.insert(errors, "can't check if IP is global : " .. err)
|
||||
else
|
||||
data.ip_is_global = ip_global
|
||||
end
|
||||
-- IP data : v4 / v6
|
||||
data.ip_is_ipv4 = is_ipv4(data.ip)
|
||||
data.ip_is_ipv6 = is_ipv6(data.ip)
|
||||
end
|
||||
data.remote_addr = ngx.var.remote_addr
|
||||
data.server_name = ngx.var.server_name
|
||||
if data.kind == "http" then
|
||||
data.uri = ngx.var.uri
|
||||
data.request_uri = ngx.var.request_uri
|
||||
data.request_method = ngx.var.request_method
|
||||
data.http_user_agent = ngx.var.http_user_agent
|
||||
data.http_host = ngx.var.http_host
|
||||
data.server_name = ngx.var.server_name
|
||||
data.http_content_type = ngx.var.http_content_type
|
||||
data.http_content_length = ngx.var.http_content_length
|
||||
data.http_origin = ngx.var.http_origin
|
||||
data.http_version = ngx.req.http_version()
|
||||
data.scheme = ngx.var.scheme
|
||||
end
|
||||
-- IP data : global
|
||||
local ip_is_global, err = utils.ip_is_global(data.remote_addr)
|
||||
if ip_is_global == nil then
|
||||
table.insert(errors, "can't check if IP is global : " .. err)
|
||||
else
|
||||
data.ip_is_global = ip_is_global
|
||||
end
|
||||
-- IP data : v4 / v6
|
||||
data.ip_is_ipv4 = utils.is_ipv4(data.ip)
|
||||
data.ip_is_ipv6 = utils.is_ipv6(data.ip)
|
||||
-- Misc info
|
||||
data.integration = utils.get_integration()
|
||||
data.version = utils.get_version()
|
||||
-- Fill ctx
|
||||
ctx.bw = data
|
||||
end
|
||||
-- Always create new objects for current phases in case of cosockets
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
local use_redis, err = get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
table.insert(errors, "can't get variable from datastore : " .. err)
|
||||
end
|
||||
ctx.bw.datastore = require "bunkerweb.datastore":new()
|
||||
ctx.bw.clusterstore = require "bunkerweb.clusterstore":new()
|
||||
ctx.bw.cachestore = require "bunkerweb.cachestore":new(use_redis == "yes")
|
||||
ctx.bw.cachestore = require "bunkerweb.cachestore":new(use_redis == "yes", ctx)
|
||||
return true, "ctx filled", errors, ctx
|
||||
end
|
||||
|
||||
helpers.save_ctx = function(ctx)
|
||||
if base.get_request() then
|
||||
bwctx.stash_ref(ctx)
|
||||
if get_request() then
|
||||
stash_ref(ctx)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -222,11 +238,11 @@ function helpers.load_variables(all_variables, plugins)
|
|||
end
|
||||
end
|
||||
end
|
||||
local file = io.open("/usr/share/bunkerweb/settings.json")
|
||||
local file = open("/usr/share/bunkerweb/settings.json")
|
||||
if not file then
|
||||
return false, "can't open settings.json"
|
||||
end
|
||||
local ok, settings = pcall(cjson.decode, file:read("*a"))
|
||||
local ok, settings = pcall(decode, file:read("*a"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid settings.json : " .. settings
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@ local class = require "middleclass"
|
|||
local errlog = require "ngx.errlog"
|
||||
local logger = class("logger")
|
||||
|
||||
local upper = string.upper
|
||||
local raw_log = errlog.raw_log
|
||||
|
||||
function logger:initialize(prefix)
|
||||
self.prefix = string.upper(prefix)
|
||||
self.prefix = upper(prefix)
|
||||
end
|
||||
|
||||
function logger:log(level, msg)
|
||||
errlog.raw_log(level, "[" .. self.prefix .. "] " .. msg)
|
||||
raw_log(level, "[" .. self.prefix .. "] " .. msg)
|
||||
end
|
||||
|
||||
return logger
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
local ngx = ngx
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local class = require "middleclass"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
|
|
@ -6,50 +7,55 @@ local logger = require "bunkerweb.logger"
|
|||
local utils = require "bunkerweb.utils"
|
||||
local plugin = class("plugin")
|
||||
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local get_variable = utils.get_variable
|
||||
local get_ctx_obj = utils.get_ctx_obj
|
||||
local subsystem = ngx.config.subsystem
|
||||
|
||||
function plugin:initialize(id, ctx)
|
||||
-- Store common, values
|
||||
self.id = id
|
||||
local multisite = false
|
||||
local current_phase = ngx.get_phase()
|
||||
local is_request = false
|
||||
local current_phase = get_phase()
|
||||
for _, check_phase in ipairs {
|
||||
"set",
|
||||
"rewrite",
|
||||
"access",
|
||||
"content",
|
||||
"header_filter",
|
||||
"body_filter",
|
||||
"log",
|
||||
"preread",
|
||||
"log_stream",
|
||||
"log_default",
|
||||
"preread"
|
||||
} do
|
||||
if current_phase == check_phase then
|
||||
multisite = true
|
||||
is_request = true
|
||||
break
|
||||
end
|
||||
end
|
||||
self.is_request = multisite
|
||||
self.is_request = is_request
|
||||
-- Store common objects
|
||||
self.logger = logger:new(self.id)
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
local use_redis, err = get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
if self.is_request then
|
||||
-- Store ctx
|
||||
self.ctx = ctx or ngx.ctx
|
||||
self.datastore = utils.get_ctx_obj("datastore", self.ctx) or datastore:new()
|
||||
self.cachestore = utils.get_ctx_obj("cachestore", self.ctx)
|
||||
or cachestore:new(use_redis == "yes", true, self.ctx)
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore", self.ctx) or clusterstore:new(false)
|
||||
self.datastore = get_ctx_obj("datastore", self.ctx) or datastore:new()
|
||||
self.cachestore = get_ctx_obj("cachestore", self.ctx)
|
||||
or cachestore:new(use_redis == "yes", self.ctx)
|
||||
self.clusterstore = get_ctx_obj("clusterstore", self.ctx) or clusterstore:new()
|
||||
else
|
||||
self.datastore = datastore:new()
|
||||
self.cachestore = cachestore:new(use_redis == "yes", true)
|
||||
self.cachestore = cachestore:new(use_redis == "yes")
|
||||
self.clusterstore = clusterstore:new(false)
|
||||
end
|
||||
-- Get metadata
|
||||
local metadata, err = self.datastore:get("plugin_" .. id, true)
|
||||
if not metadata then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
return
|
||||
end
|
||||
-- Store variables
|
||||
|
|
@ -57,21 +63,22 @@ function plugin:initialize(id, ctx)
|
|||
self.multiples = {}
|
||||
local value
|
||||
for k, v in pairs(metadata.settings) do
|
||||
value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
||||
value, err = get_variable(k, v.context == "multisite" and self.is_request)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.ERR, "can't get " .. k .. " variable : " .. err)
|
||||
self.logger:log(ERR, "can't get " .. k .. " variable : " .. err)
|
||||
end
|
||||
self.variables[k] = value
|
||||
end
|
||||
-- Is loading
|
||||
local is_loading, err = utils.get_variable("IS_LOADING", false)
|
||||
local is_loading, err = get_variable("IS_LOADING", false)
|
||||
if is_loading == nil then
|
||||
self.logger:log(ngx.ERR, "can't get IS_LOADING variable : " .. err)
|
||||
self.logger:log(ERR, "can't get IS_LOADING variable : " .. err)
|
||||
end
|
||||
self.is_loading = is_loading == "yes"
|
||||
-- Kind of server
|
||||
self.kind = "http"
|
||||
if ngx.shared.datastore_stream then
|
||||
if subsystem == "http" then
|
||||
self.kind = "http"
|
||||
else
|
||||
self.kind = "stream"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
local ngx = ngx
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local mmdb = require "bunkerweb.mmdb"
|
||||
|
|
@ -10,11 +11,32 @@ local session = require "resty.session"
|
|||
local logger = clogger:new("UTILS")
|
||||
local datastore = cdatastore:new()
|
||||
|
||||
local var = ngx.var
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local WARN = ngx.WARN
|
||||
local null = ngx.null
|
||||
local re_match = ngx.re.match
|
||||
local subsystem = ngx.config.subsystem
|
||||
local get_phase = ngx.get_phase
|
||||
local kill = ngx.thread.kill
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local parse_ipv4 = ipmatcher.parse_ipv4
|
||||
local parse_ipv6 = ipmatcher.parse_ipv6
|
||||
local open = io.open
|
||||
local encode = cjson.encode
|
||||
local decode = cjson.decode
|
||||
local char = string.char
|
||||
local random = math.random
|
||||
local session_start = session.start
|
||||
local session_open = session.open
|
||||
local tonumber = tonumber
|
||||
|
||||
local utils = {}
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
utils.get_variable = function(var, site_search)
|
||||
utils.get_variable = function(variable, site_search, ctx)
|
||||
-- Default site search to true
|
||||
if site_search == nil then
|
||||
site_search = true
|
||||
|
|
@ -24,20 +46,27 @@ utils.get_variable = function(var, site_search)
|
|||
if not variables then
|
||||
return nil, "can't access variables from datastore : " .. err
|
||||
end
|
||||
local value = variables["global"][var]
|
||||
local value = variables["global"][variable]
|
||||
-- Site search case
|
||||
local multisite = site_search and variables["global"]["MULTISITE"] == "yes" and ngx.var.server_name ~= "_"
|
||||
if multisite then
|
||||
value = variables[ngx.var.server_name][var]
|
||||
if site_search and variables["global"]["MULTISITE"] == "yes" then
|
||||
local server_name
|
||||
if ctx and ctx.bw then
|
||||
server_name = ctx.bw.server_name
|
||||
else
|
||||
server_name = var.server_name
|
||||
end
|
||||
if variables[server_name] then
|
||||
value = variables[server_name][variable]
|
||||
end
|
||||
end
|
||||
return value, "success"
|
||||
end
|
||||
|
||||
utils.has_variable = function(var, value)
|
||||
utils.has_variable = function(variable, value)
|
||||
-- Get global variable
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||
return nil, "can't access variables " .. variable .. " from datastore : " .. err
|
||||
end
|
||||
-- Multisite case
|
||||
local multisite = variables["global"]["MULTISITE"] == "yes"
|
||||
|
|
@ -45,7 +74,7 @@ utils.has_variable = function(var, value)
|
|||
local servers = variables["global"]["SERVER_NAME"]
|
||||
-- Check each server
|
||||
for server in servers:gmatch("%S+") do
|
||||
if variables[server][var] == value then
|
||||
if variables[server][variable] == value then
|
||||
return true, "success"
|
||||
end
|
||||
end
|
||||
|
|
@ -53,14 +82,14 @@ utils.has_variable = function(var, value)
|
|||
return false, "success"
|
||||
end
|
||||
end
|
||||
return variables["global"][var] == value, "success"
|
||||
return variables["global"][variable] == value, "success"
|
||||
end
|
||||
|
||||
utils.has_not_variable = function(var, value)
|
||||
utils.has_not_variable = function(variable, value)
|
||||
-- Get global variable
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||
return nil, "can't access variables " .. variable .. " from datastore : " .. err
|
||||
end
|
||||
-- Multisite case
|
||||
local multisite = variables["global"]["MULTISITE"] == "yes"
|
||||
|
|
@ -68,7 +97,7 @@ utils.has_not_variable = function(var, value)
|
|||
local servers = variables["global"]["SERVER_NAME"]
|
||||
-- Check each server
|
||||
for server in servers:gmatch("%S+") do
|
||||
if variables[server][var] ~= "value" then
|
||||
if variables[server][variable] ~= "value" then
|
||||
return true, "success"
|
||||
end
|
||||
end
|
||||
|
|
@ -76,7 +105,7 @@ utils.has_not_variable = function(var, value)
|
|||
return false, "success"
|
||||
end
|
||||
end
|
||||
return variables["global"][var] ~= value, "success"
|
||||
return variables["global"][variable] ~= value, "success"
|
||||
end
|
||||
|
||||
utils.get_multiple_variables = function(vars)
|
||||
|
|
@ -90,8 +119,8 @@ utils.get_multiple_variables = function(vars)
|
|||
result[scope] = {}
|
||||
-- Loop on vars
|
||||
for variable, value in pairs(scoped_vars) do
|
||||
for _, var in ipairs(vars) do
|
||||
if variable:find("^" .. var .. "_?[0-9]*$") then
|
||||
for _, tvar in ipairs(vars) do
|
||||
if variable:find("^" .. tvar .. "_?[0-9]*$") then
|
||||
result[scope][variable] = value
|
||||
end
|
||||
end
|
||||
|
|
@ -102,7 +131,7 @@ end
|
|||
|
||||
utils.is_ip_in_networks = function(ip, networks)
|
||||
-- Instantiate ipmatcher
|
||||
local ipm, err = ipmatcher.new(networks)
|
||||
local ipm, err = ipmatcher_new(networks)
|
||||
if not ipm then
|
||||
return nil, "can't instantiate ipmatcher : " .. err
|
||||
end
|
||||
|
|
@ -115,11 +144,11 @@ utils.is_ip_in_networks = function(ip, networks)
|
|||
end
|
||||
|
||||
utils.is_ipv4 = function(ip)
|
||||
return ipmatcher.parse_ipv4(ip)
|
||||
return parse_ipv4(ip)
|
||||
end
|
||||
|
||||
utils.is_ipv6 = function(ip)
|
||||
return ipmatcher.parse_ipv6(ip)
|
||||
return parse_ipv6(ip)
|
||||
end
|
||||
|
||||
utils.ip_is_global = function(ip)
|
||||
|
|
@ -157,7 +186,7 @@ utils.ip_is_global = function(ip)
|
|||
"ff00::/8",
|
||||
}
|
||||
-- Instantiate ipmatcher
|
||||
local ipm, err = ipmatcher.new(reserved_ips)
|
||||
local ipm, err = ipmatcher_new(reserved_ips)
|
||||
if not ipm then
|
||||
return nil, "can't instantiate ipmatcher : " .. err
|
||||
end
|
||||
|
|
@ -169,7 +198,11 @@ utils.ip_is_global = function(ip)
|
|||
return not matched, "success"
|
||||
end
|
||||
|
||||
utils.get_integration = function()
|
||||
utils.get_integration = function(ctx)
|
||||
-- Check if already in ctx
|
||||
if ctx and ctx.bw.integration then
|
||||
return ctx.bw.integration
|
||||
end
|
||||
-- Check if already in datastore
|
||||
local integration, _ = datastore:get("misc_integration", true)
|
||||
if integration then
|
||||
|
|
@ -177,7 +210,7 @@ utils.get_integration = function()
|
|||
end
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ngx.ERR, "can't get variables from datastore : " .. err)
|
||||
logger:log(ERR, "can't get variables from datastore : " .. err)
|
||||
return "unknown"
|
||||
end
|
||||
-- Swarm
|
||||
|
|
@ -193,12 +226,12 @@ utils.get_integration = function()
|
|||
integration = "autoconf"
|
||||
else
|
||||
-- Already present (e.g. : linux)
|
||||
local f, _ = io.open("/usr/share/bunkerweb/INTEGRATION", "r")
|
||||
local f, _ = open("/usr/share/bunkerweb/INTEGRATION", "r")
|
||||
if f then
|
||||
integration = f:read("*a"):gsub("[\n\r]", "")
|
||||
f:close()
|
||||
else
|
||||
f, _ = io.open("/etc/os-release", "r")
|
||||
f, _ = open("/etc/os-release", "r")
|
||||
if f then
|
||||
local data = f:read("*a")
|
||||
f:close()
|
||||
|
|
@ -217,58 +250,86 @@ utils.get_integration = function()
|
|||
-- Save integration
|
||||
local ok, err = datastore:set("misc_integration", integration, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't cache integration to datastore : " .. err)
|
||||
logger:log(ERR, "can't cache integration to datastore : " .. err)
|
||||
end
|
||||
if ctx then
|
||||
ctx.bw.integration = integration
|
||||
end
|
||||
return integration
|
||||
end
|
||||
|
||||
utils.get_version = function()
|
||||
utils.get_version = function(ctx)
|
||||
-- Check if already in ctx
|
||||
if ctx and ctx.bw.version then
|
||||
return ctx.bw.version
|
||||
end
|
||||
-- Check if already in datastore
|
||||
local version, _ = datastore:get("misc_version", true)
|
||||
if version then
|
||||
return version
|
||||
end
|
||||
-- Read VERSION file
|
||||
local f, err = io.open("/usr/share/bunkerweb/VERSION", "r")
|
||||
local f, err = open("/usr/share/bunkerweb/VERSION", "r")
|
||||
if not f then
|
||||
logger:log(ngx.ERR, "can't read VERSION file : " .. err)
|
||||
logger:log(ERR, "can't read VERSION file : " .. err)
|
||||
return nil
|
||||
end
|
||||
version = f:read("*a"):gsub("[\n\r]", "")
|
||||
f:close()
|
||||
-- Save it to datastore
|
||||
-- Save version
|
||||
local ok, err = datastore:set("misc_version", version, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't cache version to datastore : " .. err)
|
||||
logger:log(ERR, "can't cache version to datastore : " .. err)
|
||||
end
|
||||
if ctx then
|
||||
ctx.bw.version = version
|
||||
end
|
||||
return version
|
||||
end
|
||||
|
||||
utils.get_reason = function(ctx)
|
||||
-- ngx.ctx
|
||||
if ctx.bw.reason then
|
||||
if ctx and ctx.bw and ctx.bw.reason then
|
||||
return ctx.bw.reason
|
||||
end
|
||||
-- ngx.var
|
||||
if ngx.var.reason and ngx.var.reason ~= "" then
|
||||
return ngx.var.reason
|
||||
if var.reason and var.reason ~= "" then
|
||||
return var.reason
|
||||
end
|
||||
-- os.getenv
|
||||
if os.getenv("REASON") == "modsecurity" then
|
||||
return "modsecurity"
|
||||
end
|
||||
-- datastore ban
|
||||
local banned, _ = datastore:get("bans_ip_" .. ngx.var.remote_addr)
|
||||
local ip
|
||||
if ctx and ctx.bw then
|
||||
ip = ctx.bw.remote_addr
|
||||
else
|
||||
ip = var.remote_addr
|
||||
end
|
||||
local banned, _ = datastore:get("bans_ip_" .. ip)
|
||||
if banned then
|
||||
return banned
|
||||
end
|
||||
-- unknown
|
||||
if ngx.status == utils.get_deny_status(ctx) then
|
||||
if ngx.status == utils.get_deny_status() then
|
||||
return "unknown"
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
utils.is_whitelisted = function(ctx)
|
||||
-- ngx.ctx
|
||||
if ctx and ctx.bw and ctx.bw.is_whitelisted then
|
||||
return ctx.bw.is_whitelisted == "yes"
|
||||
end
|
||||
-- ngx.var
|
||||
if var.is_whitelisted and var.is_whitelisted == "yes" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
utils.get_resolvers = function()
|
||||
-- Get resolvers from datastore if existing
|
||||
local resolvers, _ = datastore:get("misc_resolvers", true)
|
||||
|
|
@ -278,7 +339,7 @@ utils.get_resolvers = function()
|
|||
-- Otherwise extract DNS_RESOLVERS variable
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ngx.ERR, "can't get variables from datastore : " .. err)
|
||||
logger:log(ERR, "can't get variables from datastore : " .. err)
|
||||
return "unknown"
|
||||
end
|
||||
-- Make table for resolver1 resolver2 ... string
|
||||
|
|
@ -289,19 +350,19 @@ utils.get_resolvers = function()
|
|||
-- Add it to the datastore
|
||||
local ok, err = datastore:set("misc_resolvers", resolvers, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save misc_resolvers to datastore : " .. err)
|
||||
logger:log(ERR, "can't save misc_resolvers to datastore : " .. err)
|
||||
end
|
||||
return resolvers
|
||||
end
|
||||
|
||||
utils.get_rdns = function(ip)
|
||||
utils.get_rdns = function(ip, ctx, pool)
|
||||
-- Check cache
|
||||
local cachestore = utils.new_cachestore()
|
||||
local cachestore = utils.new_cachestore(ctx, pool)
|
||||
local ok, value = cachestore:get("rdns_" .. ip)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't get rdns from cachestore : " .. value)
|
||||
logger:log(ERR, "can't get rdns from cachestore : " .. value)
|
||||
elseif value then
|
||||
return cjson.decode(value), "success"
|
||||
return decode(value), "success"
|
||||
end
|
||||
-- Get resolvers
|
||||
local resolvers, err = utils.get_resolvers()
|
||||
|
|
@ -323,7 +384,7 @@ utils.get_rdns = function(ip)
|
|||
-- Do rDNS query
|
||||
local answers, err = rdns:reverse_query(ip)
|
||||
if not answers then
|
||||
logger:log(ngx.ERR, "error while doing reverse DNS query for " .. ip .. " : " .. err)
|
||||
logger:log(ERR, "error while doing reverse DNS query for " .. ip .. " : " .. err)
|
||||
ret_err = err
|
||||
else
|
||||
if answers.errcode then
|
||||
|
|
@ -337,21 +398,21 @@ utils.get_rdns = function(ip)
|
|||
end
|
||||
end
|
||||
-- Save to cache
|
||||
ok, err = cachestore:set("rdns_" .. ip, cjson.encode(ptrs), 3600)
|
||||
ok, err = cachestore:set("rdns_" .. ip, encode(ptrs), 3600)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't set rdns into cachestore : " .. err)
|
||||
logger:log(ERR, "can't set rdns into cachestore : " .. err)
|
||||
end
|
||||
return ptrs, ret_err
|
||||
end
|
||||
|
||||
utils.get_ips = function(fqdn, ipv6)
|
||||
utils.get_ips = function(fqdn, ipv6, ctx, pool)
|
||||
-- Check cache
|
||||
local cachestore = utils.new_cachestore()
|
||||
local cachestore = utils.new_cachestore(ctx, pool)
|
||||
local ok, value = cachestore:get("dns_" .. fqdn)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't get dns from cachestore : " .. value)
|
||||
logger:log(ERR, "can't get dns from cachestore : " .. value)
|
||||
elseif value then
|
||||
return cjson.decode(value), "success"
|
||||
return decode(value), "success"
|
||||
end
|
||||
-- By default perform ipv6 lookups (only if USE_IPV6=yes)
|
||||
if ipv6 == nil then
|
||||
|
|
@ -377,7 +438,7 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
-- luacheck: ignore 421
|
||||
local use_ipv6, err = utils.get_variable("USE_IPV6", false)
|
||||
if not use_ipv6 then
|
||||
logger:log(ngx.ERR, "can't get USE_IPV6 variable " .. err)
|
||||
logger:log(ERR, "can't get USE_IPV6 variable " .. err)
|
||||
elseif use_ipv6 == "yes" then
|
||||
table.insert(qtypes, res.TYPE_AAAA)
|
||||
end
|
||||
|
|
@ -401,7 +462,7 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
end
|
||||
end
|
||||
for qtype, error in pairs(res_errors) do
|
||||
logger:log(ngx.ERR, "error while doing " .. qtype .. " DNS query for " .. fqdn .. " : " .. error)
|
||||
logger:log(ERR, "error while doing " .. qtype .. " DNS query for " .. fqdn .. " : " .. error)
|
||||
end
|
||||
-- Extract all IPs
|
||||
local ips = {}
|
||||
|
|
@ -414,11 +475,11 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
end
|
||||
end
|
||||
-- Save to cache
|
||||
ok, err = cachestore:set("dns_" .. fqdn, cjson.encode(ips), 3600)
|
||||
ok, err = cachestore:set("dns_" .. fqdn, encode(ips), 3600)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't set dns into cachestore : " .. err)
|
||||
logger:log(ERR, "can't set dns into cachestore : " .. err)
|
||||
end
|
||||
return ips, cjson.encode(res_errors) .. " " .. cjson.encode(ans_errors)
|
||||
return ips, encode(res_errors) .. " " .. encode(ans_errors)
|
||||
end
|
||||
|
||||
utils.get_country = function(ip)
|
||||
|
|
@ -458,38 +519,36 @@ utils.rand = function(nb, no_numbers)
|
|||
-- lowers, uppers and numbers
|
||||
if not no_numbers then
|
||||
for i = 48, 57 do
|
||||
table.insert(charset, string.char(i))
|
||||
table.insert(charset, char(i))
|
||||
end
|
||||
end
|
||||
for i = 65, 90 do
|
||||
table.insert(charset, string.char(i))
|
||||
table.insert(charset, char(i))
|
||||
end
|
||||
for i = 97, 122 do
|
||||
table.insert(charset, string.char(i))
|
||||
table.insert(charset, char(i))
|
||||
end
|
||||
local result = ""
|
||||
for _ = 1, nb do
|
||||
result = result .. charset[math.random(1, #charset)]
|
||||
result = result .. charset[random(1, #charset)]
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
utils.get_deny_status = function(ctx)
|
||||
-- Stream case
|
||||
if ctx.bw and ctx.bw.kind == "stream" then
|
||||
return 444
|
||||
utils.get_deny_status = function()
|
||||
if subsystem == "http" then
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ERR, "can't get variables from datastore : " .. err)
|
||||
return 403
|
||||
end
|
||||
return tonumber(variables["global"]["DENY_HTTP_STATUS"])
|
||||
end
|
||||
-- http case
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ngx.ERR, "can't get variables from datastore : " .. err)
|
||||
return 403
|
||||
end
|
||||
return tonumber(variables["global"]["DENY_HTTP_STATUS"])
|
||||
return 444
|
||||
end
|
||||
|
||||
utils.check_session = function(ctx)
|
||||
local _session, _, exists, _ = session.start({ audience = "metadata" })
|
||||
local _session, _, exists, _ = session_start({ audience = "metadata" })
|
||||
if exists then
|
||||
for _, check in ipairs(ctx.bw.sessions_checks) do
|
||||
local key = check[1]
|
||||
|
|
@ -500,7 +559,7 @@ utils.check_session = function(ctx)
|
|||
if not ok then
|
||||
return false, "session:destroy() error : " .. err
|
||||
end
|
||||
logger:log(ngx.WARN, "session check " .. key .. " failed, destroying session")
|
||||
logger:log(WARN, "session check " .. key .. " failed, destroying session")
|
||||
return utils.check_session(ctx)
|
||||
end
|
||||
end
|
||||
|
|
@ -527,9 +586,9 @@ utils.get_session = function(audience, ctx)
|
|||
end
|
||||
end
|
||||
-- Open session with specific audience
|
||||
local _session, err, _ = session.open({ audience = audience })
|
||||
local _session, err, _ = session_open({ audience = audience })
|
||||
if err then
|
||||
logger:log(ngx.INFO, "session:open() error : " .. err)
|
||||
logger:log(INFO, "session:open() error : " .. err)
|
||||
end
|
||||
return _session
|
||||
end
|
||||
|
|
@ -607,7 +666,7 @@ utils.is_banned = function(ip)
|
|||
elseif data.err then
|
||||
clusterstore:close()
|
||||
return nil, "redis script error : " .. data.err
|
||||
elseif data[1] ~= ngx.null then
|
||||
elseif data[1] ~= null then
|
||||
clusterstore:close()
|
||||
-- Update local cache
|
||||
ok, err = datastore:set("bans_ip_" .. ip, data[1], data[2])
|
||||
|
|
@ -649,16 +708,17 @@ utils.add_ban = function(ip, reason, ttl)
|
|||
return true, "success"
|
||||
end
|
||||
|
||||
utils.new_cachestore = function()
|
||||
utils.new_cachestore = function(ctx, pool)
|
||||
-- Check if redis is used
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
logger:log(ngx.ERR, "can't get USE_REDIS variable : " .. err)
|
||||
logger:log(ERR, "can't get USE_REDIS variable : " .. err)
|
||||
use_redis = false
|
||||
else
|
||||
use_redis = use_redis == "yes"
|
||||
end
|
||||
-- Instantiate
|
||||
return require "bunkerweb.cachestore":new(use_redis, true)
|
||||
return require "bunkerweb.cachestore":new(use_redis, ctx, pool == nil or pool)
|
||||
end
|
||||
|
||||
utils.regex_match = function(str, regex, options)
|
||||
|
|
@ -666,9 +726,9 @@ utils.regex_match = function(str, regex, options)
|
|||
if options then
|
||||
all_options = all_options .. options
|
||||
end
|
||||
local match, err = ngx.re.match(str, regex, all_options)
|
||||
local match, err = re_match(str, regex, all_options)
|
||||
if err then
|
||||
logger:log(ngx.ERR, "error while matching regex " .. regex .. "with string " .. str)
|
||||
logger:log(ERR, "error while matching regex " .. regex .. "with string " .. str)
|
||||
return nil
|
||||
end
|
||||
return match
|
||||
|
|
@ -679,24 +739,28 @@ utils.get_phases = function()
|
|||
"init",
|
||||
"init_worker",
|
||||
"set",
|
||||
"rewrite",
|
||||
"access",
|
||||
"content",
|
||||
"ssl_certificate",
|
||||
"header",
|
||||
"log",
|
||||
"preread",
|
||||
"log_stream",
|
||||
"log_default",
|
||||
"log_default"
|
||||
}
|
||||
end
|
||||
|
||||
utils.is_cosocket_available = function()
|
||||
local phases = {
|
||||
"timer",
|
||||
"rewrite",
|
||||
"access",
|
||||
"content",
|
||||
"ssl_certificate",
|
||||
"preread",
|
||||
"preread"
|
||||
}
|
||||
local current_phase = ngx.get_phase()
|
||||
local current_phase = get_phase()
|
||||
for _, phase in ipairs(phases) do
|
||||
if current_phase == phase then
|
||||
return true
|
||||
|
|
@ -707,16 +771,17 @@ end
|
|||
|
||||
utils.kill_all_threads = function(threads)
|
||||
for _, thread in ipairs(threads) do
|
||||
local ok, err = ngx.thread.kill(thread)
|
||||
local ok, err = kill(thread)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while killing thread : " .. err)
|
||||
logger:log(ERR, "error while killing thread : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils.get_ctx_obj = function(obj)
|
||||
if ngx.ctx and ngx.ctx.bw then
|
||||
return ngx.ctx.bw[obj]
|
||||
utils.get_ctx_obj = function(obj, ctx)
|
||||
local vctx = ctx or ngx.ctx
|
||||
if vctx and vctx.bw then
|
||||
return vctx.bw[obj]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -17,55 +17,63 @@ server {
|
|||
access_by_lua_block {
|
||||
-- Instantiate objects and import required modules
|
||||
local logger = require "bunkerweb.logger":new("API")
|
||||
local api = require "bunkerweb.api":new()
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local WARN = ngx.WARN
|
||||
local NOTICE = ngx.NOTICE
|
||||
local HTTP_CLOSE = ngx.HTTP_CLOSE
|
||||
local exit = ngx.exit
|
||||
local say = ngx.say
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local tostring = tostring
|
||||
|
||||
-- Start API handler
|
||||
logger:log(ngx.INFO, "API handler started")
|
||||
logger:log(INFO, "API handler started")
|
||||
|
||||
-- Fill ctx
|
||||
logger:log(ngx.INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
logger:log(INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = fill_ctx()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
|
||||
logger:log(ERR, "fill_ctx() failed : " .. ret)
|
||||
elseif errors then
|
||||
for i, error in ipairs(errors) do
|
||||
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
|
||||
-- Check host header
|
||||
if not ctx.bw.http_host or ctx.bw.http_host ~= "{{ API_SERVER_NAME }}" then
|
||||
logger:log(ngx.WARN, "wrong Host header from IP " .. ctx.bw.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_CLOSE)
|
||||
logger:log(WARN, "wrong Host header from IP " .. ctx.bw.remote_addr)
|
||||
return exit(HTTP_CLOSE)
|
||||
end
|
||||
|
||||
-- Check IP
|
||||
local api = require "bunkerweb.api":new(ctx)
|
||||
local ok, err = api:is_allowed_ip()
|
||||
if not ok then
|
||||
logger:log(ngx.WARN, "can't validate access from IP " .. ctx.bw.remote_addr .. " : " .. err)
|
||||
return ngx.exit(ngx.HTTP_CLOSE)
|
||||
logger:log(WARN, "can't validate access from IP " .. ctx.bw.remote_addr .. " : " .. err)
|
||||
return exit(HTTP_CLOSE)
|
||||
end
|
||||
logger:log(ngx.NOTICE, "validated access from IP " .. ctx.bw.remote_addr)
|
||||
logger:log(NOTICE, "validated access from IP " .. ctx.bw.remote_addr)
|
||||
|
||||
-- Do API call
|
||||
local ok, err, status, resp = api:do_api_call()
|
||||
if not ok then
|
||||
logger:log(ngx.WARN, "call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " failed : " .. err)
|
||||
logger:log(WARN, "call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " failed : " .. err)
|
||||
else
|
||||
logger:log(ngx.NOTICE, "successful call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " : " .. err)
|
||||
logger:log(NOTICE, "successful call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " : " .. err)
|
||||
end
|
||||
|
||||
-- Start API handler
|
||||
logger:log(ngx.INFO, "API handler ended")
|
||||
|
||||
-- Save ctx
|
||||
ngx.ctx = ctx
|
||||
-- Stop API handler
|
||||
logger:log(INFO, "API handler ended")
|
||||
|
||||
-- Send response
|
||||
ngx.status = status
|
||||
ngx.say(resp)
|
||||
return ngx.exit(status)
|
||||
say(resp)
|
||||
return exit(status)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,70 +45,78 @@ server {
|
|||
local helpers = require "bunkerweb.helpers"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local tostring = tostring
|
||||
local get_reason = utils.get_reason
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
|
||||
-- Start log phase
|
||||
local logger = clogger:new("LOG-DEFAULT")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "log_default phase started")
|
||||
logger:log(INFO, "log_default phase started")
|
||||
|
||||
-- Fill ctx
|
||||
logger:log(ngx.INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
logger:log(INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = fill_ctx()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
|
||||
logger:log(ERR, "fill_ctx() failed : " .. ret)
|
||||
elseif errors then
|
||||
for i, error in ipairs(errors) do
|
||||
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call log_default() methods
|
||||
logger:log(ngx.INFO, "calling log_default() methods of plugins ...")
|
||||
logger:log(INFO, "calling log_default() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.log_default) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has log method
|
||||
if plugin_lua.log_default ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua, ctx)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "log_default")
|
||||
local ok, ret = call_plugin(plugin_obj, "log_default")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":log_default() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":log_default() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method log_default() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method log_default() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called log_default() methods of plugins")
|
||||
logger:log(INFO, "called log_default() methods of plugins")
|
||||
|
||||
-- Display reason at info level
|
||||
if ctx.reason then
|
||||
logger:log(ngx.INFO, "client was denied with reason : " .. reason)
|
||||
local reason = get_reason(ctx)
|
||||
if reason then
|
||||
logger:log(INFO, "client was denied with reason : " .. reason)
|
||||
end
|
||||
|
||||
-- Save ctx
|
||||
ngx.ctx = ctx
|
||||
|
||||
logger:log(ngx.INFO, "log_default phase ended")
|
||||
logger:log(INFO, "log_default phase ended")
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,57 +5,71 @@ init_by_lua_block {
|
|||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local popen = io.popen
|
||||
local open = io.open
|
||||
local load_plugin = helpers.load_plugin
|
||||
local load_variables = helpers.load_variables
|
||||
local order_plugins = helpers.order_plugins
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local encode = cjson.encode
|
||||
|
||||
-- Start init phase
|
||||
local logger = clogger:new("INIT")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init phase started")
|
||||
logger:log(NOTICE, "init phase started")
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
logger:log(NOTICE, "deleting old keys from datastore ...")
|
||||
datastore:flush_lru()
|
||||
local data_keys = { "^plugin", "^misc_" }
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
logger:log(ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
|
||||
logger:log(INFO, "deleted " .. key .. " from datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "deleted old keys from datastore")
|
||||
logger:log(NOTICE, "deleted old keys from datastore")
|
||||
|
||||
-- Load plugins into the datastore
|
||||
logger:log(ngx.NOTICE, "saving plugins into datastore ...")
|
||||
logger:log(NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins" }
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
local paths = popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
local ok, plugin = helpers.load_plugin(path .. "/plugin.json")
|
||||
local ok, plugin = load_plugin(path .. "/plugin.json")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin)
|
||||
logger:log(ERR, plugin)
|
||||
else
|
||||
local ok, err = datastore:set("plugin_" .. plugin.id, plugin, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
logger:log(ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
else
|
||||
table.insert(plugins, plugin)
|
||||
logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
logger:log(NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local ok, err = datastore:set("plugins", plugins, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Load variables into the datastore
|
||||
logger:log(ngx.NOTICE, "saving variables into datastore ...")
|
||||
local file = io.open("/etc/nginx/variables.env")
|
||||
logger:log(NOTICE, "saving variables into datastore ...")
|
||||
local file = open("/etc/nginx/variables.env")
|
||||
if not file then
|
||||
logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
|
||||
logger:log(ERR, "can't open /etc/nginx/variables.env file")
|
||||
return false
|
||||
end
|
||||
file:close()
|
||||
|
|
@ -64,73 +78,73 @@ init_by_lua_block {
|
|||
local variable, value = line:match("^([^=]+)=(.*)$")
|
||||
all_variables[variable] = value
|
||||
end
|
||||
local ok, variables = helpers.load_variables(all_variables, plugins)
|
||||
local ok, variables = load_variables(all_variables, plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while loading variables : " .. variables)
|
||||
logger:log(ERR, "error while loading variables : " .. variables)
|
||||
return false
|
||||
end
|
||||
local ok, err = datastore:set("variables", variables, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
logger:log(NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new(false, true)
|
||||
local cachestore = require "bunkerweb.cachestore":new(false)
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
logger:log(ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
logger:log(ngx.NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = helpers.order_plugins(plugins)
|
||||
logger:log(NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = order_plugins(plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't compute plugins order : " .. err)
|
||||
logger:log(ERR, "can't compute plugins order : " .. err)
|
||||
return false
|
||||
end
|
||||
for phase, id_list in pairs(order) do
|
||||
logger:log(ngx.NOTICE, "plugins order for phase " .. phase .. " : " .. cjson.encode(id_list))
|
||||
logger:log(NOTICE, "plugins order for phase " .. phase .. " : " .. encode(id_list))
|
||||
end
|
||||
local ok, err = datastore:set("plugins_order", order, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins order into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins order into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved plugins order into datastore")
|
||||
logger:log(NOTICE, "saved plugins order into datastore")
|
||||
|
||||
-- Call init() method
|
||||
logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
|
||||
logger:log(NOTICE, "calling init() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order["init"]) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.NOTICE, err)
|
||||
logger:log(NOTICE, err)
|
||||
else
|
||||
-- Check if plugin has init method
|
||||
if plugin_lua.init ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init")
|
||||
local ok, ret = call_plugin(plugin_obj, "init")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
logger:log(NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
logger:log(NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.NOTICE, "called init() methods of plugins")
|
||||
logger:log(NOTICE, "called init() methods of plugins")
|
||||
|
||||
logger:log(ngx.NOTICE, "init phase ended")
|
||||
logger:log(NOTICE, "init phase ended")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,57 +5,71 @@ init_by_lua_block {
|
|||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local popen = io.popen
|
||||
local open = io.open
|
||||
local load_plugin = helpers.load_plugin
|
||||
local load_variables = helpers.load_variables
|
||||
local order_plugins = helpers.order_plugins
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local encode = cjson.encode
|
||||
|
||||
-- Start init phase
|
||||
local logger = clogger:new("INIT")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init-stream phase started")
|
||||
logger:log(NOTICE, "init-stream phase started")
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
logger:log(NOTICE, "deleting old keys from datastore ...")
|
||||
datastore:flush_lru()
|
||||
local data_keys = { "^plugin", "^misc_" }
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
logger:log(ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
|
||||
logger:log(INFO, "deleted " .. key .. " from datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "deleted old keys from datastore")
|
||||
logger:log(NOTICE, "deleted old keys from datastore")
|
||||
|
||||
-- Load plugins into the datastore
|
||||
logger:log(ngx.NOTICE, "saving plugins into datastore ...")
|
||||
logger:log(NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins" }
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
local paths = popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
local ok, plugin = helpers.load_plugin(path .. "/plugin.json")
|
||||
local ok, plugin = load_plugin(path .. "/plugin.json")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin)
|
||||
logger:log(ERR, plugin)
|
||||
else
|
||||
local ok, err = datastore:set("plugin_" .. plugin.id, plugin, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
logger:log(ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
else
|
||||
table.insert(plugins, plugin)
|
||||
logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
logger:log(NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local ok, err = datastore:set("plugins", plugins, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Load variables into the datastore
|
||||
logger:log(ngx.NOTICE, "saving variables into datastore ...")
|
||||
local file = io.open("/etc/nginx/variables.env")
|
||||
local file = open("/etc/nginx/variables.env")
|
||||
if not file then
|
||||
logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
|
||||
logger:log(ERR, "can't open /etc/nginx/variables.env file")
|
||||
return false
|
||||
end
|
||||
file:close()
|
||||
|
|
@ -64,73 +78,73 @@ init_by_lua_block {
|
|||
local variable, value = line:match("^([^=]+)=(.*)$")
|
||||
all_variables[variable] = value
|
||||
end
|
||||
local ok, variables = helpers.load_variables(all_variables, plugins)
|
||||
local ok, variables = load_variables(all_variables, plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while loading variables : " .. variables)
|
||||
logger:log(ERR, "error while loading variables : " .. variables)
|
||||
return false
|
||||
end
|
||||
local ok, err = datastore:set("variables", variables, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
logger:log(NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new(false, true)
|
||||
local cachestore = require "bunkerweb.cachestore":new(false)
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
logger:log(ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
logger:log(ngx.NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = helpers.order_plugins(plugins)
|
||||
logger:log(NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = order_plugins(plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't compute plugins order : " .. err)
|
||||
logger:log(ERR, "can't compute plugins order : " .. err)
|
||||
return false
|
||||
end
|
||||
for phase, id_list in pairs(order) do
|
||||
logger:log(ngx.NOTICE, "plugins order for phase " .. phase .. " : " .. cjson.encode(id_list))
|
||||
logger:log(NOTICE, "plugins order for phase " .. phase .. " : " .. encode(id_list))
|
||||
end
|
||||
local ok, err = datastore:set("plugins_order", order, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins order into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins order into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved plugins order into datastore")
|
||||
logger:log(NOTICE, "saved plugins order into datastore")
|
||||
|
||||
-- Call init() method
|
||||
logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
|
||||
logger:log(NOTICE, "calling init() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order["init"]) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.NOTICE, err)
|
||||
logger:log(NOTICE, err)
|
||||
else
|
||||
-- Check if plugin has init method
|
||||
if plugin_lua.init ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init")
|
||||
local ok, ret = call_plugin(plugin_obj, "init")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
logger:log(NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
logger:log(NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.NOTICE, "called init() methods of plugins")
|
||||
logger:log(NOTICE, "called init() methods of plugins")
|
||||
|
||||
logger:log(ngx.NOTICE, "init-stream phase ended")
|
||||
logger:log(NOTICE, "init-stream phase ended")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,16 +5,23 @@ init_worker_by_lua_block {
|
|||
local ready_work = function(premature)
|
||||
-- Libs
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cjson = require "cjson"
|
||||
|
||||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("INIT-WORKER")
|
||||
local datastore = require "bunkerweb.datastore":new()
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
|
||||
-- Don't go further we are in loading state
|
||||
local is_loading, err = require "bunkerweb.utils".get_variable("IS_LOADING", false)
|
||||
if not is_loading then
|
||||
logger:log(ngx.ERR, "utils.get_variable() failed : " .. err)
|
||||
logger:log(ERR, "utils.get_variable() failed : " .. err)
|
||||
return
|
||||
elseif is_loading == "yes" then
|
||||
return
|
||||
|
|
@ -23,92 +30,92 @@ init_worker_by_lua_block {
|
|||
-- Instantiate lock
|
||||
local lock = require "resty.lock":new("worker_lock", { timeout = 10 })
|
||||
if not lock then
|
||||
logger:log(ngx.ERR, "lock:new() failed : " .. err)
|
||||
logger:log(ERR, "lock:new() failed : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Acquire lock
|
||||
local elapsed, err = lock:lock("ready")
|
||||
if elapsed == nil then
|
||||
logger:log(ngx.ERR, "lock:lock() failed : " .. err)
|
||||
logger:log(ERR, "lock:lock() failed : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if work is done
|
||||
local ok, err = datastore:get("misc_ready")
|
||||
if not ok and err ~= "not found" then
|
||||
logger:log(ngx.ERR, "datastore:get() failed : " .. err)
|
||||
logger:log(ERR, "datastore:get() failed : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
if ok then
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
logger:log(ngx.INFO, "init_worker phase started")
|
||||
logger:log(INFO, "init_worker phase started")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Call init_worker() methods
|
||||
logger:log(ngx.INFO, "calling init_worker() methods of plugins ...")
|
||||
logger:log(INFO, "calling init_worker() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.init_worker) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has init_worker method
|
||||
if plugin_lua.init_worker ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init_worker")
|
||||
local ok, ret = call_plugin(plugin_obj, "init_worker")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init_worker() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":init_worker() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":init_worker() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":init_worker() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method init_worker() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method init_worker() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called init_worker() methods of plugins")
|
||||
logger:log(INFO, "called init_worker() methods of plugins")
|
||||
|
||||
-- End
|
||||
local ok, err = datastore:set("misc_ready", "ok")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "datastore:set() failed : " .. err)
|
||||
logger:log(ERR, "datastore:set() failed : " .. err)
|
||||
end
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
logger:log(ngx.INFO, "init phase ended")
|
||||
logger:log(ngx.NOTICE, "BunkerWeb is ready to fool hackers ! 🚀")
|
||||
logger:log(INFO, "init phase ended")
|
||||
logger:log(NOTICE, "BunkerWeb is ready to fool hackers ! 🚀")
|
||||
end
|
||||
|
||||
-- Start timer
|
||||
|
|
|
|||
|
|
@ -7,114 +7,134 @@ access_by_lua_block {
|
|||
local cclusterstore = require "bunkerweb.clusterstore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local is_internal = ngx_req.is_internal
|
||||
local exit = ngx.exit
|
||||
local ngx_redirect = ngx.redirect
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local WARN = ngx.WARN
|
||||
local NOTICE = ngx.NOTICE
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local save_ctx = helpers.save_ctx
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local is_banned = utils.is_banned
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local tostring = tostring
|
||||
|
||||
-- Don't process internal requests
|
||||
local logger = clogger:new("ACCESS")
|
||||
if ngx.req.is_internal() then
|
||||
logger:log(ngx.INFO, "skipped access phase because request is internal")
|
||||
if is_internal() then
|
||||
logger:log(INFO, "skipped access phase because request is internal")
|
||||
return true
|
||||
end
|
||||
|
||||
-- Start access phase
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "access phase started")
|
||||
logger:log(INFO, "access phase started")
|
||||
|
||||
-- Fill ctx
|
||||
logger:log(ngx.INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
logger:log(INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = fill_ctx()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
|
||||
logger:log(ERR, "fill_ctx() failed : " .. ret)
|
||||
elseif errors then
|
||||
for i, error in ipairs(errors) do
|
||||
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
|
||||
-- Process bans as soon as possible
|
||||
if ctx.bw.is_whitelisted ~= "yes" then
|
||||
local banned, reason, ttl = utils.is_banned(ctx.bw.remote_addr)
|
||||
if not is_whitelisted(ctx) then
|
||||
local banned, reason, ttl = is_banned(ctx.bw.remote_addr)
|
||||
if banned == nil then
|
||||
logger:log(ngx.ERR, "can't check if IP " .. ctx.bw.remote_addr .. " is banned : " .. reason)
|
||||
logger:log(ERR, "can't check if IP " .. ctx.bw.remote_addr .. " is banned : " .. reason)
|
||||
elseif banned then
|
||||
ctx.bw.is_banned = true
|
||||
helpers.save_ctx(ctx)
|
||||
logger:log(ngx.WARN,
|
||||
ctx.bw.reason = reason
|
||||
save_ctx(ctx)
|
||||
logger:log(WARN,
|
||||
"IP " .. ctx.bw.remote_addr .. " is banned with reason " .. reason .. " (" .. tostring(ttl) .. "s remaining)")
|
||||
return ngx.exit(utils.get_deny_status(ctx))
|
||||
return exit(get_deny_status())
|
||||
else
|
||||
logger:log(ngx.INFO, "IP " .. ctx.bw.remote_addr .. " is not banned")
|
||||
logger:log(INFO, "IP " .. ctx.bw.remote_addr .. " is not banned")
|
||||
end
|
||||
end
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call access() methods
|
||||
logger:log(ngx.INFO, "calling access() methods of plugins ...")
|
||||
logger:log(INFO, "calling access() methods of plugins ...")
|
||||
local status = nil
|
||||
local redirect = nil
|
||||
for i, plugin_id in ipairs(order.access) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has access method
|
||||
if plugin_lua.access ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua, ctx)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua, ctx)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "access")
|
||||
local ok, ret = call_plugin(plugin_obj, "access")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":access() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":access() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":access() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":access() call successful : " .. ret.msg)
|
||||
end
|
||||
if ret.status then
|
||||
if ret.status == utils.get_deny_status(ctx) then
|
||||
if ret.status == get_deny_status() then
|
||||
ctx.bw.reason = plugin_id
|
||||
logger:log(ngx.WARN, "denied access from " .. plugin_id .. " : " .. ret.msg)
|
||||
logger:log(WARN, "denied access from " .. plugin_id .. " : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. " returned status " .. tostring(ret.status) .. " : " .. ret.msg)
|
||||
logger:log(NOTICE, plugin_id .. " returned status " .. tostring(ret.status) .. " : " .. ret.msg)
|
||||
end
|
||||
status = ret.status
|
||||
break
|
||||
elseif ret.redirect then
|
||||
logger:log(ngx.NOTICE, plugin_id .. " redirect to " .. ret.redirect .. " : " .. ret.msg)
|
||||
logger:log(NOTICE, plugin_id .. " redirect to " .. ret.redirect .. " : " .. ret.msg)
|
||||
redirect = ret.redirect
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method access() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method access() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called access() methods of plugins")
|
||||
logger:log(INFO, "called access() methods of plugins")
|
||||
|
||||
-- Save ctx
|
||||
helpers.save_ctx(ctx)
|
||||
save_ctx(ctx)
|
||||
|
||||
logger:log(ngx.INFO, "access phase ended")
|
||||
logger:log(INFO, "access phase ended")
|
||||
|
||||
-- Return status if needed
|
||||
if status then
|
||||
return ngx.exit(status)
|
||||
return exit(status)
|
||||
end
|
||||
|
||||
-- Redirect if needed
|
||||
if redirect then
|
||||
return ngx.redirect(redirect)
|
||||
return ngx_redirect(redirect)
|
||||
end
|
||||
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -1,69 +1,77 @@
|
|||
header_filter_by_lua_block {
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local save_ctx = helpers.save_ctx
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local tostring = tostring
|
||||
|
||||
-- Start set phase
|
||||
local logger = clogger:new("HEADER")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "header phase started")
|
||||
logger:log(INFO, "header phase started")
|
||||
|
||||
-- Fill ctx
|
||||
logger:log(ngx.INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
logger:log(INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = fill_ctx()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
|
||||
logger:log(ERR, "fill_ctx() failed : " .. ret)
|
||||
elseif errors then
|
||||
for i, error in ipairs(errors) do
|
||||
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call header() methods
|
||||
logger:log(ngx.INFO, "calling header() methods of plugins ...")
|
||||
logger:log(INFO, "calling header() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.header) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has header method
|
||||
if plugin_lua.header ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua, ctx)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua, ctx)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "header")
|
||||
local ok, ret = call_plugin(plugin_obj, "header")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":header() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":header() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":header() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":header() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method header() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method header() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called header() methods of plugins")
|
||||
logger:log(INFO, "called header() methods of plugins")
|
||||
|
||||
-- Save ctx
|
||||
helpers.save_ctx(ctx)
|
||||
save_ctx(ctx)
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,71 +1,81 @@
|
|||
log_by_lua_block {
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local get_reason = utils.get_reason
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local tostring = tostring
|
||||
|
||||
-- Start log phase
|
||||
local logger = clogger:new("LOG")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "log phase started")
|
||||
logger:log(INFO, "log phase started")
|
||||
|
||||
-- Fill ctx
|
||||
logger:log(ngx.INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
logger:log(INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = fill_ctx()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
|
||||
logger:log(ERR, "fill_ctx() failed : " .. ret)
|
||||
elseif errors then
|
||||
for i, error in ipairs(errors) do
|
||||
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call log() methods
|
||||
logger:log(ngx.INFO, "calling log() methods of plugins ...")
|
||||
logger:log(INFO, "calling log() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.log) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has log method
|
||||
if plugin_lua.log ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua, ctx)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua, ctx)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "log")
|
||||
local ok, ret = call_plugin(plugin_obj, "log")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":log() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":log() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":log() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":log() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method log() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method log() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called log() methods of plugins")
|
||||
logger:log(INFO, "called log() methods of plugins")
|
||||
|
||||
-- Display reason at info level
|
||||
if ctx.bw.reason then
|
||||
logger:log(ngx.INFO, "client was denied with reason : " .. ctx.bw.reason)
|
||||
local reason = get_reason(ctx)
|
||||
if reason then
|
||||
logger:log(INFO, "client was denied with reason : " .. reason)
|
||||
end
|
||||
|
||||
logger:log(ngx.INFO, "log phase ended")
|
||||
logger:log(INFO, "log phase ended")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +1,56 @@
|
|||
set $dummy_set "";
|
||||
set_by_lua_block $dummy_set {
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local ccachestore = require "bunkerweb.cachestore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local is_internal = ngx_req.is_internal
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local save_ctx = helpers.save_ctx
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local tostring = tostring
|
||||
|
||||
-- Don't process internal requests
|
||||
local logger = clogger:new("SET")
|
||||
if ngx.req.is_internal() then
|
||||
logger:log(ngx.INFO, "skipped set phase because request is internal")
|
||||
if is_internal() then
|
||||
logger:log(INFO, "skipped set phase because request is internal")
|
||||
return true
|
||||
end
|
||||
|
||||
-- Start set phase
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "set phase started")
|
||||
logger:log(INFO, "set phase started")
|
||||
|
||||
-- Update cachestore only once and before any other code
|
||||
local cachestore = ccachestore:new()
|
||||
local ok, err = cachestore.cache:update()
|
||||
local cachestore = ccachestore:new(false)
|
||||
local ok, err = cachestore:update()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't update cachestore : " .. err)
|
||||
logger:log(ERR, "can't update cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- Fill ctx
|
||||
logger:log(ngx.INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
logger:log(INFO, "filling ngx.ctx ...")
|
||||
local ok, ret, errors, ctx = fill_ctx()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
|
||||
logger:log(ERR, "fill_ctx() failed : " .. ret)
|
||||
elseif errors then
|
||||
for i, error in ipairs(errors) do
|
||||
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -48,37 +58,37 @@ set_by_lua_block $dummy_set {
|
|||
logger:log(ngx.INFO, "calling set() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.set) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has set method
|
||||
if plugin_lua.set ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua, ctx)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua, ctx)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "set")
|
||||
local ok, ret = call_plugin(plugin_obj, "set")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":set() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":set() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":set() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":set() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method set() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method set() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called set() methods of plugins")
|
||||
logger:log(INFO, "called set() methods of plugins")
|
||||
|
||||
-- Save ctx
|
||||
helpers.save_ctx(ctx)
|
||||
save_ctx(ctx)
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,51 +27,63 @@ ssl_certificate_by_lua_block {
|
|||
local cjson = require "cjson"
|
||||
local ssl = require "ngx.ssl"
|
||||
|
||||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local is_internal = ngx_req.is_internal
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local set_cert = ssl.set_cert
|
||||
local set_priv_key = ssl.set_priv_key
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local tostring = tostring
|
||||
|
||||
-- Start ssl_certificate phase
|
||||
local logger = clogger:new("SSL-CERTIFICATE")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "ssl_certificate phase started")
|
||||
logger:log(INFO, "ssl_certificate phase started")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call ssl_certificate() methods
|
||||
logger:log(ngx.INFO, "calling ssl_certificate() methods of plugins ...")
|
||||
logger:log(INFO, "calling ssl_certificate() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.ssl_certificate) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has ssl_certificate method
|
||||
if plugin_lua.ssl_certificate ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "ssl_certificate")
|
||||
local ok, ret = call_plugin(plugin_obj, "ssl_certificate")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":ssl_certificate() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":ssl_certificate() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":ssl_certificate() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":ssl_certificate() call successful : " .. ret.msg)
|
||||
if ret.status then
|
||||
logger:log(ngx.INFO, plugin_id .. " is setting certificate/key : " .. ret.msg)
|
||||
local ok, err = ssl.set_cert(ret.status[1])
|
||||
logger:log(INFO, plugin_id .. " is setting certificate/key : " .. ret.msg)
|
||||
local ok, err = set_cert(ret.status[1])
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while setting certificate : " .. err)
|
||||
logger:log(ERR, "error while setting certificate : " .. err)
|
||||
else
|
||||
local ok, err = ssl.set_priv_key(ret.status[2])
|
||||
local ok, err = set_priv_key(ret.status[2])
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while setting private key : " .. err)
|
||||
logger:log(ERR, "error while setting private key : " .. err)
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
|
@ -80,13 +92,13 @@ ssl_certificate_by_lua_block {
|
|||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method ssl_certificate() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method ssl_certificate() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called ssl_certificate() methods of plugins")
|
||||
logger:log(INFO, "called ssl_certificate() methods of plugins")
|
||||
|
||||
logger:log(ngx.INFO, "ssl_certificate phase ended")
|
||||
logger:log(INFO, "ssl_certificate phase ended")
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,30 @@ local plugin = require "bunkerweb.plugin"
|
|||
local sha256 = require "resty.sha256"
|
||||
local str = require "resty.string"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local ngx = ngx
|
||||
local subsystem = ngx.config.subsystem
|
||||
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
|
||||
local OK = ngx.OK
|
||||
local tonumber = tonumber
|
||||
local tostring = tostring
|
||||
local get_session = utils.get_session
|
||||
local get_session_data = utils.get_session_data
|
||||
local set_session_data = utils.set_session_data
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local rand = utils.rand
|
||||
local now = ngx.now
|
||||
local captcha_new = captcha.new
|
||||
local base64_encode = base64.encode
|
||||
local to_hex = str.to_hex
|
||||
local http_new = http.new
|
||||
local decode = cjson.decode
|
||||
|
||||
local template = nil
|
||||
if ngx.shared.datastore then
|
||||
local render = nil
|
||||
if subsystem == "http" then
|
||||
template = require "resty.template"
|
||||
render = template.render
|
||||
end
|
||||
|
||||
local antibot = class("antibot", plugin)
|
||||
|
|
@ -30,12 +51,12 @@ function antibot:header()
|
|||
end
|
||||
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot", self.ctx)
|
||||
local session, err = get_session("antibot", self.ctx)
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't get session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session, true, self.ctx)
|
||||
self.session_data = get_session_data(self.session, true, self.ctx)
|
||||
-- Check if session is valid
|
||||
self:check_session()
|
||||
|
||||
|
|
@ -48,7 +69,7 @@ function antibot:header()
|
|||
end
|
||||
|
||||
if self.ctx.bw.uri ~= self.variables["ANTIBOT_URI"] then
|
||||
return self:ret(true, "Not antibot uri")
|
||||
return self:ret(true, "not antibot uri")
|
||||
end
|
||||
|
||||
local header = "Content-Security-Policy"
|
||||
|
|
@ -97,12 +118,12 @@ function antibot:access()
|
|||
end
|
||||
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot", self.ctx)
|
||||
local session, err = get_session("antibot", self.ctx)
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't get session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session, true, self.ctx)
|
||||
self.session_data = get_session_data(self.session, true, self.ctx)
|
||||
-- Check if session is valid
|
||||
self:check_session()
|
||||
|
||||
|
|
@ -118,7 +139,7 @@ function antibot:access()
|
|||
self:prepare_challenge()
|
||||
local ok, err = self:set_session_data()
|
||||
if not ok then
|
||||
return self:ret(false, "can't save session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't save session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
|
||||
-- Redirect to challenge page
|
||||
|
|
@ -143,10 +164,10 @@ function antibot:access()
|
|||
local ok, err, redirect = self:check_challenge()
|
||||
local set_ok, set_err = self:set_session_data()
|
||||
if not set_ok then
|
||||
return self:ret(false, "can't save session : " .. set_err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't save session : " .. set_err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
if ok == nil then
|
||||
return self:ret(false, "check challenge error : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "check challenge error : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
elseif not ok then
|
||||
self.logger:log(ngx.WARN, "client failed challenge : " .. err)
|
||||
end
|
||||
|
|
@ -156,14 +177,14 @@ function antibot:access()
|
|||
self:prepare_challenge()
|
||||
ok, err = self:set_session_data()
|
||||
if not ok then
|
||||
return self:ret(false, "can't save session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't save session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.ctx.bw.antibot_display_content = true
|
||||
return self:ret(true, "displaying challenge to client", ngx.OK)
|
||||
return self:ret(true, "displaying challenge to client", OK)
|
||||
end
|
||||
|
||||
-- Method is suspicious, let's deny the request
|
||||
return self:ret(true, "unsupported HTTP method for antibot", utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "unsupported HTTP method for antibot", get_deny_status())
|
||||
end
|
||||
|
||||
function antibot:content()
|
||||
|
|
@ -178,12 +199,12 @@ function antibot:content()
|
|||
end
|
||||
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot", self.ctx)
|
||||
local session, err = get_session("antibot", self.ctx)
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't get session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session, true, self.ctx)
|
||||
self.session_data = get_session_data(self.session, true, self.ctx)
|
||||
|
||||
-- Direct access without session
|
||||
if not self.session_data.prepared then
|
||||
|
|
@ -228,7 +249,7 @@ end
|
|||
|
||||
function antibot:set_session_data()
|
||||
if self.session_updated then
|
||||
local ok, err = utils.set_session_data(self.session, self.session_data, true, self.ctx)
|
||||
local ok, err = set_session_data(self.session, self.session_data, true, self.ctx)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
|
|
@ -246,18 +267,18 @@ function antibot:prepare_challenge()
|
|||
self.session_data.type = self.variables["USE_ANTIBOT"]
|
||||
self.session_data.resolved = false
|
||||
self.session_data.original_uri = self.ctx.bw.request_uri
|
||||
self.session_data.nonce_script = utils.rand(16)
|
||||
self.session_data.nonce_style = utils.rand(16)
|
||||
self.session_data.nonce_script = rand(16)
|
||||
self.session_data.nonce_style = rand(16)
|
||||
if self.ctx.bw.uri == self.variables["ANTIBOT_URI"] then
|
||||
self.session_data.original_uri = "/"
|
||||
end
|
||||
if self.session_data.type == "cookie" then
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
elseif self.session_data.type == "javascript" then
|
||||
self.session_data.random = utils.rand(20)
|
||||
self.session_data.random = rand(20)
|
||||
elseif self.session_data.type == "captcha" then
|
||||
self.session_data.captcha = utils.rand(6, true)
|
||||
self.session_data.captcha = rand(6, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -282,11 +303,11 @@ function antibot:display_challenge()
|
|||
|
||||
-- Captcha case
|
||||
if self.session_data.type == "captcha" then
|
||||
local chall_captcha = captcha.new()
|
||||
local chall_captcha = captcha_new()
|
||||
chall_captcha:font("/usr/share/bunkerweb/core/antibot/files/font.ttf")
|
||||
chall_captcha:string(self.session_data.captcha)
|
||||
chall_captcha:generate()
|
||||
template_vars.captcha = base64.encode(chall_captcha:jpegStr(70))
|
||||
template_vars.captcha = base64_encode(chall_captcha:jpegStr(70))
|
||||
end
|
||||
|
||||
-- reCAPTCHA case
|
||||
|
|
@ -305,7 +326,7 @@ function antibot:display_challenge()
|
|||
end
|
||||
|
||||
-- Render content
|
||||
template.render(self.session_data.type .. ".html", template_vars)
|
||||
render(self.session_data.type .. ".html", template_vars)
|
||||
|
||||
return true, "displayed challenge"
|
||||
end
|
||||
|
|
@ -317,33 +338,35 @@ function antibot:check_challenge()
|
|||
end
|
||||
|
||||
local resolved
|
||||
|
||||
local ngx_req = ngx.req
|
||||
local read_body = ngx_req.read_body
|
||||
local get_post_args = ngx_req.get_post_args
|
||||
self.session_data.prepared = false
|
||||
self.session_updated = true
|
||||
|
||||
-- Javascript case
|
||||
if self.session_data.type == "javascript" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["challenge"] then
|
||||
return nil, "missing challenge arg"
|
||||
end
|
||||
local hash = sha256:new()
|
||||
hash:update(self.session_data.random .. args["challenge"])
|
||||
local digest = hash:final()
|
||||
resolved = str.to_hex(digest):find("^0000") ~= nil
|
||||
resolved = to_hex(digest):find("^0000") ~= nil
|
||||
if not resolved then
|
||||
return false, "wrong value"
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- Captcha case
|
||||
if self.session_data.type == "captcha" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["captcha"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
|
|
@ -351,18 +374,18 @@ function antibot:check_challenge()
|
|||
return false, "wrong value", nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- reCAPTCHA case
|
||||
if self.session_data.type == "recaptcha" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return nil, "can't instantiate http object : " .. err, nil, nil
|
||||
end
|
||||
|
|
@ -382,7 +405,7 @@ function antibot:check_challenge()
|
|||
if not res then
|
||||
return nil, "can't send request to reCAPTCHA API : " .. err, nil
|
||||
end
|
||||
local ok, rdata = pcall(cjson.decode, res.body)
|
||||
local ok, rdata = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from reCAPTCHA API : " .. rdata, nil
|
||||
end
|
||||
|
|
@ -390,18 +413,18 @@ function antibot:check_challenge()
|
|||
return false, "client failed challenge with score " .. tostring(rdata.score), nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- hCaptcha case
|
||||
if self.session_data.type == "hcaptcha" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return nil, "can't instantiate http object : " .. err, nil, nil
|
||||
end
|
||||
|
|
@ -421,7 +444,7 @@ function antibot:check_challenge()
|
|||
if not res then
|
||||
return nil, "can't send request to hCaptcha API : " .. err, nil
|
||||
end
|
||||
local ok, hdata = pcall(cjson.decode, res.body)
|
||||
local ok, hdata = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from hCaptcha API : " .. hdata, nil
|
||||
end
|
||||
|
|
@ -429,18 +452,18 @@ function antibot:check_challenge()
|
|||
return false, "client failed challenge", nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- Turnstile case
|
||||
if self.session_data.type == "turnstile" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return nil, "can't instantiate http object : " .. err, nil, nil
|
||||
end
|
||||
|
|
@ -460,7 +483,7 @@ function antibot:check_challenge()
|
|||
if not res then
|
||||
return nil, "can't send request to Turnstile API : " .. err, nil
|
||||
end
|
||||
local ok, tdata = pcall(cjson.decode, res.body)
|
||||
local ok, tdata = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from Turnstile API : " .. tdata, nil
|
||||
end
|
||||
|
|
@ -468,7 +491,7 @@ function antibot:check_challenge()
|
|||
return false, "client failed challenge", nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,22 +5,30 @@ location {{ ANTIBOT_URI }} {
|
|||
content_by_lua_block {
|
||||
local logger = require "bunkerweb.logger":new("ANTIBOT")
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local save_ctx = helpers.save_ctx
|
||||
local tostring = tostring
|
||||
|
||||
local ok, ret, errors, ctx = fill_ctx()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
|
||||
logger:log(ERR, "fill_ctx() failed : " .. ret)
|
||||
elseif errors then
|
||||
for i, error in ipairs(errors) do
|
||||
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
|
||||
end
|
||||
end
|
||||
local antibot = require "antibot.antibot":new(ctx)
|
||||
local ret = antibot:content()
|
||||
if not ret.ret then
|
||||
logger:log(ngx.ERR, "antibot:content() failed : " .. ret.msg)
|
||||
logger:log(ERR, "antibot:content() failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, "antibot:content() success : " .. ret.msg)
|
||||
logger:log(INFO, "antibot:content() success : " .. ret.msg)
|
||||
end
|
||||
ngx.ctx = ctx
|
||||
save_ctx(ctx)
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local badbehavior = class("badbehavior", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local WARN = ngx.WARN
|
||||
local NOTICE = ngx.NOTICE
|
||||
local timer_at = ngx.timer.at
|
||||
local add_ban = utils.add_ban
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local is_banned = utils.is_banned
|
||||
local tostring = tostring
|
||||
|
||||
function badbehavior:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "badbehavior", ctx)
|
||||
|
|
@ -11,7 +21,7 @@ end
|
|||
|
||||
function badbehavior:log()
|
||||
-- Check if we are whitelisted
|
||||
if self.ctx.bw.is_whitelisted == "yes" then
|
||||
if is_whitelisted(self.ctx) == "yes" then
|
||||
return self:ret(true, "client is whitelisted")
|
||||
end
|
||||
-- Check if bad behavior is activated
|
||||
|
|
@ -23,11 +33,11 @@ function badbehavior:log()
|
|||
return self:ret(true, "not increasing counter")
|
||||
end
|
||||
-- Check if we are already banned
|
||||
if self.ctx.bw.is_banned then
|
||||
if is_banned(self.ctx.bw.remote_addr) then
|
||||
return self:ret(true, "already banned")
|
||||
end
|
||||
-- Call increase function later and with cosocket enabled
|
||||
local ok, err = ngx.timer.at(
|
||||
local ok, err = timer_at(
|
||||
0,
|
||||
badbehavior.increase,
|
||||
self.ctx.bw.remote_addr,
|
||||
|
|
@ -55,13 +65,14 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("badbehavior")
|
||||
local datastore = require "bunkerweb.datastore":new()
|
||||
|
||||
-- Declare counter
|
||||
local counter = false
|
||||
-- Redis case
|
||||
if use_redis then
|
||||
local redis_counter, err = badbehavior.redis_increase(ip, count_time, ban_time)
|
||||
if not redis_counter then
|
||||
logger:log(ngx.ERR, "(increase) redis_increase failed, falling back to local : " .. err)
|
||||
logger:log(ERR, "(increase) redis_increase failed, falling back to local : " .. err)
|
||||
else
|
||||
counter = redis_counter
|
||||
end
|
||||
|
|
@ -70,7 +81,7 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
if not counter then
|
||||
local local_counter, err = datastore:get("plugin_badbehavior_count_" .. ip)
|
||||
if not local_counter and err ~= "not found" then
|
||||
logger:log(ngx.ERR, "(increase) can't get counts from the datastore : " .. err)
|
||||
logger:log(ERR, "(increase) can't get counts from the datastore : " .. err)
|
||||
end
|
||||
if local_counter == nil then
|
||||
local_counter = 0
|
||||
|
|
@ -78,25 +89,25 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
counter = local_counter + 1
|
||||
end
|
||||
-- Call decrease later
|
||||
local ok, err = ngx.timer.at(count_time, badbehavior.decrease, ip, count_time, threshold, use_redis)
|
||||
local ok, err = timer_at(count_time, badbehavior.decrease, ip, count_time, threshold, use_redis)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(increase) can't create decrease timer : " .. err)
|
||||
logger:log(ERR, "(increase) can't create decrease timer : " .. err)
|
||||
end
|
||||
-- Store local counter
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter, count_time)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(increase) can't save counts to the datastore : " .. err)
|
||||
logger:log(ERR, "(increase) can't save counts to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
-- Store local ban
|
||||
if counter > threshold then
|
||||
ok, err = utils.add_ban(ip, "bad behavior", ban_time)
|
||||
ok, err = add_ban(ip, "bad behavior", ban_time)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(increase) can't save ban : " .. err)
|
||||
logger:log(ERR, "(increase) can't save ban : " .. err)
|
||||
return
|
||||
end
|
||||
logger:log(
|
||||
ngx.WARN,
|
||||
WARN,
|
||||
"IP "
|
||||
.. ip
|
||||
.. " is banned for "
|
||||
|
|
@ -109,7 +120,7 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
)
|
||||
end
|
||||
logger:log(
|
||||
ngx.NOTICE,
|
||||
NOTICE,
|
||||
"increased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||
)
|
||||
end
|
||||
|
|
@ -124,7 +135,7 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
|||
if use_redis then
|
||||
local redis_counter, err = badbehavior.redis_decrease(ip, count_time)
|
||||
if not redis_counter then
|
||||
logger:log(ngx.ERR, "(decrease) redis_decrease failed, falling back to local : " .. err)
|
||||
logger:log(ERR, "(decrease) redis_decrease failed, falling back to local : " .. err)
|
||||
else
|
||||
counter = redis_counter
|
||||
end
|
||||
|
|
@ -133,7 +144,7 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
|||
if not counter then
|
||||
local local_counter, err = datastore:get("plugin_badbehavior_count_" .. ip)
|
||||
if not local_counter and err ~= "not found" then
|
||||
logger:log(ngx.ERR, "(decrease) can't get counts from the datastore : " .. err)
|
||||
logger:log(ERR, "(decrease) can't get counts from the datastore : " .. err)
|
||||
end
|
||||
if local_counter == nil or local_counter <= 1 then
|
||||
counter = 0
|
||||
|
|
@ -148,19 +159,19 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
|||
else
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter, count_time)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(decrease) can't save counts to the datastore : " .. err)
|
||||
logger:log(ERR, "(decrease) can't save counts to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
end
|
||||
logger:log(
|
||||
ngx.NOTICE,
|
||||
NOTICE,
|
||||
"decreased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||
)
|
||||
end
|
||||
|
||||
function badbehavior.redis_increase(ip, count_time, ban_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_incr = redis.pcall("INCR", KEYS[1])
|
||||
|
|
@ -201,7 +212,7 @@ end
|
|||
|
||||
function badbehavior.redis_decrease(ip, count_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_decr = redis.pcall("DECR", KEYS[1])
|
||||
|
|
|
|||
|
|
@ -5,14 +5,26 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local blacklist = class("blacklist", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local get_rdns = utils.get_rdns
|
||||
local get_asn = utils.get_asn
|
||||
local regex_match = utils.regex_match
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
|
||||
function blacklist:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "blacklist", ctx)
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_blacklist_lists", true)
|
||||
if not lists then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
self.lists = {}
|
||||
else
|
||||
self.lists = lists
|
||||
|
|
@ -50,9 +62,9 @@ function blacklist:is_needed()
|
|||
return self.variables["USE_BLACKLIST"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_BLACKLIST", "yes")
|
||||
local is_needed, err = has_variable("USE_BLACKLIST", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_BLACKLIST variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_BLACKLIST variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -78,7 +90,7 @@ function blacklist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(blacklists) do
|
||||
local f, _ = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
||||
local f, _ = open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(blacklists[kind], line)
|
||||
|
|
@ -118,12 +130,12 @@ function blacklist:access()
|
|||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
self.logger:log(ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ok" then
|
||||
return self:ret(
|
||||
true,
|
||||
k .. " is in cached blacklist (info : " .. cached .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
if ok and cached then
|
||||
|
|
@ -139,18 +151,18 @@ function blacklist:access()
|
|||
if not already_cached[k] then
|
||||
local ok, blacklisted = self:is_blacklisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is blacklisted : " .. blacklisted)
|
||||
self.logger:log(ERR, "error while checking if " .. k .. " is blacklisted : " .. blacklisted)
|
||||
else
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), blacklisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
self.logger:log(ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if blacklisted ~= "ok" then
|
||||
return self:ret(
|
||||
true,
|
||||
k .. " is blacklisted (info : " .. blacklisted .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -204,7 +216,7 @@ end
|
|||
|
||||
function blacklist:is_blacklisted_ip()
|
||||
-- Check if IP is in ignore list
|
||||
local ipm, err = ipmatcher.new(self.lists["IGNORE_IP"])
|
||||
local ipm, err = ipmatcher_new(self.lists["IGNORE_IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -235,7 +247,7 @@ function blacklist:is_blacklisted_ip()
|
|||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
local rdns_list, err = get_rdns(self.ctx.bw.remote_addr, self.ctx, true)
|
||||
if rdns_list then
|
||||
-- Check if rDNS is in ignore list
|
||||
local ignore = false
|
||||
|
|
@ -258,13 +270,13 @@ function blacklist:is_blacklisted_ip()
|
|||
end
|
||||
end
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns : " .. err)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in ignore list
|
||||
if self.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(self.ctx.bw.remote_addr)
|
||||
local asn, err = get_asn(self.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
|
|
@ -294,7 +306,7 @@ function blacklist:is_blacklisted_uri()
|
|||
-- Check if URI is in ignore list
|
||||
local ignore = false
|
||||
for _, ignore_uri in ipairs(self.lists["IGNORE_URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, ignore_uri) then
|
||||
if regex_match(self.ctx.bw.uri, ignore_uri) then
|
||||
ignore = true
|
||||
break
|
||||
end
|
||||
|
|
@ -302,7 +314,7 @@ function blacklist:is_blacklisted_uri()
|
|||
-- Check if URI is in blacklist
|
||||
if not ignore then
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
if regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
end
|
||||
|
|
@ -315,7 +327,7 @@ function blacklist:is_blacklisted_ua()
|
|||
-- Check if UA is in ignore list
|
||||
local ignore = false
|
||||
for _, ignore_ua in ipairs(self.lists["IGNORE_USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ignore_ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ignore_ua) then
|
||||
ignore = true
|
||||
break
|
||||
end
|
||||
|
|
@ -323,7 +335,7 @@ function blacklist:is_blacklisted_ua()
|
|||
-- Check if UA is in blacklist
|
||||
if not ignore then
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,18 +6,41 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local bunkernet = class("bunkernet", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local WARN = ngx.WARN
|
||||
local timer_at = ngx.timer.at
|
||||
local get_phase = ngx.get_phase
|
||||
local get_version = utils.get_version
|
||||
local get_integration = utils.get_integration
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local is_ipv4 = utils.is_ipv4
|
||||
local is_ipv6 = utils.is_ipv6
|
||||
local ip_is_global = utils.ip_is_global
|
||||
local has_variable = utils.has_variable
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local is_ip_in_networks = utils.is_ip_in_networks
|
||||
local get_reason = utils.get_reason
|
||||
local get_variable = utils.get_variable
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
local encode = cjson.encode
|
||||
local decode = cjson.decode
|
||||
local http_new = http.new
|
||||
|
||||
function bunkernet:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "bunkernet", ctx)
|
||||
-- Get BunkerNet ID and save info
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local id, err = self.datastore:get("plugin_bunkernet_id", true)
|
||||
if id then
|
||||
self.bunkernet_id = id
|
||||
self.version = (self.ctx and self.ctx.bw.version) or utils.get_version()
|
||||
self.integration = (self.ctx and self.ctx.bw.integration) or utils.get_integration()
|
||||
self.version = get_version(self.ctx)
|
||||
self.integration = get_integration(self.ctx)
|
||||
else
|
||||
self.logger:log(ngx.ERR, "can't get BunkerNet ID from datastore : " .. err)
|
||||
self.logger:log(ERR, "can't get BunkerNet ID from datastore : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -32,9 +55,9 @@ function bunkernet:is_needed()
|
|||
return self.variables["USE_BUNKERNET"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_BUNKERNET", "yes")
|
||||
local is_needed, err = has_variable("USE_BUNKERNET", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_BUNKERNET variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_BUNKERNET variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -59,7 +82,7 @@ function bunkernet:init_worker()
|
|||
"received status " .. tostring(status) .. " from API using instance ID " .. self.bunkernet_id
|
||||
)
|
||||
end
|
||||
self.logger:log(ngx.NOTICE, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||
self.logger:log(NOTICE, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||
return self:ret(true, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||
end
|
||||
|
||||
|
|
@ -69,7 +92,7 @@ function bunkernet:init()
|
|||
return self:ret(true, "no service uses BunkerNet, skipping init")
|
||||
end
|
||||
-- Check if instance ID is present
|
||||
local f, err = io.open("/var/cache/bunkerweb/bunkernet/instance.id", "r")
|
||||
local f, err = open("/var/cache/bunkerweb/bunkernet/instance.id", "r")
|
||||
if not f then
|
||||
return self:ret(false, "can't read instance id : " .. err)
|
||||
end
|
||||
|
|
@ -87,12 +110,12 @@ function bunkernet:init()
|
|||
local db = {
|
||||
ip = {},
|
||||
}
|
||||
local f, err = io.open("/var/cache/bunkerweb/bunkernet/ip.list", "r")
|
||||
local f, err = open("/var/cache/bunkerweb/bunkernet/ip.list", "r")
|
||||
if not f then
|
||||
ret = false
|
||||
else
|
||||
for line in f:lines() do
|
||||
if (utils.is_ipv4(line) or utils.is_ipv6(line)) and utils.ip_is_global(line) then
|
||||
if (is_ipv4(line) or is_ipv6(line)) and ip_is_global(line) then
|
||||
table.insert(db.ip, line)
|
||||
i = i + 1
|
||||
end
|
||||
|
|
@ -123,7 +146,7 @@ function bunkernet:access()
|
|||
return self:ret(true, "IP is not global")
|
||||
end
|
||||
-- Check if whitelisted
|
||||
if self.ctx.bw.is_whitelisted == "yes" then
|
||||
if is_whitelisted(self.ctx) then
|
||||
return self:ret(true, "client is whitelisted")
|
||||
end
|
||||
-- Extract DB
|
||||
|
|
@ -132,12 +155,12 @@ function bunkernet:access()
|
|||
-- Check if is IP is present
|
||||
if #db.ip > 0 then
|
||||
-- luacheck: ignore 421
|
||||
local present, err = utils.is_ip_in_networks(self.ctx.bw.remote_addr, db.ip)
|
||||
local present, err = is_ip_in_networks(self.ctx.bw.remote_addr, db.ip)
|
||||
if present == nil then
|
||||
return self:ret(false, "can't check if ip is in db : " .. err)
|
||||
end
|
||||
if present then
|
||||
return self:ret(true, "ip is in db", utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "ip is in db", get_deny_status())
|
||||
end
|
||||
end
|
||||
else
|
||||
|
|
@ -158,7 +181,7 @@ function bunkernet:log(bypass_checks)
|
|||
end
|
||||
end
|
||||
-- Check if IP has been blocked
|
||||
local reason = utils.get_reason(self.ctx)
|
||||
local reason = get_reason(self.ctx)
|
||||
if not reason then
|
||||
return self:ret(true, "ip is not blocked")
|
||||
end
|
||||
|
|
@ -169,20 +192,30 @@ function bunkernet:log(bypass_checks)
|
|||
if not self.ctx.bw.ip_is_global then
|
||||
return self:ret(true, "IP is not global")
|
||||
end
|
||||
-- TODO : check if IP has been reported recently
|
||||
-- Check if IP has been reported recently
|
||||
local ok, data = self.cachestore:get("plugin_bunkernet_" .. self.ctx.bw.remote_addr .. "_" .. reason)
|
||||
if not ok then
|
||||
self.logger:log(ERR, "can't check cachestore : " .. data)
|
||||
elseif data then
|
||||
return self:ret(true, "already reported recently")
|
||||
end
|
||||
-- luacheck: ignore 212 431
|
||||
local function report_callback(premature, obj, ip, reason, method, url, headers)
|
||||
local function report_callback(premature, obj, ip, reason, method, url, headers, use_redis)
|
||||
local ok, err, status, _ = obj:report(ip, reason, method, url, headers)
|
||||
if status == 429 then
|
||||
obj.logger:log(ngx.WARN, "bunkernet API is rate limiting us")
|
||||
obj.logger:log(WARN, "bunkernet API is rate limiting us")
|
||||
elseif not ok then
|
||||
obj.logger:log(ngx.ERR, "can't report IP : " .. err)
|
||||
obj.logger:log(ERR, "can't report IP : " .. err)
|
||||
else
|
||||
obj.logger:log(ngx.NOTICE, "successfully reported IP " .. ip .. " (reason : " .. reason .. ")")
|
||||
obj.logger:log(NOTICE, "successfully reported IP " .. ip .. " (reason : " .. reason .. ")")
|
||||
local cachestore = require "bunkerweb.cachestore":new(use_redis, nil, true)
|
||||
local ok, err = cachestore:set("plugin_bunkernet_" .. ip .. "_" .. reason)
|
||||
if not ok then
|
||||
obj.logger:log(ERR, "error from cachestore : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local hdr, err = ngx.timer.at(
|
||||
local hdr, err = timer_at(
|
||||
0,
|
||||
report_callback,
|
||||
self,
|
||||
|
|
@ -208,7 +241,7 @@ function bunkernet:log_default()
|
|||
return self:ret(false, "missing instance ID")
|
||||
end
|
||||
-- Check if default server is disabled
|
||||
local check, err = utils.get_variable("DISABLE_DEFAULT_SERVER", false)
|
||||
local check, err = get_variable("DISABLE_DEFAULT_SERVER", false)
|
||||
if check == nil then
|
||||
return self:ret(false, "error while getting variable DISABLE_DEFAULT_SERVER : " .. err)
|
||||
end
|
||||
|
|
@ -224,7 +257,7 @@ function bunkernet:log_stream()
|
|||
end
|
||||
|
||||
function bunkernet:request(method, url, data)
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return false, "can't instantiate http object : " .. err
|
||||
end
|
||||
|
|
@ -240,7 +273,7 @@ function bunkernet:request(method, url, data)
|
|||
end
|
||||
local res, err = httpc:request_uri(self.variables["BUNKERNET_SERVER"] .. url, {
|
||||
method = method,
|
||||
body = cjson.encode(all_data),
|
||||
body = encode(all_data),
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
["User-Agent"] = "BunkerWeb/" .. self.version,
|
||||
|
|
@ -253,7 +286,7 @@ function bunkernet:request(method, url, data)
|
|||
if res.status ~= 200 then
|
||||
return false, "status code != 200", res.status, nil
|
||||
end
|
||||
local ok, ret = pcall(cjson.decode, res.body)
|
||||
local ok, ret = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return false, "error while decoding json : " .. ret
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local cors = class("cors", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local HTTP_NO_CONTENT = ngx.HTTP_NO_CONTENT
|
||||
local WARN = ngx.WARN
|
||||
local regex_match = utils.regex_match
|
||||
local get_deny_status = utils.get_deny_status
|
||||
|
||||
function cors:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "cors", ctx)
|
||||
|
|
@ -31,50 +37,51 @@ function cors:header()
|
|||
return self:ret(true, "origin header not present")
|
||||
end
|
||||
-- Always include Vary header to prevent caching
|
||||
local vary = ngx.header.Vary
|
||||
local ngx_header = ngx.header
|
||||
local vary = ngx_header.Vary
|
||||
if vary then
|
||||
if type(vary) == "string" then
|
||||
ngx.header.Vary = { vary, "Origin" }
|
||||
ngx_header.Vary = { vary, "Origin" }
|
||||
else
|
||||
table.insert(vary, "Origin")
|
||||
ngx.header.Vary = vary
|
||||
ngx_header.Vary = vary
|
||||
end
|
||||
else
|
||||
ngx.header.Vary = "Origin"
|
||||
ngx_header.Vary = "Origin"
|
||||
end
|
||||
-- Check if Origin is allowed
|
||||
if
|
||||
self.ctx.bw.http_origin
|
||||
and self.variables["CORS_DENY_REQUEST"] == "yes"
|
||||
and self.variables["CORS_ALLOW_ORIGIN"] ~= "*"
|
||||
and not utils.regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
and not regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
then
|
||||
self.logger:log(ngx.WARN, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
self.logger:log(WARN, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
return self:ret(true, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
end
|
||||
-- Set headers
|
||||
if self.variables["CORS_ALLOW_ORIGIN"] == "*" then
|
||||
ngx.header["Access-Control-Allow-Origin"] = "*"
|
||||
ngx_header["Access-Control-Allow-Origin"] = "*"
|
||||
else
|
||||
ngx.header["Access-Control-Allow-Origin"] = self.ctx.bw.http_origin
|
||||
ngx_header["Access-Control-Allow-Origin"] = self.ctx.bw.http_origin
|
||||
end
|
||||
for variable, header in pairs(self.all_headers) do
|
||||
if self.variables[variable] ~= "" then
|
||||
ngx.header[header] = self.variables[variable]
|
||||
ngx_header[header] = self.variables[variable]
|
||||
end
|
||||
end
|
||||
if self.ctx.bw.request_method == "OPTIONS" then
|
||||
for variable, header in pairs(self.preflight_headers) do
|
||||
if variable == "CORS_ALLOW_CREDENTIALS" then
|
||||
if self.variables["CORS_ALLOW_CREDENTIALS"] == "yes" then
|
||||
ngx.header[header] = "true"
|
||||
ngx_header[header] = "true"
|
||||
end
|
||||
elseif self.variables[variable] ~= "" then
|
||||
ngx.header[header] = self.variables[variable]
|
||||
ngx_header[header] = self.variables[variable]
|
||||
end
|
||||
end
|
||||
ngx.header["Content-Type"] = "text/html; charset=UTF-8"
|
||||
ngx.header["Content-Length"] = "0"
|
||||
ngx_header["Content-Type"] = "text/html; charset=UTF-8"
|
||||
ngx_header["Content-Length"] = "0"
|
||||
return self:ret(true, "edited headers for preflight request")
|
||||
end
|
||||
return self:ret(true, "edited headers for standard request")
|
||||
|
|
@ -90,17 +97,17 @@ function cors:access()
|
|||
self.ctx.bw.http_origin
|
||||
and self.variables["CORS_DENY_REQUEST"] == "yes"
|
||||
and self.variables["CORS_ALLOW_ORIGIN"] ~= "*"
|
||||
and not utils.regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
and not regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
then
|
||||
return self:ret(
|
||||
true,
|
||||
"origin " .. self.ctx.bw.http_origin .. " is not allowed, denying access",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
-- Send CORS policy with a 204 (no content) status
|
||||
if self.ctx.bw.request_method == "OPTIONS" and self.ctx.bw.http_origin then
|
||||
return self:ret(true, "preflight request", ngx.HTTP_NO_CONTENT)
|
||||
return self:ret(true, "preflight request", HTTP_NO_CONTENT)
|
||||
end
|
||||
return self:ret(true, "standard request")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local country = class("country", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local get_country = utils.get_country
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local decode = cjson.decode
|
||||
local encode = cjson.encode
|
||||
|
||||
function country:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "country", ctx)
|
||||
|
|
@ -18,7 +24,7 @@ function country:access()
|
|||
-- Check if IP is in cache
|
||||
local _, data = self:is_in_cache(self.ctx.bw.remote_addr)
|
||||
if data then
|
||||
data = cjson.decode(data)
|
||||
data = decode(data)
|
||||
if data.result == "ok" then
|
||||
return self:ret(
|
||||
true,
|
||||
|
|
@ -36,7 +42,7 @@ function country:access()
|
|||
.. " is in country cache (blacklisted, country = "
|
||||
.. data.country
|
||||
.. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -50,7 +56,7 @@ function country:access()
|
|||
end
|
||||
|
||||
-- Get the country of client
|
||||
local country_data, err = utils.get_country(self.ctx.bw.remote_addr)
|
||||
local country_data, err = get_country(self.ctx.bw.remote_addr)
|
||||
if not country_data then
|
||||
return self:ret(false, "can't get country of client IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
end
|
||||
|
|
@ -78,7 +84,7 @@ function country:access()
|
|||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is not whitelisted (country = " .. country_data .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -93,7 +99,7 @@ function country:access()
|
|||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is blacklisted (country = " .. country_data .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -125,7 +131,7 @@ end
|
|||
function country:add_to_cache(ip, country_data, result)
|
||||
local ok, err = self.cachestore:set(
|
||||
"plugin_country_" .. self.ctx.bw.server_name .. ip,
|
||||
cjson.encode { country = country_data, result = result },
|
||||
encode({ country = country_data, result = result }),
|
||||
86400
|
||||
)
|
||||
if not ok then
|
||||
|
|
|
|||
|
|
@ -5,6 +5,16 @@ local ssl = require "ngx.ssl"
|
|||
|
||||
local customcert = class("customcert", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local parse_pem_cert = ssl.parse_pem_cert
|
||||
local parse_pem_priv_key = ssl.parse_pem_priv_key
|
||||
local ssl_server_name = ssl.server_name
|
||||
local get_variable = utils.get_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local has_variable = utils.has_variable
|
||||
local open = io.open
|
||||
|
||||
function customcert:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "customcert", ctx)
|
||||
|
|
@ -12,13 +22,13 @@ end
|
|||
|
||||
function customcert:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if utils.has_variable("USE_CUSTOM_SSL", "yes") then
|
||||
local multisite, err = utils.get_variable("MULTISITE", false)
|
||||
if has_variable("USE_CUSTOM_SSL", "yes") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
return self:ret(false, "can't get MULTISITE variable : " .. err)
|
||||
end
|
||||
if multisite == "yes" then
|
||||
local vars, err = utils.get_multiple_variables({"USE_CUSTOM_SSL", "SERVER_NAME"})
|
||||
local vars, err = get_multiple_variables({"USE_CUSTOM_SSL", "SERVER_NAME"})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get USE_CUSTOM_SSL variables : " .. err)
|
||||
end
|
||||
|
|
@ -26,13 +36,13 @@ function customcert:init()
|
|||
if multisite_vars["USE_CUSTOM_SSL"] == "yes" and server_name ~= "global" then
|
||||
local check, data = self:read_files(server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -40,19 +50,19 @@ function customcert:init()
|
|||
end
|
||||
end
|
||||
else
|
||||
local server_name, err = utils.get_variable("SERVER_NAME", false)
|
||||
local server_name, err = get_variable("SERVER_NAME", false)
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -65,7 +75,7 @@ function customcert:init()
|
|||
end
|
||||
|
||||
function customcert:ssl_certificate()
|
||||
local server_name, err = ssl.server_name()
|
||||
local server_name, err = ssl_server_name()
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
|
|
@ -86,7 +96,7 @@ function customcert:read_files(server_name)
|
|||
}
|
||||
local data = {}
|
||||
for i, file in ipairs(files) do
|
||||
local f, err = io.open(file, "r")
|
||||
local f, err = open(file, "r")
|
||||
if not f then
|
||||
return false, file .. " = " .. err
|
||||
end
|
||||
|
|
@ -98,12 +108,12 @@ end
|
|||
|
||||
function customcert:load_data(data, server_name)
|
||||
-- Load certificate
|
||||
local cert_chain, err = ssl.parse_pem_cert(data[1])
|
||||
local cert_chain, err = parse_pem_cert(data[1])
|
||||
if not cert_chain then
|
||||
return false, "error while parsing pem cert : " .. err
|
||||
end
|
||||
-- Load key
|
||||
local priv_key, err = ssl.parse_pem_priv_key(data[2])
|
||||
local priv_key, err = parse_pem_priv_key(data[2])
|
||||
if not priv_key then
|
||||
return false, "error while parsing pem priv key : " .. err
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,9 +5,20 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local dnsbl = class("dnsbl", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local spawn = ngx.thread.spawn
|
||||
local wait = ngx.thread.wait
|
||||
local arpa_str = resolver.arpa_str
|
||||
local get_ips = utils.get_ips
|
||||
local has_variable = utils.has_variable
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local kill_all_threads = utils.kill_all_threads
|
||||
|
||||
local is_in_dnsbl = function(addr, server)
|
||||
local request = resolver.arpa_str(addr):gsub("%.in%-addr%.arpa", ""):gsub("%.ip6%.arpa", "") .. "." .. server
|
||||
local ips, err = utils.get_ips(request, false)
|
||||
local request = arpa_str(addr):gsub("%.in%-addr%.arpa", ""):gsub("%.ip6%.arpa", "") .. "." .. server
|
||||
local ips, err = get_ips(request, false, nil, true)
|
||||
if not ips then
|
||||
return nil, server, err
|
||||
end
|
||||
|
|
@ -30,7 +41,7 @@ function dnsbl:init_worker()
|
|||
return self:ret(false, "BW is loading")
|
||||
end
|
||||
-- Check if at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_DNSBL", "yes")
|
||||
local is_needed, err = has_variable("USE_DNSBL", "yes")
|
||||
if is_needed == nil then
|
||||
return self:ret(false, "can't check USE_DNSBL variable : " .. err)
|
||||
elseif not is_needed then
|
||||
|
|
@ -40,21 +51,21 @@ function dnsbl:init_worker()
|
|||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(is_in_dnsbl, "127.0.0.2", server)
|
||||
local thread = spawn(is_in_dnsbl, "127.0.0.2", server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
for data, thread in pairs(threads) do
|
||||
-- luacheck: ignore 421
|
||||
local ok, result, server, err = ngx.thread.wait(thread)
|
||||
local ok, result, server, err = wait(thread)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while waiting thread of " .. data .. " check : " .. result)
|
||||
self.logger:log(ERR, "error while waiting thread of " .. data .. " check : " .. result)
|
||||
elseif result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
self.logger:log(ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
elseif not result then
|
||||
self.logger:log(ngx.ERR, "dnsbl check for " .. server .. " failed")
|
||||
self.logger:log(ERR, "dnsbl check for " .. server .. " failed")
|
||||
else
|
||||
self.logger:log(ngx.NOTICE, "dnsbl check for " .. server .. " is successful")
|
||||
self.logger:log(NOTICE, "dnsbl check for " .. server .. " is successful")
|
||||
end
|
||||
end
|
||||
return self:ret(true, "success")
|
||||
|
|
@ -83,14 +94,14 @@ function dnsbl:access()
|
|||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
-- Loop on DNSBL list
|
||||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(is_in_dnsbl, self.ctx.bw.remote_addr, server)
|
||||
local thread = spawn(is_in_dnsbl, self.ctx.bw.remote_addr, server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
|
|
@ -109,7 +120,7 @@ function dnsbl:access()
|
|||
end
|
||||
-- Wait for first thread
|
||||
-- luacheck: ignore 421
|
||||
local ok, result, server, err = ngx.thread.wait(unpack(wait_threads))
|
||||
local ok, result, server, err = wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
|
|
@ -120,7 +131,7 @@ function dnsbl:access()
|
|||
threads[server] = nil
|
||||
-- DNS error
|
||||
if result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
self.logger:log(ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
end
|
||||
-- IP is in DNSBL
|
||||
if result then
|
||||
|
|
@ -137,7 +148,7 @@ function dnsbl:access()
|
|||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Blacklisted by a server : add to cache and deny access
|
||||
if ret_threads then
|
||||
|
|
@ -145,7 +156,7 @@ function dnsbl:access()
|
|||
if not ok then
|
||||
return self:ret(false, "error while adding element to cache : " .. err)
|
||||
end
|
||||
return self:ret(true, "IP is blacklisted by " .. ret_server, utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "IP is blacklisted by " .. ret_server, get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
|
||||
local ngx = ngx
|
||||
local subsystem = ngx.config.subsystem
|
||||
|
||||
local template = nil
|
||||
if ngx.shared.datastore then
|
||||
local render = nil
|
||||
if subsystem == "http" then
|
||||
template = require "resty.template"
|
||||
render = template.render
|
||||
end
|
||||
|
||||
local errors = class("errors", plugin)
|
||||
|
|
@ -65,7 +71,7 @@ end
|
|||
|
||||
function errors:render_template(code)
|
||||
-- Render template
|
||||
template.render("error.html", {
|
||||
render("error.html", {
|
||||
title = code .. " - " .. self.default_errors[code].title,
|
||||
error_title = self.default_errors[code].title,
|
||||
error_code = code,
|
||||
|
|
|
|||
|
|
@ -5,14 +5,26 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local greylist = class("greylist", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local get_rdns = utils.get_rdns
|
||||
local get_asn = utils.get_asn
|
||||
local regex_match = utils.regex_match
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
|
||||
function greylist:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "greylist", ctx)
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_greylist_lists", true)
|
||||
if not lists then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
self.lists = {}
|
||||
else
|
||||
self.lists = lists
|
||||
|
|
@ -45,9 +57,9 @@ function greylist:is_needed()
|
|||
return self.variables["USE_GREYLIST"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_GREYLIST", "yes")
|
||||
local is_needed, err = has_variable("USE_GREYLIST", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_GREYLIST variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_GREYLIST variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -67,7 +79,7 @@ function greylist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(greylists) do
|
||||
local f, _ = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
||||
local f, _ = open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(greylists[kind], line)
|
||||
|
|
@ -107,7 +119,7 @@ function greylist:access()
|
|||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
self.logger:log(ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ko" then
|
||||
return self:ret(true, k .. " is in cached greylist (info : " .. cached .. ")")
|
||||
end
|
||||
|
|
@ -124,12 +136,12 @@ function greylist:access()
|
|||
if not already_cached[k] then
|
||||
local ok, greylisted = self:is_greylisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is greylisted : " .. greylisted)
|
||||
self.logger:log(ERR, "error while checking if " .. k .. " is greylisted : " .. greylisted)
|
||||
else
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), greylisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
self.logger:log(ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if greylisted ~= "ko" then
|
||||
return self:ret(true, k .. " is in greylist")
|
||||
|
|
@ -139,7 +151,7 @@ function greylist:access()
|
|||
end
|
||||
|
||||
-- Return
|
||||
return self:ret(true, "not in greylist", utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "not in greylist", get_deny_status())
|
||||
end
|
||||
|
||||
function greylist:preread()
|
||||
|
|
@ -169,7 +181,7 @@ end
|
|||
|
||||
function greylist:is_greylisted_ip()
|
||||
-- Check if IP is in greylist
|
||||
local ipm, err = ipmatcher.new(self.lists["IP"])
|
||||
local ipm, err = ipmatcher_new(self.lists["IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -189,7 +201,7 @@ function greylist:is_greylisted_ip()
|
|||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
local rdns_list, err = get_rdns(self.ctx.bw.remote_addr, self.ctx, true)
|
||||
-- Check if rDNS is in greylist
|
||||
if rdns_list then
|
||||
for _, rdns in ipairs(rdns_list) do
|
||||
|
|
@ -200,15 +212,15 @@ function greylist:is_greylisted_ip()
|
|||
end
|
||||
end
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns : " .. err)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in greylist
|
||||
if self.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(self.ctx.bw.remote_addr)
|
||||
local asn, err = get_asn(self.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
self.logger:log(ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
|
|
@ -225,7 +237,7 @@ end
|
|||
function greylist:is_greylisted_uri()
|
||||
-- Check if URI is in greylist
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
if regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
end
|
||||
|
|
@ -236,7 +248,7 @@ end
|
|||
function greylist:is_greylisted_ua()
|
||||
-- Check if UA is in greylist
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,13 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local headers = class("headers", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local regex_match = utils.regex_match
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local tostring = tostring
|
||||
|
||||
function headers:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "headers", ctx)
|
||||
|
|
@ -18,11 +25,11 @@ function headers:initialize(ctx)
|
|||
["X_XSS_PROTECTION"] = "X-XSS-Protection",
|
||||
}
|
||||
-- Load data from datastore if needed
|
||||
if ngx.get_phase() ~= "init" then
|
||||
if get_phase() ~= "init" then
|
||||
-- Get custom headers from datastore
|
||||
local custom_headers, err = self.datastore:get("plugin_headers_custom_headers", true)
|
||||
if not custom_headers then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
return
|
||||
end
|
||||
self.custom_headers = {}
|
||||
|
|
@ -43,7 +50,7 @@ end
|
|||
|
||||
function headers:init()
|
||||
-- Get variables
|
||||
local variables, err = utils.get_multiple_variables({ "CUSTOM_HEADER" })
|
||||
local variables, err = get_multiple_variables({ "CUSTOM_HEADER" })
|
||||
if variables == nil then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
|
|
@ -55,7 +62,7 @@ function headers:init()
|
|||
if data[srv] == nil then
|
||||
data[srv] = {}
|
||||
end
|
||||
local m = utils.regex_match(value, "([\\w-]+): ([^,]+)")
|
||||
local m = regex_match(value, "([\\w-]+): ([^,]+)")
|
||||
if m then
|
||||
data[srv][m[1]] = m[2]
|
||||
end
|
||||
|
|
@ -72,14 +79,15 @@ end
|
|||
|
||||
function headers:header()
|
||||
-- Override upstream headers if needed
|
||||
local ngx_header = ngx.header
|
||||
local ssl = self.ctx.bw.scheme == "https"
|
||||
for variable, header in pairs(self.all_headers) do
|
||||
if
|
||||
ngx.header[header] == nil
|
||||
ngx_header[header] == nil
|
||||
or (
|
||||
self.variables[variable] ~= ""
|
||||
and self.variables["KEEP_UPSTREAM_HEADERS"] ~= "*"
|
||||
and utils.regex_match(self.variables["KEEP_UPSTREAM_HEADERS"], "(^| )" .. header .. "($| )") == nil
|
||||
and regex_match(self.variables["KEEP_UPSTREAM_HEADERS"], "(^| )" .. header .. "($| )") == nil
|
||||
)
|
||||
then
|
||||
if header ~= "Strict-Transport-Security" or ssl then
|
||||
|
|
@ -87,21 +95,21 @@ function headers:header()
|
|||
header == "Content-Security-Policy"
|
||||
and self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes"
|
||||
then
|
||||
ngx.header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
||||
ngx_header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
||||
else
|
||||
ngx.header[header] = self.variables[variable]
|
||||
ngx_header[header] = self.variables[variable]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Add custom headers
|
||||
for header, value in pairs(self.custom_headers) do
|
||||
ngx.header[header] = value
|
||||
ngx_header[header] = value
|
||||
end
|
||||
-- Remove headers
|
||||
if self.variables["REMOVE_HEADERS"] ~= "" then
|
||||
for header in self.variables["REMOVE_HEADERS"]:gmatch("%S+") do
|
||||
ngx.header[header] = nil
|
||||
ngx_header[header] = nil
|
||||
end
|
||||
end
|
||||
return self:ret(true, "edited headers for request")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,27 @@ local ssl = require "ngx.ssl"
|
|||
|
||||
local letsencrypt = class("letsencrypt", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local OK = ngx.OK
|
||||
local HTTP_NOT_FOUND = ngx.HTTP_NOT_FOUND
|
||||
local HTTP_OK = ngx.HTTP_OK
|
||||
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
|
||||
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
|
||||
local parse_pem_cert = ssl.parse_pem_cert
|
||||
local parse_pem_priv_key = ssl.parse_pem_priv_key
|
||||
local ssl_server_name = ssl.server_name
|
||||
local get_variable = utils.get_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local has_variable = utils.has_variable
|
||||
local open = io.open
|
||||
local sub = string.sub
|
||||
local match = string.match
|
||||
local decode = cjson.decode
|
||||
local execute = os.execute
|
||||
local remove = os.remove
|
||||
|
||||
function letsencrypt:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "letsencrypt", ctx)
|
||||
|
|
@ -13,13 +34,13 @@ end
|
|||
|
||||
function letsencrypt:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if utils.has_variable("AUTO_LETS_ENCRYPT", "yes") then
|
||||
local multisite, err = utils.get_variable("MULTISITE", false)
|
||||
if has_variable("AUTO_LETS_ENCRYPT", "yes") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
return self:ret(false, "can't get MULTISITE variable : " .. err)
|
||||
end
|
||||
if multisite == "yes" then
|
||||
local vars, err = utils.get_multiple_variables({"AUTO_LETS_ENCRYPT", "SERVER_NAME"})
|
||||
local vars, err = get_multiple_variables({"AUTO_LETS_ENCRYPT", "SERVER_NAME"})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get AUTO_LETS_ENCRYPT variables : " .. err)
|
||||
end
|
||||
|
|
@ -27,13 +48,13 @@ function letsencrypt:init()
|
|||
if multisite_vars["AUTO_LETS_ENCRYPT"] == "yes" and server_name ~= "global" then
|
||||
local check, data = self:read_files(server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -41,19 +62,19 @@ function letsencrypt:init()
|
|||
end
|
||||
end
|
||||
else
|
||||
local server_name, err = utils.get_variable("SERVER_NAME", false)
|
||||
local server_name, err = get_variable("SERVER_NAME", false)
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -66,7 +87,7 @@ function letsencrypt:init()
|
|||
end
|
||||
|
||||
function letsencrypt:ssl_certificate()
|
||||
local server_name, err = ssl.server_name()
|
||||
local server_name, err = ssl_server_name()
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
|
|
@ -87,7 +108,7 @@ function letsencrypt:read_files(server_name)
|
|||
}
|
||||
local data = {}
|
||||
for i, file in ipairs(files) do
|
||||
local f, err = io.open(file, "r")
|
||||
local f, err = open(file, "r")
|
||||
if not f then
|
||||
return false, file .. " = " .. err
|
||||
end
|
||||
|
|
@ -99,12 +120,12 @@ end
|
|||
|
||||
function letsencrypt:load_data(data, server_name)
|
||||
-- Load certificate
|
||||
local cert_chain, err = ssl.parse_pem_cert(data[1])
|
||||
local cert_chain, err = parse_pem_cert(data[1])
|
||||
if not cert_chain then
|
||||
return false, "error while parsing pem cert : " .. err
|
||||
end
|
||||
-- Load key
|
||||
local priv_key, err = ssl.parse_pem_priv_key(data[2])
|
||||
local priv_key, err = parse_pem_priv_key(data[2])
|
||||
if not priv_key then
|
||||
return false, "error while parsing pem priv key : " .. err
|
||||
end
|
||||
|
|
@ -120,48 +141,45 @@ function letsencrypt:load_data(data, server_name)
|
|||
end
|
||||
|
||||
function letsencrypt:access()
|
||||
if string.sub(self.ctx.bw.uri, 1, string.len("/.well-known/acme-challenge/")) == "/.well-known/acme-challenge/" then
|
||||
self.logger:log(ngx.NOTICE, "got a visit from Let's Encrypt, let's whitelist it")
|
||||
return self:ret(true, "visit from LE", ngx.OK)
|
||||
if sub(self.ctx.bw.uri, 1, string.len("/.well-known/acme-challenge/")) == "/.well-known/acme-challenge/" then
|
||||
self.logger:log(NOTICE, "got a visit from Let's Encrypt, let's whitelist it")
|
||||
return self:ret(true, "visit from LE", OK)
|
||||
end
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function letsencrypt:api(ctx)
|
||||
function letsencrypt:api()
|
||||
if
|
||||
not string.match(ctx.bw.uri, "^/lets%-encrypt/challenge$")
|
||||
or (ctx.bw.request_method ~= "POST" and ctx.bw.request_method ~= "DELETE")
|
||||
not match(self.ctx.bw.uri, "^/lets%-encrypt/challenge$")
|
||||
or (self.ctx.bw.request_method ~= "POST" and self.ctx.bw.request_method ~= "DELETE")
|
||||
then
|
||||
return false, nil, nil
|
||||
return self:ret(false, "success")
|
||||
end
|
||||
local acme_folder = "/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/"
|
||||
ngx.req.read_body()
|
||||
local ret, data = pcall(cjson.decode, ngx.req.get_body_data())
|
||||
local ngx_req = ngx.req
|
||||
ngx_req.read_body()
|
||||
local ret, data = pcall(decode, ngx_req.get_body_data())
|
||||
if not ret then
|
||||
return true, ngx.HTTP_BAD_REQUEST, { status = "error", msg = "json body decoding failed" }
|
||||
return self:ret(true, "json body decoding failed", HTTP_BAD_REQUEST)
|
||||
end
|
||||
os.execute("mkdir -p " .. acme_folder)
|
||||
if ctx.bw.request_method == "POST" then
|
||||
local file, err = io.open(acme_folder .. data.token, "w+")
|
||||
execute("mkdir -p " .. acme_folder)
|
||||
if self.ctx.bw.request_method == "POST" then
|
||||
local file, err = open(acme_folder .. data.token, "w+")
|
||||
if not file then
|
||||
return true,
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
{ status = "error", msg = "can't write validation token : " .. err }
|
||||
return self:ret(true, "can't write validation token : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
file:write(data.validation)
|
||||
file:close()
|
||||
return true, ngx.HTTP_OK, { status = "success", msg = "validation token written" }
|
||||
return self:ret(true, "validation token written", HTTP_OK)
|
||||
elseif ctx.bw.request_method == "DELETE" then
|
||||
local ok, err = os.remove(acme_folder .. data.token)
|
||||
local ok, err = remove(acme_folder .. data.token)
|
||||
if not ok then
|
||||
return true,
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
{ status = "error", msg = "can't remove validation token : " .. err }
|
||||
return self:ret(true, "can't remove validation token : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
return true, ngx.HTTP_OK, { status = "success", msg = "validation token removed" }
|
||||
return true, HTTP_OK, { status = "success", msg = "validation token removed" }
|
||||
end
|
||||
return true, ngx.HTTP_NOT_FOUND, { status = "error", msg = "unknown request" }
|
||||
return true, HTTP_NOT_FOUND, { status = "error", msg = "unknown request" }
|
||||
end
|
||||
|
||||
return letsencrypt
|
||||
|
|
|
|||
|
|
@ -5,11 +5,24 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local limit = class("limit", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local HTTP_TOO_MANY_REQUESTS = ngx.HTTP_TOO_MANY_REQUESTS
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local regex_match = utils.regex_match
|
||||
local time = os.time
|
||||
local date = os.date
|
||||
local encode = cjson.encode
|
||||
local decode = cjson.decode
|
||||
|
||||
local limit_req_timestamps = function(rate_max, rate_time, timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated = false
|
||||
local new_timestamps = {}
|
||||
local current_timestamp = os.time(os.date "!*t")
|
||||
local current_timestamp = time(date("!*t"))
|
||||
local delay = 0
|
||||
if rate_time == "s" then
|
||||
delay = 1
|
||||
|
|
@ -40,11 +53,11 @@ function limit:initialize(ctx)
|
|||
-- Call parent initialize
|
||||
plugin.initialize(self, "limit", ctx)
|
||||
-- Load rules if needed
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
-- Get all rules from datastore
|
||||
local all_rules, err = self.datastore:get("plugin_limit_rules", true)
|
||||
if not all_rules then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
return
|
||||
end
|
||||
self.rules = {}
|
||||
|
|
@ -73,7 +86,7 @@ function limit:is_needed()
|
|||
return self.variables["USE_LIMIT_REQ"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_LIMIT_REQ", "yes")
|
||||
local is_needed, err = has_variable("USE_LIMIT_REQ", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_LIMIT_REQ variable : " .. err)
|
||||
end
|
||||
|
|
@ -86,7 +99,7 @@ function limit:init()
|
|||
return self:ret(true, "no service uses limit for requests, skipping init")
|
||||
end
|
||||
-- Get variables
|
||||
local variables, err = utils.get_multiple_variables({ "LIMIT_REQ_URL", "LIMIT_REQ_RATE" })
|
||||
local variables, err = get_multiple_variables({ "LIMIT_REQ_URL", "LIMIT_REQ_RATE" })
|
||||
if variables == nil then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
|
|
@ -95,7 +108,7 @@ function limit:init()
|
|||
local i = 0
|
||||
for srv, vars in pairs(variables) do
|
||||
for var, value in pairs(vars) do
|
||||
if utils.regex_match(var, "LIMIT_REQ_URL") then
|
||||
if regex_match(var, "LIMIT_REQ_URL") then
|
||||
local url = value
|
||||
local rate = vars[var:gsub("URL", "RATE")]
|
||||
if data[srv] == nil then
|
||||
|
|
@ -115,7 +128,7 @@ end
|
|||
|
||||
function limit:access()
|
||||
-- Check if we are whitelisted
|
||||
if self.ctx.bw.is_whitelisted == "yes" then
|
||||
if is_whitelisted(self.ctx) then
|
||||
return self:ret(true, "client is whitelisted")
|
||||
end
|
||||
-- Check if access is needed
|
||||
|
|
@ -125,7 +138,7 @@ function limit:access()
|
|||
-- Check if URI is limited
|
||||
local rate
|
||||
for k, v in pairs(self.rules) do
|
||||
if k ~= "/" and utils.regex_match(self.ctx.bw.uri, k) then
|
||||
if k ~= "/" and regex_match(self.ctx.bw.uri, k) then
|
||||
rate = v
|
||||
break
|
||||
end
|
||||
|
|
@ -158,7 +171,7 @@ function limit:access()
|
|||
.. " and max rate = "
|
||||
.. rate
|
||||
.. ")",
|
||||
ngx.HTTP_TOO_MANY_REQUESTS
|
||||
HTTP_TOO_MANY_REQUESTS
|
||||
)
|
||||
end
|
||||
-- Limit not reached
|
||||
|
|
@ -184,14 +197,14 @@ function limit:limit_req(rate_max, rate_time)
|
|||
if self.use_redis then
|
||||
local redis_timestamps, err = self:limit_req_redis(rate_max, rate_time)
|
||||
if redis_timestamps == nil then
|
||||
self.logger:log(ngx.ERR, "limit_req_redis failed, falling back to local : " .. err)
|
||||
self.logger:log(ERR, "limit_req_redis failed, falling back to local : " .. err)
|
||||
else
|
||||
timestamps = redis_timestamps
|
||||
-- Save the new timestamps
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self.datastore:set(
|
||||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||
cjson.encode(timestamps),
|
||||
encode(timestamps),
|
||||
delay
|
||||
)
|
||||
if not ok then
|
||||
|
|
@ -222,7 +235,7 @@ function limit:limit_req_local(rate_max, rate_time)
|
|||
elseif err == "not found" then
|
||||
timestamps = "{}"
|
||||
end
|
||||
timestamps = cjson.decode(timestamps)
|
||||
timestamps = decode(timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated, new_timestamps, delay = limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
-- Save new timestamps if needed
|
||||
|
|
@ -230,7 +243,7 @@ function limit:limit_req_local(rate_max, rate_time)
|
|||
-- luacheck: ignore 421
|
||||
local ok, err = self.datastore:set(
|
||||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||
cjson.encode(new_timestamps),
|
||||
encode(new_timestamps),
|
||||
delay
|
||||
)
|
||||
if not ok then
|
||||
|
|
@ -303,7 +316,7 @@ function limit:limit_req_redis(rate_max, rate_time)
|
|||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||
rate_max,
|
||||
rate_time,
|
||||
os.time(os.date("!*t"))
|
||||
time(date("!*t"))
|
||||
)
|
||||
if not timestamps then
|
||||
self.clusterstore:close()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local misc = class("misc", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local HTTP_NOT_ALLOWED = ngx.HTTP_NOT_ALLOWED
|
||||
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
|
||||
local regex_match = utils.regex_match
|
||||
|
||||
function misc:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "misc", ctx)
|
||||
|
|
@ -12,8 +17,8 @@ end
|
|||
function misc:access()
|
||||
-- Check if method is valid
|
||||
local method = self.ctx.bw.request_method
|
||||
if not method or not utils.regex_match(method, "^[A-Z]+$") then
|
||||
return self:ret(true, "method is not valid", ngx.HTTP_BAD_REQUEST)
|
||||
if not method or not regex_match(method, "^[A-Z]+$") then
|
||||
return self:ret(true, "method is not valid", HTTP_BAD_REQUEST)
|
||||
end
|
||||
-- Check if method is allowed
|
||||
for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do
|
||||
|
|
@ -21,7 +26,7 @@ function misc:access()
|
|||
return self:ret(true, "method " .. method .. " is allowed")
|
||||
end
|
||||
end
|
||||
return self:ret(true, "method " .. method .. " is not allowed", ngx.HTTP_NOT_ALLOWED)
|
||||
return self:ret(true, "method " .. method .. " is not allowed", HTTP_NOT_ALLOWED)
|
||||
end
|
||||
|
||||
return misc
|
||||
|
|
|
|||
|
|
@ -76,6 +76,60 @@
|
|||
"label": "Redis keepalive pool",
|
||||
"regex": "^[0-9]+$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_USERNAME": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "Redis username used in AUTH command.",
|
||||
"id": "redis-username",
|
||||
"label": "Redis username",
|
||||
"regex": "^.*$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_PASSWORD": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "Redis password used in AUTH command.",
|
||||
"id": "redis-password",
|
||||
"label": "Redis password",
|
||||
"regex": "^.*$",
|
||||
"type": "password"
|
||||
},
|
||||
"REDIS_SENTINEL_HOSTS": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "Redis sentinel hosts with format host:[port] separated with spaces.",
|
||||
"id": "redis-sentinel-hosts",
|
||||
"label": "Redis sentinel hosts",
|
||||
"regex": "^.*$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_SENTINEL_USERNAME": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "Redis sentinel username.",
|
||||
"id": "redis-sentinel-username",
|
||||
"label": "Redis sentinel username",
|
||||
"regex": "^.*$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_SENTINEL_PASSWORD": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "Redis sentinel password.",
|
||||
"id": "redis-sentinel-password",
|
||||
"label": "Redis sentinel password",
|
||||
"regex": "^.*$",
|
||||
"type": "password"
|
||||
},
|
||||
"REDIS_SENTINEL_MASTER": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "Redis sentinel master name.",
|
||||
"id": "redis-sentinel-master",
|
||||
"label": "Redis sentinel master",
|
||||
"regex": "^.*$",
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ local plugin = require "bunkerweb.plugin"
|
|||
|
||||
local redis = class("redis", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local NOTICE = ngx.NOTICE
|
||||
|
||||
function redis:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "redis", ctx)
|
||||
|
|
@ -27,7 +30,7 @@ function redis:init_worker()
|
|||
if not ok then
|
||||
return self:ret(false, "redis ping command failed")
|
||||
end
|
||||
self.logger:log(ngx.NOTICE, "connectivity with redis server " .. self.variables["REDIS_HOST"] .. " is successful")
|
||||
self.logger:log(NOTICE, "connectivity with redis server " .. self.variables["REDIS_HOST"] .. " is successful")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,14 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local reversescan = class("reversescan", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local spawn = ngx.thread.spawn
|
||||
local wait = ngx.thread.wait
|
||||
local ngx_socket = ngx.socket
|
||||
local kill_all_threads = utils.kill_all_threads
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local tonumber = tonumber
|
||||
|
||||
function reversescan:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "reversescan", ctx)
|
||||
|
|
@ -32,7 +40,7 @@ function reversescan:access()
|
|||
break
|
||||
-- Perform scan in a thread
|
||||
elseif not cached then
|
||||
local thread = ngx.thread.spawn(
|
||||
local thread = spawn(
|
||||
self.scan,
|
||||
self.ctx.bw.remote_addr,
|
||||
tonumber(port),
|
||||
|
|
@ -47,11 +55,11 @@ function reversescan:access()
|
|||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, ret_err, get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
|
|
@ -71,7 +79,7 @@ function reversescan:access()
|
|||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
local ok, open, port = ngx.thread.wait(unpack(wait_threads))
|
||||
local ok, open, port = wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
|
|
@ -100,7 +108,7 @@ function reversescan:access()
|
|||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Cache results
|
||||
for port, result in pairs(results) do
|
||||
|
|
@ -112,7 +120,7 @@ function reversescan:access()
|
|||
if ret_threads ~= nil then
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, ret_err, get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
|
|
@ -126,7 +134,7 @@ function reversescan:preread()
|
|||
end
|
||||
|
||||
function reversescan.scan(ip, port, timeout)
|
||||
local tcpsock = ngx.socket.tcp()
|
||||
local tcpsock = ngx_socket.tcp()
|
||||
tcpsock:settimeout(timeout)
|
||||
local ok, _ = tcpsock:connect(ip, port)
|
||||
tcpsock:close()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,16 @@ local ssl = require "ngx.ssl"
|
|||
|
||||
local selfsigned = class("selfsigned", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local parse_pem_cert = ssl.parse_pem_cert
|
||||
local parse_pem_priv_key = ssl.parse_pem_priv_key
|
||||
local ssl_server_name = ssl.server_name
|
||||
local get_variable = utils.get_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local has_variable = utils.has_variable
|
||||
local open = io.open
|
||||
|
||||
function selfsigned:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "selfsigned", ctx)
|
||||
|
|
@ -12,13 +22,13 @@ end
|
|||
|
||||
function selfsigned:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if utils.has_variable("GENERATE_SELF_SIGNED_SSL", "yes") then
|
||||
local multisite, err = utils.get_variable("MULTISITE", false)
|
||||
if has_variable("GENERATE_SELF_SIGNED_SSL", "yes") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
return self:ret(false, "can't get MULTISITE variable : " .. err)
|
||||
end
|
||||
if multisite == "yes" then
|
||||
local vars, err = utils.get_multiple_variables({"GENERATE_SELF_SIGNED_SSL", "SERVER_NAME"})
|
||||
local vars, err = get_multiple_variables({"GENERATE_SELF_SIGNED_SSL", "SERVER_NAME"})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get GENERATE_SELF_SIGNED_SSL variables : " .. err)
|
||||
end
|
||||
|
|
@ -26,13 +36,13 @@ function selfsigned:init()
|
|||
if multisite_vars["GENERATE_SELF_SIGNED_SSL"] == "yes" and server_name ~= "global" then
|
||||
local check, data = self:read_files(server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -40,19 +50,19 @@ function selfsigned:init()
|
|||
end
|
||||
end
|
||||
else
|
||||
local server_name, err = utils.get_variable("SERVER_NAME", false)
|
||||
local server_name, err = get_variable("SERVER_NAME", false)
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -65,7 +75,7 @@ function selfsigned:init()
|
|||
end
|
||||
|
||||
function selfsigned:ssl_certificate()
|
||||
local server_name, err = ssl.server_name()
|
||||
local server_name, err = ssl_server_name()
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
|
|
@ -86,7 +96,7 @@ function selfsigned:read_files(server_name)
|
|||
}
|
||||
local data = {}
|
||||
for i, file in ipairs(files) do
|
||||
local f, err = io.open(file, "r")
|
||||
local f, err = open(file, "r")
|
||||
if not f then
|
||||
return false, file .. " = " .. err
|
||||
end
|
||||
|
|
@ -98,12 +108,12 @@ end
|
|||
|
||||
function selfsigned:load_data(data, server_name)
|
||||
-- Load certificate
|
||||
local cert_chain, err = ssl.parse_pem_cert(data[1])
|
||||
local cert_chain, err = parse_pem_cert(data[1])
|
||||
if not cert_chain then
|
||||
return false, "error while parsing pem cert : " .. err
|
||||
end
|
||||
-- Load key
|
||||
local priv_key, err = ssl.parse_pem_priv_key(data[2])
|
||||
local priv_key, err = parse_pem_priv_key(data[2])
|
||||
if not priv_key then
|
||||
return false, "error while parsing pem priv key : " .. err
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local sessions = class("sessions", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_variable = utils.get_variable
|
||||
local session_init = session.init
|
||||
local tonumber = tonumber
|
||||
|
||||
function sessions:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "sessions", ctx)
|
||||
|
|
@ -57,7 +63,7 @@ function sessions:init()
|
|||
["REDIS_KEEPALIVE_POOL"] = "",
|
||||
}
|
||||
for k, _ in pairs(redis_vars) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
local value, err = get_variable(k, false)
|
||||
if value == nil then
|
||||
return self:ret(false, "can't get " .. k .. " variable : " .. err)
|
||||
end
|
||||
|
|
@ -78,7 +84,7 @@ function sessions:init()
|
|||
config.secret = utils.rand(16)
|
||||
local ok, err = self.datastore:set("storage_sessions_SESSIONS_SECRET", config.secret)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
||||
self.logger:log(ERR, "error from datastore:set : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -89,7 +95,7 @@ function sessions:init()
|
|||
config.cookie_name = utils.rand(16)
|
||||
local ok, err = self.datastore:set("storage_sessions_SESSIONS_NAME", config.cookie_name)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
||||
self.logger:log(ERR, "error from datastore:set : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -111,7 +117,7 @@ function sessions:init()
|
|||
database = tonumber(redis_vars["REDIS_DATABASE"]),
|
||||
}
|
||||
end
|
||||
session.init(config)
|
||||
session_init(config)
|
||||
return self:ret(true, "sessions init successful")
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,29 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local whitelist = class("whitelist", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local OK = ngx.OK
|
||||
local WARN = ngx.WARN
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_ips = utils.get_ips
|
||||
local get_rdns = utils.get_rdns
|
||||
local get_asn = utils.get_asn
|
||||
local regex_match = utils.regex_match
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
local env_set = env.set
|
||||
|
||||
function whitelist:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "whitelist", ctx)
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_whitelist_lists", true)
|
||||
if not lists then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
self.lists = {}
|
||||
else
|
||||
self.lists = lists
|
||||
|
|
@ -46,9 +61,9 @@ function whitelist:is_needed()
|
|||
return self.variables["USE_WHITELIST"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_WHITELIST", "yes")
|
||||
local is_needed, err = has_variable("USE_WHITELIST", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_WHITELIST variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_WHITELIST variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -68,7 +83,7 @@ function whitelist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(whitelists) do
|
||||
local f, _ = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
||||
local f, _ = open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(whitelists[kind], line)
|
||||
|
|
@ -86,10 +101,11 @@ function whitelist:init()
|
|||
end
|
||||
|
||||
function whitelist:set()
|
||||
local ngx_var = ngx.var
|
||||
-- Set default value
|
||||
ngx.var.is_whitelisted = "no"
|
||||
ngx_var.is_whitelisted = "no"
|
||||
self.ctx.bw.is_whitelisted = "no"
|
||||
env.set("is_whitelisted", "no")
|
||||
env_set("is_whitelisted", "no")
|
||||
-- Check if set is needed
|
||||
if not self:is_needed() then
|
||||
return self:ret(true, "whitelist not activated")
|
||||
|
|
@ -99,9 +115,9 @@ function whitelist:set()
|
|||
if whitelisted == nil then
|
||||
return self:ret(false, err)
|
||||
elseif whitelisted then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
ngx_var.is_whitelisted = "yes"
|
||||
self.ctx.bw.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
env_set("is_whitelisted", "yes")
|
||||
return self:ret(true, err)
|
||||
end
|
||||
return self:ret(true, "not in whitelist cache")
|
||||
|
|
@ -113,14 +129,15 @@ function whitelist:access()
|
|||
return self:ret(true, "whitelist not activated")
|
||||
end
|
||||
-- Check cache
|
||||
local ngx_var = ngx.var
|
||||
local whitelisted, err, already_cached = self:check_cache()
|
||||
if whitelisted == nil then
|
||||
return self:ret(false, err)
|
||||
elseif whitelisted then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
ngx_var.is_whitelisted = "yes"
|
||||
self.ctx.bw.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return self:ret(true, err, ngx.OK)
|
||||
env_set("is_whitelisted", "yes")
|
||||
return self:ret(true, err, OK)
|
||||
end
|
||||
-- Perform checks
|
||||
local ok
|
||||
|
|
@ -128,16 +145,16 @@ function whitelist:access()
|
|||
if not already_cached[k] then
|
||||
ok, whitelisted = self:is_whitelisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is whitelisted : " .. whitelisted)
|
||||
self.logger:log(ERR, "error while checking if " .. k .. " is whitelisted : " .. whitelisted)
|
||||
else
|
||||
ok, err = self:add_to_cache(self:kind_to_ele(k), whitelisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
self.logger:log(ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if whitelisted ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
ngx_var.is_whitelisted = "yes"
|
||||
self.ctx.bw.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
env_set("is_whitelisted", "yes")
|
||||
return self:ret(true, k .. " is whitelisted (info : " .. whitelisted .. ")", ngx.OK)
|
||||
end
|
||||
end
|
||||
|
|
@ -179,7 +196,7 @@ function whitelist:check_cache()
|
|||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
self.logger:log(ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ok" then
|
||||
return true, k .. " is in cached whitelist (info : " .. cached .. ")"
|
||||
end
|
||||
|
|
@ -224,7 +241,7 @@ end
|
|||
|
||||
function whitelist:is_whitelisted_ip()
|
||||
-- Check if IP is in whitelist
|
||||
local ipm, err = ipmatcher.new(self.lists["IP"])
|
||||
local ipm, err = ipmatcher_new(self.lists["IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -244,7 +261,7 @@ function whitelist:is_whitelisted_ip()
|
|||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
local rdns_list, err = get_rdns(self.ctx.bw.remote_addr, self.ctx, true)
|
||||
-- Check if rDNS is in whitelist
|
||||
if rdns_list then
|
||||
local forward_check = nil
|
||||
|
|
@ -262,7 +279,7 @@ function whitelist:is_whitelisted_ip()
|
|||
end
|
||||
end
|
||||
if forward_check then
|
||||
local ip_list, err = utils.get_ips(forward_check)
|
||||
local ip_list, err = get_ips(forward_check, nil, self.ctx, true)
|
||||
if ip_list then
|
||||
for _, ip in ipairs(ip_list) do
|
||||
if ip == self.ctx.bw.remote_addr then
|
||||
|
|
@ -270,23 +287,23 @@ function whitelist:is_whitelisted_ip()
|
|||
end
|
||||
end
|
||||
self.logger:log(
|
||||
ngx.WARN,
|
||||
WARN,
|
||||
"IP " .. self.ctx.bw.remote_addr .. " may spoof reverse DNS " .. forward_check
|
||||
)
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns (forward check) : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns (forward check) : " .. err)
|
||||
end
|
||||
end
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns : " .. err)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in whitelist
|
||||
if self.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(self.ctx.bw.remote_addr)
|
||||
local asn, err = get_asn(self.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
self.logger:log(ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
|
|
@ -303,7 +320,7 @@ end
|
|||
function whitelist:is_whitelisted_uri()
|
||||
-- Check if URI is in whitelist
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
if regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
end
|
||||
|
|
@ -314,7 +331,7 @@ end
|
|||
function whitelist:is_whitelisted_ua()
|
||||
-- Check if UA is in whitelist
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -234,6 +234,12 @@
|
|||
"name": "zlib v1.3",
|
||||
"url": "https://github.com/madler/zlib.git",
|
||||
"commit": "09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851"
|
||||
},
|
||||
{
|
||||
"id": "lua-resty-redis-connector",
|
||||
"name": "lua-resty-redis-connector v0.11.0",
|
||||
"url": "https://github.com/ledgetech/lua-resty-redis-connector.git",
|
||||
"commit": "02a29f93253d1f6ad392c5ac2b643c57e62b5979"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,22 +43,24 @@ do
|
|||
url="$(echo "$repo" | jq -r .url)"
|
||||
commit="$(echo "$repo" | jq -r .commit)"
|
||||
post_install="$(echo "$repo" | jq -r .post_install)"
|
||||
post="no"
|
||||
|
||||
echo "ℹ️ Clone ${name} from $url at commit/version $commit"
|
||||
|
||||
if [ ! -d "src/deps/src/$id" ] ; then
|
||||
do_and_check_cmd git subtree add --prefix "src/deps/src/$id" "$url" "$commit" --squash
|
||||
post="yes"
|
||||
else
|
||||
echo "⚠️ Skipping clone of $url because target directory is already present"
|
||||
echo "ℹ️ Updating ${name} from $url at commit/version $commit"
|
||||
do_and_check_cmd git subtree pull --prefix "src/deps/src/$id" "$url" "$commit" --squash
|
||||
# echo "ℹ️ Updating ${name} from $url at commit/version $commit"
|
||||
# do_and_check_cmd git subtree pull --prefix "src/deps/src/$id" "$url" "$commit" --squash
|
||||
fi
|
||||
|
||||
if [ -d "src/deps/src/$id/.git" ] ; then
|
||||
do_and_check_cmd rm -rf "src/deps/src/$id/.git"
|
||||
fi
|
||||
|
||||
if [ "$post_install" != "null" ] ; then
|
||||
if [ "$post_install" != "null" ] && [ "$post" != "no" ]; then
|
||||
echo "ℹ️ Running post install script for ${name}"
|
||||
bash -c "$post_install"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -183,6 +183,12 @@ export CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-signal"
|
|||
do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps -j "$NTASK"
|
||||
do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
|
||||
|
||||
# Installing lua-resty-redis-connector
|
||||
echo "ℹ️ Installing lua-resty-redis-connector"
|
||||
export CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-redis-connector"
|
||||
do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
|
||||
|
||||
# Patch modsec module
|
||||
export CHANGE_DIR="/tmp/bunkerweb/deps/misc"
|
||||
do_and_check_cmd bash -c "mv ngx_http_modsecurity_access.c /tmp/bunkerweb/deps/src/modsecurity-nginx/src/"
|
||||
|
||||
|
|
|
|||
1
src/deps/src/lua-resty-redis-connector/.gitattributes
vendored
Normal file
1
src/deps/src/lua-resty-redis-connector/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.t linguist-language=lua
|
||||
1
src/deps/src/lua-resty-redis-connector/.github/FUNDING.yml
vendored
Normal file
1
src/deps/src/lua-resty-redis-connector/.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
github: pintsized
|
||||
5
src/deps/src/lua-resty-redis-connector/.gitignore
vendored
Normal file
5
src/deps/src/lua-resty-redis-connector/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
t/servroot/
|
||||
t/error.log
|
||||
luacov.*
|
||||
*.src.rock
|
||||
lua-resty-redis-connector*.tar.gz
|
||||
2
src/deps/src/lua-resty-redis-connector/.luacheckrc
vendored
Normal file
2
src/deps/src/lua-resty-redis-connector/.luacheckrc
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
std = "ngx_lua"
|
||||
redefined = false
|
||||
4
src/deps/src/lua-resty-redis-connector/.luacov
vendored
Normal file
4
src/deps/src/lua-resty-redis-connector/.luacov
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
modules = {
|
||||
["resty.redis.connector"] = "lib/resty/redis/connector.lua",
|
||||
["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua",
|
||||
}
|
||||
84
src/deps/src/lua-resty-redis-connector/.travis.yml
vendored
Normal file
84
src/deps/src/lua-resty-redis-connector/.travis.yml
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
sudo: required
|
||||
dist: focal
|
||||
|
||||
os: linux
|
||||
|
||||
language: c
|
||||
|
||||
compiler: gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:redislabs/redis'
|
||||
packages:
|
||||
- luarocks
|
||||
- lsof
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- download-cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- JOBS=3
|
||||
- NGX_BUILD_JOBS=$JOBS
|
||||
- LUAJIT_PREFIX=/opt/luajit21
|
||||
- LUAJIT_LIB=$LUAJIT_PREFIX/lib
|
||||
- LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
|
||||
- LUA_INCLUDE_DIR=$LUAJIT_INC
|
||||
- OPENSSL_PREFIX=/opt/ssl
|
||||
- OPENSSL_LIB=$OPENSSL_PREFIX/lib
|
||||
- OPENSSL_INC=$OPENSSL_PREFIX/include
|
||||
- OPENSSL_VER=1.1.1f
|
||||
- LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
|
||||
- TEST_NGINX_SLEEP=0.006
|
||||
- LUACHECK_VER=0.21.1
|
||||
jobs:
|
||||
- NGINX_VERSION=1.19.9
|
||||
|
||||
before_install:
|
||||
# we can't update redis in addons.apt.packages as updated package automatically tries to start and immediately fails
|
||||
- echo exit 101 | sudo tee /usr/sbin/policy-rc.d
|
||||
- sudo chmod +x /usr/sbin/policy-rc.d
|
||||
- sudo apt-get install -y redis-server
|
||||
- sudo luarocks install luacov
|
||||
- sudo luarocks install lua-resty-redis
|
||||
- sudo luarocks install luacheck $LUACHECK_VER
|
||||
- luacheck -q .
|
||||
|
||||
install:
|
||||
- if [ ! -d download-cache ]; then mkdir download-cache; fi
|
||||
- if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -O download-cache/openssl-$OPENSSL_VER.tar.gz https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz; fi
|
||||
- sudo apt-get install -qq -y cpanminus axel
|
||||
- sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- git clone https://github.com/openresty/openresty.git ../openresty
|
||||
- git clone https://github.com/openresty/nginx-devel-utils.git
|
||||
- git clone https://github.com/openresty/lua-cjson.git
|
||||
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
|
||||
- git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
|
||||
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
|
||||
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
|
||||
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
|
||||
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
|
||||
- git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git
|
||||
|
||||
script:
|
||||
- sudo iptables -A OUTPUT -p tcp --dst 127.0.0.2 --dport 12345 -j DROP
|
||||
- cd luajit2/
|
||||
- make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- cd ../lua-cjson && make && sudo PATH=$PATH make install && cd ..
|
||||
- tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz
|
||||
- cd openssl-$OPENSSL_VER/
|
||||
- ./config shared --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- cd ..
|
||||
- export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH
|
||||
- export NGX_BUILD_CC=$CC
|
||||
- ngx-build $NGINX_VERSION --with-ipv6 --with-http_realip_module --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --add-module=../echo-nginx-module --add-module=../lua-nginx-module --add-module=../stream-lua-nginx-module --with-stream --with-stream_ssl_module --with-debug > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- nginx -V
|
||||
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
|
||||
- mkdir -p tmp
|
||||
- TMP_DIR=$PWD/tmp make test_all
|
||||
189
src/deps/src/lua-resty-redis-connector/Makefile
vendored
Normal file
189
src/deps/src/lua-resty-redis-connector/Makefile
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
SHELL := /bin/bash # Cheat by using bash :)
|
||||
|
||||
OPENRESTY_PREFIX = /usr/local/openresty
|
||||
|
||||
TEST_FILE ?= t
|
||||
TMP_DIR ?= /tmp
|
||||
|
||||
REDIS_CMD = redis-server
|
||||
SENTINEL_CMD = $(REDIS_CMD) --sentinel
|
||||
|
||||
REDIS_SOCK = /redis.sock
|
||||
REDIS_PID = /redis.pid
|
||||
REDIS_LOG = /redis.log
|
||||
REDIS_PREFIX = $(TMP_DIR)/redis-
|
||||
|
||||
# Overrideable redis test variables
|
||||
TEST_REDIS_PORT ?= 6380
|
||||
TEST_REDIS_PORT_SL1 ?= 6381
|
||||
TEST_REDIS_PORT_SL2 ?= 6382
|
||||
TEST_REDIS_PORT_AUTH ?= 6383
|
||||
TEST_REDIS_PORTS ?= $(TEST_REDIS_PORT) $(TEST_REDIS_PORT_SL1) $(TEST_REDIS_PORT_SL2)
|
||||
TEST_REDIS_PORTS_ALL ?= $(TEST_REDIS_PORTS) $(TEST_REDIS_PORT_AUTH)
|
||||
TEST_REDIS_DATABASE ?= 1
|
||||
TEST_REDIS_SOCKET ?= $(REDIS_PREFIX)$(TEST_REDIS_PORT)$(REDIS_SOCK)
|
||||
|
||||
REDIS_SLAVE_ARG := --slaveof 127.0.0.1 $(TEST_REDIS_PORT)
|
||||
REDIS_CLI := redis-cli -p $(TEST_REDIS_PORT) -n $(TEST_REDIS_DATABASE)
|
||||
|
||||
# Overrideable redis + sentinel test variables
|
||||
TEST_SENTINEL_PORT1 ?= 6390
|
||||
TEST_SENTINEL_PORT2 ?= 6391
|
||||
TEST_SENTINEL_PORT3 ?= 6392
|
||||
TEST_SENTINEL_PORT_AUTH ?= 6393
|
||||
TEST_SENTINEL_PORTS ?= $(TEST_SENTINEL_PORT1) $(TEST_SENTINEL_PORT2) $(TEST_SENTINEL_PORT3)
|
||||
TEST_SENTINEL_PORTS_ALL ?= $(TEST_SENTINEL_PORTS) $(TEST_SENTINEL_PORT_AUTH)
|
||||
TEST_SENTINEL_MASTER_NAME ?= mymaster
|
||||
TEST_SENTINEL_PROMOTION_TIME ?= 20
|
||||
|
||||
# Command line arguments for redis tests
|
||||
TEST_REDIS_VARS = PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$(PATH) \
|
||||
TEST_NGINX_REDIS_PORT=$(TEST_REDIS_PORT) \
|
||||
TEST_NGINX_REDIS_PORT_SL1=$(TEST_REDIS_PORT_SL1) \
|
||||
TEST_NGINX_REDIS_PORT_SL2=$(TEST_REDIS_PORT_SL2) \
|
||||
TEST_NGINX_REDIS_PORT_AUTH=$(TEST_REDIS_PORT_AUTH) \
|
||||
TEST_NGINX_REDIS_SOCKET=unix:$(TEST_REDIS_SOCKET) \
|
||||
TEST_NGINX_REDIS_DATABASE=$(TEST_REDIS_DATABASE) \
|
||||
TEST_NGINX_NO_SHUFFLE=1
|
||||
|
||||
# Command line arguments for sentinel tests
|
||||
TEST_SENTINEL_VARS = PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$(PATH) \
|
||||
TEST_NGINX_REDIS_PORT=$(TEST_NGINX_REDIS_PORT) \
|
||||
TEST_NGINX_REDIS_PORT_SL1=$(TEST_NGINX_REDIS_PORT_SL1) \
|
||||
TEST_NGINX_REDIS_PORT_SL2=$(TEST_NGINX_REDIS_PORT_SL2) \
|
||||
TEST_NGINX_SENTINEL_PORT1=$(TEST_NGINX_SENTINEL_PORT1) \
|
||||
TEST_NGINX_SENTINEL_PORT2=$(TEST_NGINX_SENTINEL_PORT2) \
|
||||
TEST_NGINX_SENTINEL_PORT3=$(TEST_NGINX_SENTINEL_PORT3) \
|
||||
TEST_NGINX_SENTINEL_PORT_AUTH=$(TEST_NGINX_SENTINEL_AUTH) \
|
||||
TEST_NGINX_SENTINEL_MASTER_NAME=$(TEST_NGINX_SENTINEL_MASTER_NAME) \
|
||||
TEST_NGINX_REDIS_DATABASE=$(TEST_NGINX_REDIS_DATABASE) \
|
||||
TEST_NGINX_NO_SHUFFLE=1
|
||||
|
||||
# Sentinel configuration can only be set by a config file
|
||||
define TEST_SENTINEL_CONFIG
|
||||
sentinel monitor $(TEST_SENTINEL_MASTER_NAME) 127.0.0.1 $(TEST_REDIS_PORT) 2
|
||||
sentinel down-after-milliseconds $(TEST_SENTINEL_MASTER_NAME) 2000
|
||||
sentinel failover-timeout $(TEST_SENTINEL_MASTER_NAME) 10000
|
||||
sentinel parallel-syncs $(TEST_SENTINEL_MASTER_NAME) 5
|
||||
endef
|
||||
define TEST_SENTINEL_AUTH_CONFIG
|
||||
sentinel monitor $(TEST_SENTINEL_MASTER_NAME) 127.0.0.1 $(TEST_REDIS_PORT_AUTH) 1
|
||||
endef
|
||||
|
||||
export TEST_SENTINEL_CONFIG TEST_SENTINEL_AUTH_CONFIG
|
||||
|
||||
SENTINEL_CONFIG_FILE = /tmp/sentinel-test-config
|
||||
SENTINEL_AUTH_CONFIG_FILE = /tmp/sentinel-auth-test-config
|
||||
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
LUA_INCLUDE_DIR ?= $(PREFIX)/include
|
||||
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
|
||||
PROVE ?= prove -I ../test-nginx/lib
|
||||
INSTALL ?= install
|
||||
|
||||
.PHONY: all install test test_all start_redis_instances stop_redis_instances \
|
||||
start_redis_instance stop_redis_instance cleanup_redis_instance flush_db \
|
||||
create_sentinel_config delete_sentinel_config check_ports test_redis \
|
||||
test_sentinel sleep
|
||||
|
||||
all: ;
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty/redis
|
||||
$(INSTALL) lib/resty/redis/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty/redis
|
||||
|
||||
test: test_redis
|
||||
test_all: start_redis_instances sleep test_redis stop_redis_instances
|
||||
|
||||
check:
|
||||
luacheck lib
|
||||
|
||||
sleep:
|
||||
sleep 3
|
||||
|
||||
start_redis_instances: check_ports create_sentinel_config
|
||||
$(REDIS_CMD) --version
|
||||
|
||||
@$(foreach port,$(TEST_REDIS_PORTS), \
|
||||
[[ "$(port)" != "$(TEST_REDIS_PORT)" ]] && \
|
||||
SLAVE="$(REDIS_SLAVE_ARG)" || \
|
||||
SLAVE="" && \
|
||||
$(MAKE) start_redis_instance args="$$SLAVE" port=$(port) \
|
||||
prefix=$(REDIS_PREFIX)$(port) && \
|
||||
) true
|
||||
|
||||
$(MAKE) start_redis_instance \
|
||||
args="--user redisuser on '>redisuserpass' '~*' '&*' '+@all'" \
|
||||
port=$(TEST_REDIS_PORT_AUTH) \
|
||||
prefix=$(REDIS_PREFIX)$(TEST_REDIS_PORT_AUTH)
|
||||
|
||||
@$(foreach port,$(TEST_SENTINEL_PORTS), \
|
||||
$(MAKE) start_redis_instance \
|
||||
port=$(port) args="$(SENTINEL_CONFIG_FILE) --sentinel" \
|
||||
prefix=$(REDIS_PREFIX)$(port) && \
|
||||
) true
|
||||
|
||||
$(MAKE) start_redis_instance \
|
||||
args="$(SENTINEL_AUTH_CONFIG_FILE) --sentinel --user sentineluser on '>sentineluserpass' '~*' '&*' '+@all'" \
|
||||
port=$(TEST_SENTINEL_PORT_AUTH) \
|
||||
prefix=$(REDIS_PREFIX)$(TEST_SENTINEL_PORT_AUTH)
|
||||
|
||||
stop_redis_instances: delete_sentinel_config
|
||||
-@$(foreach port,$(TEST_REDIS_PORTS_ALL) $(TEST_SENTINEL_PORTS_ALL), \
|
||||
$(MAKE) stop_redis_instance cleanup_redis_instance port=$(port) \
|
||||
prefix=$(REDIS_PREFIX)$(port) && \
|
||||
) true 2>&1 > /dev/null
|
||||
|
||||
|
||||
start_redis_instance:
|
||||
-@echo "Starting redis on port $(port) with args: \"$(args)\""
|
||||
-@mkdir -p $(prefix)
|
||||
$(REDIS_CMD) $(args) \
|
||||
--pidfile $(prefix)$(REDIS_PID) \
|
||||
--bind 127.0.0.1 --port $(port) \
|
||||
--unixsocket $(prefix)$(REDIS_SOCK) \
|
||||
--unixsocketperm 777 \
|
||||
--dir $(prefix) \
|
||||
--logfile $(prefix)$(REDIS_LOG) \
|
||||
--loglevel debug \
|
||||
--daemonize yes
|
||||
|
||||
stop_redis_instance:
|
||||
-@echo "Stopping redis on port $(port)"
|
||||
-@[[ -f "$(prefix)$(REDIS_PID)" ]] && kill -QUIT \
|
||||
`cat $(prefix)$(REDIS_PID)` 2>&1 > /dev/null || true
|
||||
|
||||
cleanup_redis_instance: stop_redis_instance
|
||||
-@echo "Cleaning up redis files in $(prefix)"
|
||||
-@rm -rf $(prefix)
|
||||
|
||||
flush_db:
|
||||
-@echo "Flushing Redis DB"
|
||||
@$(REDIS_CLI) flushdb
|
||||
|
||||
create_sentinel_config:
|
||||
-@echo "Creating $(SENTINEL_CONFIG_FILE)"
|
||||
@echo "$$TEST_SENTINEL_CONFIG" > $(SENTINEL_CONFIG_FILE)
|
||||
-@echo "Creating $(SENTINEL_AUTH_CONFIG_FILE)"
|
||||
@echo "$$TEST_SENTINEL_AUTH_CONFIG" > $(SENTINEL_AUTH_CONFIG_FILE)
|
||||
|
||||
delete_sentinel_config:
|
||||
-@echo "Removing $(SENTINEL_CONFIG_FILE)"
|
||||
@rm -f $(SENTINEL_CONFIG_FILE)
|
||||
-@echo "Removing $(SENTINEL_AUTH_CONFIG_FILE)"
|
||||
@rm -f $(SENTINEL_AUTH_CONFIG_FILE)
|
||||
|
||||
check_ports:
|
||||
-@echo "Checking ports $(TEST_REDIS_PORTS_ALL) $(TEST_SENTINEL_PORTS_ALL)"
|
||||
@$(foreach port,$(TEST_REDIS_PORTS_ALL) $(TEST_SENTINEL_PORTS_ALL),! lsof -i :$(port) &&) true 2>&1 > /dev/null
|
||||
|
||||
test_redis: flush_db
|
||||
util/lua-releng
|
||||
@rm -f luacov.stats.out
|
||||
$(TEST_REDIS_VARS) $(PROVE) $(TEST_FILE)
|
||||
@luacov
|
||||
@tail -7 luacov.report.out
|
||||
|
||||
test_leak: flush_db
|
||||
$(TEST_REDIS_VARS) TEST_NGINX_CHECK_LEAK=1 $(PROVE) $(TEST_FILE)
|
||||
312
src/deps/src/lua-resty-redis-connector/README.md
vendored
Normal file
312
src/deps/src/lua-resty-redis-connector/README.md
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
# lua-resty-redis-connector
|
||||
|
||||
[](https://travis-ci.org/ledgetech/lua-resty-redis-connector)
|
||||
|
||||
Connection utilities for
|
||||
[lua-resty-redis](https://github.com/openresty/lua-resty-redis), making it easy
|
||||
and reliable to connect to Redis hosts, either directly or via [Redis
|
||||
Sentinel](http://redis.io/topics/sentinel).
|
||||
|
||||
|
||||
## Synopsis
|
||||
|
||||
Quick and simple authenticated connection on localhost to DB 2:
|
||||
|
||||
```lua
|
||||
local redis, err = require("resty.redis.connector").new({
|
||||
url = "redis://PASSWORD@127.0.0.1:6379/2",
|
||||
}):connect()
|
||||
```
|
||||
|
||||
More verbose configuration, with timeouts and a default password:
|
||||
|
||||
```lua
|
||||
local rc = require("resty.redis.connector").new({
|
||||
connect_timeout = 50,
|
||||
send_timeout = 5000,
|
||||
read_timeout = 5000,
|
||||
keepalive_timeout = 30000,
|
||||
password = "mypass",
|
||||
})
|
||||
|
||||
local redis, err = rc:connect({
|
||||
url = "redis://127.0.0.1:6379/2",
|
||||
})
|
||||
|
||||
-- ...
|
||||
|
||||
local ok, err = rc:set_keepalive(redis) -- uses keepalive params
|
||||
```
|
||||
|
||||
Keep all config in a table, to easily create / close connections as needed:
|
||||
|
||||
```lua
|
||||
local rc = require("resty.redis.connector").new({
|
||||
connect_timeout = 50,
|
||||
send_timeout = 5000,
|
||||
read_timeout = 5000,
|
||||
keepalive_timeout = 30000,
|
||||
|
||||
host = "127.0.0.1",
|
||||
port = 6379,
|
||||
db = 2,
|
||||
password = "mypass",
|
||||
})
|
||||
|
||||
local redis, err = rc:connect()
|
||||
|
||||
-- ...
|
||||
|
||||
local ok, err = rc:set_keepalive(redis)
|
||||
```
|
||||
|
||||
[connect](#connect) can be used to override some defaults given in [new](#new),
|
||||
which are pertinent to this connection only.
|
||||
|
||||
|
||||
```lua
|
||||
local rc = require("resty.redis.connector").new({
|
||||
host = "127.0.0.1",
|
||||
port = 6379,
|
||||
db = 2,
|
||||
})
|
||||
|
||||
local redis, err = rc:connect({
|
||||
db = 5,
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## DSN format
|
||||
|
||||
If the `params.url` field is present then it will be parsed to set the other
|
||||
params. Any manually specified params will override values given in the DSN.
|
||||
|
||||
*Note: this is a behaviour change as of v0.06. Previously, the DSN values would
|
||||
take precedence.*
|
||||
|
||||
### Direct Redis connections
|
||||
|
||||
The format for connecting directly to Redis is:
|
||||
|
||||
`redis://USERNAME:PASSWORD@HOST:PORT/DB`
|
||||
|
||||
The `USERNAME`, `PASSWORD` and `DB` fields are optional, all other components
|
||||
are required.
|
||||
|
||||
Use of username requires Redis 6.0.0 or newer.
|
||||
|
||||
### Connections via Redis Sentinel
|
||||
|
||||
When connecting via Redis Sentinel, the format is as follows:
|
||||
|
||||
`sentinel://USERNAME:PASSWORD@MASTER_NAME:ROLE/DB`
|
||||
|
||||
Again, `USERNAME`, `PASSWORD` and `DB` are optional. `ROLE` must be either `m`
|
||||
or `s` for master / slave respectively.
|
||||
|
||||
On versions of Redis newer than 5.0.1, Sentinels can optionally require their
|
||||
own password. If enabled, provide this password in the `sentinel_password`
|
||||
parameter. On Redis 6.2.0 and newer you can pass username using
|
||||
`sentinel_username` parameter.
|
||||
|
||||
A table of `sentinels` must also be supplied. e.g.
|
||||
|
||||
```lua
|
||||
local redis, err = rc:connect{
|
||||
url = "sentinel://mymaster:a/2",
|
||||
sentinels = {
|
||||
{ host = "127.0.0.1", port = 26379 },
|
||||
},
|
||||
sentinel_username = "default",
|
||||
sentinel_password = "password"
|
||||
}
|
||||
```
|
||||
|
||||
## Proxy Mode
|
||||
|
||||
Enable the `connection_is_proxied` parameter if connecting to Redis through a
|
||||
proxy service (e.g. Twemproxy). These proxies generally only support a limited
|
||||
sub-set of Redis commands, those which do not require state and do not affect
|
||||
multiple keys. Databases and transactions are also not supported.
|
||||
|
||||
Proxy mode will disable switching to a DB on connect. Unsupported commands
|
||||
(defaults to those not supported by Twemproxy) will return `nil, err`
|
||||
immediately rather than being sent to the proxy, which can result in dropped
|
||||
connections.
|
||||
|
||||
`discard` will not be sent when adding connections to the keepalive pool
|
||||
|
||||
|
||||
## Disabled commands
|
||||
|
||||
If configured as a table of commands, the command methods will be replaced by a
|
||||
function which immediately returns `nil, err` without forwarding the command to
|
||||
the server
|
||||
|
||||
## Default Parameters
|
||||
|
||||
|
||||
```lua
|
||||
{
|
||||
connect_timeout = 100,
|
||||
send_timeout = 1000,
|
||||
read_timeout = 1000,
|
||||
keepalive_timeout = 60000,
|
||||
keepalive_poolsize = 30,
|
||||
|
||||
-- ssl, ssl_verify, server_name, pool, pool_size, backlog
|
||||
-- see: https://github.com/openresty/lua-resty-redis#connect
|
||||
connection_options = {},
|
||||
|
||||
host = "127.0.0.1",
|
||||
port = "6379",
|
||||
path = "", -- unix socket path, e.g. /tmp/redis.sock
|
||||
username = "",
|
||||
password = "",
|
||||
sentinel_username = "",
|
||||
sentinel_password = "",
|
||||
db = 0,
|
||||
|
||||
master_name = "mymaster",
|
||||
role = "master", -- master | slave
|
||||
sentinels = {},
|
||||
|
||||
connection_is_proxied = false,
|
||||
|
||||
disabled_commands = {},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
* [new](#new)
|
||||
* [connect](#connect)
|
||||
* [set_keepalive](#set_keepalive)
|
||||
* [Utilities](#utilities)
|
||||
* [connect_via_sentinel](#connect_via_sentinel)
|
||||
* [try_hosts](#try_hosts)
|
||||
* [connect_to_host](#connect_to_host)
|
||||
* [sentinel.get_master](#sentinelget_master)
|
||||
* [sentinel.get_slaves](#sentinelget_slaves)
|
||||
|
||||
|
||||
### new
|
||||
|
||||
`syntax: rc = redis_connector.new(params)`
|
||||
|
||||
Creates the Redis Connector object, overring default params with the ones given.
|
||||
In case of failures, returns `nil` and a string describing the error.
|
||||
|
||||
|
||||
### connect
|
||||
|
||||
`syntax: redis, err = rc:connect(params)`
|
||||
|
||||
Attempts to create a connection, according to the [params](#parameters)
|
||||
supplied, falling back to defaults given in `new` or the predefined defaults. If
|
||||
a connection cannot be made, returns `nil` and a string describing the reason.
|
||||
|
||||
Note that `params` given here do not change the connector's own configuration,
|
||||
and are only used to alter this particular connection operation. As such, the
|
||||
following parameters have no meaning when given in `connect`.
|
||||
|
||||
* `keepalive_poolsize`
|
||||
* `keepalive_timeout`
|
||||
* `connection_is_proxied`
|
||||
* `disabled_commands`
|
||||
|
||||
|
||||
### set_keepalive
|
||||
|
||||
`syntax: ok, err = rc:set_keepalive(redis)`
|
||||
|
||||
Attempts to place the given Redis connection on the keepalive pool, according to
|
||||
timeout and poolsize params given in `new` or the predefined defaults.
|
||||
|
||||
This allows an application to release resources without having to keep track of
|
||||
application wide keepalive settings.
|
||||
|
||||
Returns `1` or in the case of error, `nil` and a string describing the error.
|
||||
|
||||
|
||||
## Utilities
|
||||
|
||||
The following methods are not typically needed, but may be useful if a custom
|
||||
interface is required.
|
||||
|
||||
|
||||
### connect_via_sentinel
|
||||
|
||||
`syntax: redis, err = rc:connect_via_sentinel(params)`
|
||||
|
||||
Returns a Redis connection by first accessing a sentinel as supplied by the
|
||||
`params.sentinels` table, and querying this with the `params.master_name` and
|
||||
`params.role`.
|
||||
|
||||
|
||||
### try_hosts
|
||||
|
||||
`syntax: redis, err = rc:try_hosts(hosts)`
|
||||
|
||||
Tries the hosts supplied in order and returns the first successful connection.
|
||||
|
||||
|
||||
### connect_to_host
|
||||
|
||||
`syntax: redis, err = rc:connect_to_host(host)`
|
||||
|
||||
Attempts to connect to the supplied `host`.
|
||||
|
||||
|
||||
### sentinel.get_master
|
||||
|
||||
`syntax: master, err = sentinel.get_master(sentinel, master_name)`
|
||||
|
||||
Given a connected Sentinel instance and a master name, will return the current
|
||||
master Redis instance.
|
||||
|
||||
|
||||
### sentinel.get_slaves
|
||||
|
||||
`syntax: slaves, err = sentinel.get_slaves(sentinel, master_name)`
|
||||
|
||||
Given a connected Sentinel instance and a master name, will return a list of
|
||||
registered slave Redis instances.
|
||||
|
||||
|
||||
# Author
|
||||
|
||||
James Hurst <james@pintsized.co.uk>
|
||||
|
||||
|
||||
# Licence
|
||||
|
||||
This module is licensed under the 2-clause BSD license.
|
||||
|
||||
Copyright (c) James Hurst <james@pintsized.co.uk>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
9
src/deps/src/lua-resty-redis-connector/dist.ini
vendored
Normal file
9
src/deps/src/lua-resty-redis-connector/dist.ini
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
name=lua-resty-redis-connector
|
||||
abstract=Connection utilities for lua-resty-redis, making it easy and reliable to connect to Redis hosts, either directly or via Redis Sentinel.
|
||||
author=James Hurst
|
||||
is_original=yes
|
||||
license=2bsd
|
||||
lib_dir=lib
|
||||
doc_dir=lib
|
||||
repo_link=https://github.com/ledgetech/lua-resty-redis-connector
|
||||
main_module=lib/resty/redis/connector.lua
|
||||
459
src/deps/src/lua-resty-redis-connector/lib/resty/redis/connector.lua
vendored
Normal file
459
src/deps/src/lua-resty-redis-connector/lib/resty/redis/connector.lua
vendored
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
local ipairs, pcall, error, tostring, type, next, setmetatable, getmetatable =
|
||||
ipairs, pcall, error, tostring, type, next, setmetatable, getmetatable
|
||||
|
||||
local ngx_log = ngx.log
|
||||
local ngx_ERR = ngx.ERR
|
||||
local ngx_re_match = ngx.re.match
|
||||
|
||||
local str_find = string.find
|
||||
local str_sub = string.sub
|
||||
local tbl_remove = table.remove
|
||||
local tbl_sort = table.sort
|
||||
local ok, tbl_new = pcall(require, "table.new")
|
||||
if not ok then
|
||||
tbl_new = function (narr, nrec) return {} end -- luacheck: ignore 212
|
||||
end
|
||||
|
||||
local redis = require("resty.redis")
|
||||
redis.add_commands("sentinel")
|
||||
|
||||
local get_master = require("resty.redis.sentinel").get_master
|
||||
local get_slaves = require("resty.redis.sentinel").get_slaves
|
||||
|
||||
|
||||
-- A metatable which prevents undefined fields from being created / accessed
|
||||
local fixed_field_metatable = {
|
||||
__index =
|
||||
function(t, k) -- luacheck: ignore 212
|
||||
error("field " .. tostring(k) .. " does not exist", 3)
|
||||
end,
|
||||
__newindex =
|
||||
function(t, k, v) -- luacheck: ignore 212
|
||||
error("attempt to create new field " .. tostring(k), 3)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
-- Returns a new table, recursively copied from the one given, retaining
|
||||
-- metatable assignment.
|
||||
--
|
||||
-- @param table table to be copied
|
||||
-- @return table
|
||||
local function tbl_copy(orig)
|
||||
local orig_type = type(orig)
|
||||
local copy
|
||||
if orig_type == "table" then
|
||||
copy = {}
|
||||
for orig_key, orig_value in next, orig, nil do
|
||||
copy[tbl_copy(orig_key)] = tbl_copy(orig_value)
|
||||
end
|
||||
setmetatable(copy, tbl_copy(getmetatable(orig)))
|
||||
else -- number, string, boolean, etc
|
||||
copy = orig
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
|
||||
-- Returns a new table, recursively copied from the combination of the given
|
||||
-- table `t1`, with any missing fields copied from `defaults`.
|
||||
--
|
||||
-- If `defaults` is of type "fixed field" and `t1` contains a field name not
|
||||
-- present in the defults, an error will be thrown.
|
||||
--
|
||||
-- @param table t1
|
||||
-- @param table defaults
|
||||
-- @return table a new table, recursively copied and merged
|
||||
local function tbl_copy_merge_defaults(t1, defaults)
|
||||
if t1 == nil then t1 = {} end
|
||||
if defaults == nil then defaults = {} end
|
||||
if type(t1) == "table" and type(defaults) == "table" then
|
||||
local copy = {}
|
||||
for t1_key, t1_value in next, t1, nil do
|
||||
copy[tbl_copy(t1_key)] = tbl_copy_merge_defaults(
|
||||
t1_value, tbl_copy(defaults[t1_key])
|
||||
)
|
||||
end
|
||||
for defaults_key, defaults_value in next, defaults, nil do
|
||||
if t1[defaults_key] == nil then
|
||||
copy[tbl_copy(defaults_key)] = tbl_copy(defaults_value)
|
||||
end
|
||||
end
|
||||
return copy
|
||||
else
|
||||
return t1 -- not a table
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local DEFAULTS = setmetatable({
|
||||
connect_timeout = 100,
|
||||
read_timeout = 1000,
|
||||
send_timeout = 1000,
|
||||
connection_options = {}, -- pool, etc
|
||||
keepalive_timeout = 60000,
|
||||
keepalive_poolsize = 30,
|
||||
|
||||
host = "127.0.0.1",
|
||||
port = 6379,
|
||||
path = "", -- /tmp/redis.sock
|
||||
username = "",
|
||||
password = "",
|
||||
sentinel_username = "",
|
||||
sentinel_password = "",
|
||||
db = 0,
|
||||
url = "", -- DSN url
|
||||
|
||||
master_name = "mymaster",
|
||||
role = "master", -- master | slave
|
||||
sentinels = {},
|
||||
|
||||
-- Redis proxies typically don't support full Redis capabilities
|
||||
connection_is_proxied = false,
|
||||
|
||||
disabled_commands = {},
|
||||
|
||||
}, fixed_field_metatable)
|
||||
|
||||
|
||||
-- This is the set of commands unsupported by Twemproxy
|
||||
local default_disabled_commands = {
|
||||
"migrate", "move", "object", "randomkey", "rename", "renamenx", "scan",
|
||||
"bitop", "msetnx", "blpop", "brpop", "brpoplpush", "psubscribe", "publish",
|
||||
"punsubscribe", "subscribe", "unsubscribe", "discard", "exec", "multi",
|
||||
"unwatch", "watch", "script", "auth", "echo", "select", "bgrewriteaof",
|
||||
"bgsave", "client", "config", "dbsize", "debug", "flushall", "flushdb",
|
||||
"info", "lastsave", "monitor", "save", "shutdown", "slaveof", "slowlog",
|
||||
"sync", "time"
|
||||
}
|
||||
|
||||
|
||||
local _M = {
|
||||
_VERSION = '0.11.0',
|
||||
}
|
||||
|
||||
local mt = { __index = _M }
|
||||
|
||||
|
||||
local function parse_dsn(params)
|
||||
local url = params.url
|
||||
if url and url ~= "" then
|
||||
local url_pattern = [[^(?:(redis|sentinel)://)(?:([^@]*)@)?([^:/]+)(?::(\d+|[msa]+))/?(.*)$]]
|
||||
|
||||
local m, err = ngx_re_match(url, url_pattern, "oj")
|
||||
if not m then
|
||||
return nil, "could not parse DSN: " .. tostring(err)
|
||||
end
|
||||
|
||||
-- TODO: Support a 'protocol' for proxied Redis?
|
||||
local fields
|
||||
if m[1] == "redis" then
|
||||
fields = { "password", "host", "port", "db" }
|
||||
elseif m[1] == "sentinel" then
|
||||
fields = { "password", "master_name", "role", "db" }
|
||||
end
|
||||
|
||||
-- username/password may not be present
|
||||
if #m < 5 then tbl_remove(fields, 1) end
|
||||
|
||||
local roles = { m = "master", s = "slave" }
|
||||
|
||||
local parsed_params = {}
|
||||
|
||||
for i, v in ipairs(fields) do
|
||||
if v == "db" or v == "port" then
|
||||
parsed_params[v] = tonumber(m[i + 1])
|
||||
else
|
||||
parsed_params[v] = m[i + 1]
|
||||
end
|
||||
|
||||
if v == "role" then
|
||||
parsed_params[v] = roles[parsed_params[v]]
|
||||
end
|
||||
end
|
||||
|
||||
local colon_pos = str_find(parsed_params.password or "", ":", 1, true)
|
||||
if colon_pos then
|
||||
parsed_params.username = str_sub(parsed_params.password, 1, colon_pos - 1)
|
||||
parsed_params.password = str_sub(parsed_params.password, colon_pos + 1)
|
||||
end
|
||||
|
||||
return tbl_copy_merge_defaults(params, parsed_params)
|
||||
end
|
||||
|
||||
return params
|
||||
end
|
||||
_M.parse_dsn = parse_dsn
|
||||
|
||||
|
||||
-- Fill out gaps in config with any dsn params
|
||||
local function apply_dsn(config)
|
||||
if config and config.url then
|
||||
local err
|
||||
config, err = parse_dsn(config)
|
||||
if err then ngx_log(ngx_ERR, err) end
|
||||
end
|
||||
return config
|
||||
end
|
||||
|
||||
|
||||
-- For backwards compatability; previously send_timeout was implicitly the
|
||||
-- same as read_timeout. So if only the latter is given, ensure the former
|
||||
-- matches.
|
||||
local function apply_fallback_send_timeout(config)
|
||||
if config and not config.send_timeout and config.read_timeout then
|
||||
config.send_timeout = config.read_timeout
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.new(config)
|
||||
config = apply_dsn(config)
|
||||
apply_fallback_send_timeout(config)
|
||||
|
||||
local ok, config = pcall(tbl_copy_merge_defaults, config, DEFAULTS)
|
||||
if not ok then
|
||||
return nil, config -- err
|
||||
else
|
||||
-- In proxied Redis mode disable default commands
|
||||
if config.connection_is_proxied == true and
|
||||
not next(config.disabled_commands) then
|
||||
|
||||
config.disabled_commands = default_disabled_commands
|
||||
end
|
||||
|
||||
return setmetatable({
|
||||
config = setmetatable(config, fixed_field_metatable)
|
||||
}, mt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.connect(self, params)
|
||||
params = apply_dsn(params)
|
||||
apply_fallback_send_timeout(params)
|
||||
|
||||
params = tbl_copy_merge_defaults(params, self.config)
|
||||
|
||||
if #params.sentinels > 0 then
|
||||
return self:connect_via_sentinel(params)
|
||||
else
|
||||
return self:connect_to_host(params)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function sort_by_localhost(a, b)
|
||||
if a.host == "127.0.0.1" and b.host ~= "127.0.0.1" then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.connect_via_sentinel(self, params)
|
||||
local sentinels = params.sentinels
|
||||
local master_name = params.master_name
|
||||
local role = params.role
|
||||
local db = params.db
|
||||
local username = params.username
|
||||
local password = params.password
|
||||
local sentinel_username = params.sentinel_username
|
||||
local sentinel_password = params.sentinel_password
|
||||
if sentinel_password then
|
||||
for _, host in ipairs(sentinels) do
|
||||
host.username = sentinel_username
|
||||
host.password = sentinel_password
|
||||
end
|
||||
end
|
||||
|
||||
local sentnl, err, previous_errors = self:try_hosts(sentinels)
|
||||
if not sentnl then
|
||||
return nil, err, previous_errors
|
||||
end
|
||||
|
||||
if role == "master" then
|
||||
local master, err = get_master(sentnl, master_name)
|
||||
if not master then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
sentnl:set_keepalive()
|
||||
|
||||
master.db = db
|
||||
master.username = username
|
||||
master.password = password
|
||||
|
||||
local redis, err = self:connect_to_host(master)
|
||||
if not redis then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return redis
|
||||
else
|
||||
-- We want a slave
|
||||
local slaves, err = get_slaves(sentnl, master_name)
|
||||
if not slaves then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
sentnl:set_keepalive()
|
||||
|
||||
-- Put any slaves on 127.0.0.1 at the front
|
||||
tbl_sort(slaves, sort_by_localhost)
|
||||
|
||||
if db or password then
|
||||
for _, slave in ipairs(slaves) do
|
||||
slave.db = db
|
||||
slave.username = username
|
||||
slave.password = password
|
||||
end
|
||||
end
|
||||
|
||||
local slave, err, previous_errors = self:try_hosts(slaves)
|
||||
if not slave then
|
||||
return nil, err, previous_errors
|
||||
end
|
||||
|
||||
return slave
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- In case of errors, returns "nil, err, previous_errors" where err is
|
||||
-- the last error received, and previous_errors is a table of the previous errors.
|
||||
function _M.try_hosts(self, hosts)
|
||||
local errors = tbl_new(#hosts, 0)
|
||||
|
||||
for i, host in ipairs(hosts) do
|
||||
local r, err = self:connect_to_host(host)
|
||||
if r and not err then
|
||||
return r, nil, errors
|
||||
else
|
||||
errors[i] = err
|
||||
end
|
||||
end
|
||||
|
||||
return nil, "no hosts available", errors
|
||||
end
|
||||
|
||||
|
||||
function _M.connect_to_host(self, host)
|
||||
local r = redis.new()
|
||||
|
||||
-- config options in 'host' should override the global defaults
|
||||
-- host contains keys that aren't in config
|
||||
-- this can break tbl_copy_merge_defaults, hence the mannual loop here
|
||||
local config = tbl_copy(self.config)
|
||||
for k, _ in pairs(config) do
|
||||
if host[k] then
|
||||
config[k] = host[k]
|
||||
end
|
||||
end
|
||||
|
||||
r:set_timeouts(
|
||||
config.connect_timeout,
|
||||
config.send_timeout,
|
||||
config.read_timeout
|
||||
)
|
||||
|
||||
-- Stub out methods for disabled commands
|
||||
if next(config.disabled_commands) then
|
||||
for _, cmd in ipairs(config.disabled_commands) do
|
||||
r[cmd] = function()
|
||||
return nil, ("Command "..cmd.." is disabled")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err
|
||||
local path = host.path
|
||||
local opts = config.connection_options
|
||||
if path and path ~= "" then
|
||||
if opts then
|
||||
ok, err = r:connect(path, config.connection_options)
|
||||
else
|
||||
ok, err = r:connect(path)
|
||||
end
|
||||
else
|
||||
if opts then
|
||||
ok, err = r:connect(host.host, host.port, config.connection_options)
|
||||
else
|
||||
ok, err = r:connect(host.host, host.port)
|
||||
end
|
||||
end
|
||||
|
||||
if not ok then
|
||||
return nil, err
|
||||
else
|
||||
local username = host.username
|
||||
local password = host.password
|
||||
if password and password ~= "" then
|
||||
local res
|
||||
-- usernames are supported only on Redis 6+, so use new AUTH form only when absolutely necessary
|
||||
if username and username ~= "" and username ~= "default" then
|
||||
res, err = r:auth(username, password)
|
||||
else
|
||||
res, err = r:auth(password)
|
||||
end
|
||||
if err then
|
||||
return res, err
|
||||
end
|
||||
end
|
||||
|
||||
-- No support for DBs in proxied Redis.
|
||||
if config.connection_is_proxied ~= true and host.db ~= nil then
|
||||
local res, err = r:select(host.db)
|
||||
|
||||
-- SELECT will fail if we are connected to sentinel:
|
||||
-- detect it and ignore error message it that's the case
|
||||
if err and str_find(err, "ERR unknown command") then
|
||||
local role = r:role()
|
||||
if role and role[1] == "sentinel" then
|
||||
err = nil
|
||||
end
|
||||
end
|
||||
if err then
|
||||
return res, err
|
||||
end
|
||||
end
|
||||
return r, nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.set_keepalive(self, redis)
|
||||
-- Restore connection to "NORMAL" before putting into keepalive pool,
|
||||
-- ignoring any errors.
|
||||
-- Proxied Redis does not support transactions.
|
||||
if self.config.connection_is_proxied ~= true then
|
||||
redis:discard()
|
||||
end
|
||||
|
||||
local config = self.config
|
||||
return redis:set_keepalive(
|
||||
config.keepalive_timeout, config.keepalive_poolsize
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
-- Deprecated: use config table in new() or connect() instead.
|
||||
function _M.set_connect_timeout(self, timeout)
|
||||
self.config.connect_timeout = timeout
|
||||
end
|
||||
|
||||
|
||||
-- Deprecated: use config table in new() or connect() instead.
|
||||
function _M.set_read_timeout(self, timeout)
|
||||
self.config.read_timeout = timeout
|
||||
end
|
||||
|
||||
|
||||
-- Deprecated: use config table in new() or connect() instead.
|
||||
function _M.set_connection_options(self, options)
|
||||
self.config.connection_options = options
|
||||
end
|
||||
|
||||
|
||||
return setmetatable(_M, fixed_field_metatable)
|
||||
63
src/deps/src/lua-resty-redis-connector/lib/resty/redis/sentinel.lua
vendored
Normal file
63
src/deps/src/lua-resty-redis-connector/lib/resty/redis/sentinel.lua
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
local ipairs, type = ipairs, type
|
||||
|
||||
local ngx_null = ngx.null
|
||||
|
||||
local tbl_insert = table.insert
|
||||
local ok, tbl_new = pcall(require, "table.new")
|
||||
if not ok then
|
||||
tbl_new = function (narr, nrec) return {} end -- luacheck: ignore 212
|
||||
end
|
||||
|
||||
|
||||
local _M = {
|
||||
_VERSION = '0.11.0'
|
||||
}
|
||||
|
||||
|
||||
function _M.get_master(sentinel, master_name)
|
||||
local res, err = sentinel:sentinel(
|
||||
"get-master-addr-by-name",
|
||||
master_name
|
||||
)
|
||||
if res and res ~= ngx_null and res[1] and res[2] then
|
||||
return { host = res[1], port = res[2] }
|
||||
elseif res == ngx_null then
|
||||
return nil, "invalid master name"
|
||||
else
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.get_slaves(sentinel, master_name)
|
||||
local res, err = sentinel:sentinel("slaves", master_name)
|
||||
|
||||
if res and type(res) == "table" then
|
||||
local hosts = tbl_new(#res, 0)
|
||||
for _,slave in ipairs(res) do
|
||||
local num_recs = #slave
|
||||
local host = tbl_new(0, num_recs + 1)
|
||||
for i = 1, num_recs, 2 do
|
||||
host[slave[i]] = slave[i + 1]
|
||||
end
|
||||
|
||||
local master_link_status_ok = host["master-link-status"] == "ok"
|
||||
local is_down = host["flags"] and (string.find(host["flags"],"s_down")
|
||||
or string.find(host["flags"],"disconnected"))
|
||||
if master_link_status_ok and not is_down then
|
||||
host.host = host.ip -- for parity with other functions
|
||||
tbl_insert(hosts, host)
|
||||
end
|
||||
end
|
||||
if hosts[1] ~= nil then
|
||||
return hosts
|
||||
else
|
||||
return nil, "no slaves available"
|
||||
end
|
||||
else
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return _M
|
||||
27
src/deps/src/lua-resty-redis-connector/lua-resty-redis-connector-0.11.0-0.rockspec
vendored
Normal file
27
src/deps/src/lua-resty-redis-connector/lua-resty-redis-connector-0.11.0-0.rockspec
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package = "lua-resty-redis-connector"
|
||||
version = "0.11.0-0"
|
||||
source = {
|
||||
url = "git://github.com/ledgetech/lua-resty-redis-connector",
|
||||
tag = "v0.11.0"
|
||||
}
|
||||
description = {
|
||||
summary = "Connection utilities for lua-resty-redis.",
|
||||
detailed = [[
|
||||
Connection utilities for lua-resty-redis, making it easy and
|
||||
reliable to connect to Redis hosts, either directly or via Redis
|
||||
Sentinel.
|
||||
]],
|
||||
homepage = "https://github.com/ledgetech/lua-resty-redis-connector",
|
||||
license = "2-clause BSD",
|
||||
maintainer = "James Hurst <james@pintsized.co.uk>"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["resty.redis.connector"] = "lib/resty/redis/connector.lua",
|
||||
["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua"
|
||||
}
|
||||
}
|
||||
229
src/deps/src/lua-resty-redis-connector/t/config.t
vendored
Normal file
229
src/deps/src/lua-resty-redis-connector/t/config.t
vendored
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
use Test::Nginx::Socket::Lua;
|
||||
use Cwd qw(cwd);
|
||||
|
||||
repeat_each(2);
|
||||
|
||||
plan tests => repeat_each() * blocks() * 2;
|
||||
|
||||
my $pwd = cwd();
|
||||
|
||||
our $HttpConfig = qq{
|
||||
lua_package_path "$pwd/lib/?.lua;;";
|
||||
lua_socket_log_errors Off;
|
||||
|
||||
init_by_lua_block {
|
||||
require("luacov.runner").init()
|
||||
}
|
||||
};
|
||||
|
||||
$ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
|
||||
|
||||
no_long_string();
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: Default config
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = assert(require("resty.redis.connector").new())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 2: Defaults via new
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local config = {
|
||||
connect_timeout = 500,
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 6,
|
||||
}
|
||||
local rc = require("resty.redis.connector").new(config)
|
||||
|
||||
assert(config ~= rc.config, "config should not equal rc.config")
|
||||
assert(rc.config.connect_timeout == 500, "connect_timeout should be 500")
|
||||
assert(rc.config.db == 6, "db should be 6")
|
||||
assert(rc.config.role == "master", "role should be master")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 3: Config via connect still overrides
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
connect_timeout = 500,
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 6,
|
||||
keepalive_poolsize = 10,
|
||||
})
|
||||
|
||||
assert(config ~= rc.config, "config should not equal rc.config")
|
||||
assert(rc.config.connect_timeout == 500, "connect_timeout should be 500")
|
||||
assert(rc.config.db == 6, "db should be 6")
|
||||
assert(rc.config.role == "master", "role should be master")
|
||||
assert(rc.config.keepalive_poolsize == 10,
|
||||
"keepalive_poolsize should be 10")
|
||||
|
||||
local redis, err = rc:connect({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
disabled_commands = { "set" }
|
||||
})
|
||||
|
||||
if not redis or err then
|
||||
ngx.log(ngx.ERR, "connect failed: ", err)
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = redis:set("foo", "bar")
|
||||
assert( ok == nil and (string.find(err, "disabled") ~= nil) , "Disabled commands not passed through" )
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 4: Unknown config errors, all config does not error
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc, err = require("resty.redis.connector").new({
|
||||
connect_timeout = 500,
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 6,
|
||||
foo = "bar",
|
||||
})
|
||||
|
||||
assert(rc == nil, "rc should be nil")
|
||||
assert(string.find(err, "field foo does not exist"),
|
||||
"err should contain error")
|
||||
|
||||
-- Provide all options, without errors
|
||||
|
||||
assert(require("resty.redis.connector").new({
|
||||
connect_timeout = 100,
|
||||
send_timeout = 500,
|
||||
read_timeout = 1000,
|
||||
connection_options = { pool = "<host>::<port>" },
|
||||
keepalive_timeout = 60000,
|
||||
keepalive_poolsize = 30,
|
||||
|
||||
host = "127.0.0.1",
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
path = "",
|
||||
username = "",
|
||||
password = "",
|
||||
db = 0,
|
||||
|
||||
url = "",
|
||||
|
||||
master_name = "mymaster",
|
||||
role = "master",
|
||||
sentinels = {},
|
||||
}), "new should return positively")
|
||||
|
||||
-- Provide all options via connect, without errors
|
||||
|
||||
local rc = require("resty.redis.connector").new()
|
||||
|
||||
assert(rc:connect({
|
||||
connect_timeout = 100,
|
||||
send_timeout = 500,
|
||||
read_timeout = 1000,
|
||||
connection_options = { pool = "<host>::<port>" },
|
||||
keepalive_timeout = 60000,
|
||||
keepalive_poolsize = 30,
|
||||
|
||||
host = "127.0.0.1",
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
path = "",
|
||||
username = "",
|
||||
password = "",
|
||||
db = 0,
|
||||
|
||||
url = "",
|
||||
|
||||
master_name = "mymaster",
|
||||
role = "master",
|
||||
sentinels = {},
|
||||
}), "rc:connect should return positively")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 5: timeout defaults
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
-- global defaults
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 6,
|
||||
keepalive_poolsize = 10,
|
||||
})
|
||||
|
||||
assert(rc.config.connect_timeout == 100, "connect_timeout should be 100")
|
||||
assert(rc.config.send_timeout == 1000, "send_timeout should be 1000")
|
||||
assert(rc.config.read_timeout == 1000, "read_timeout should be 1000")
|
||||
|
||||
local redis = assert(rc:connect(), "rc:connect should return positively")
|
||||
assert(redis:set("foo", "bar"))
|
||||
rc:set_keepalive(redis)
|
||||
|
||||
-- send_timeout defaults to read_timeout
|
||||
rc = require("resty.redis.connector").new({
|
||||
read_timeout = 500,
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 6,
|
||||
keepalive_poolsize = 10,
|
||||
})
|
||||
|
||||
assert(rc.config.connect_timeout == 100, "connect_timeout should be 100")
|
||||
assert(rc.config.send_timeout == 500, "send_timeout should be 500")
|
||||
assert(rc.config.read_timeout == 500, "read_timeout should be 500")
|
||||
|
||||
local redis = assert(rc:connect(), "rc:connect should return positively")
|
||||
assert(redis:set("foo", "bar"))
|
||||
rc:set_keepalive(redis)
|
||||
|
||||
-- send_timeout can be set separately from read_timeout
|
||||
rc = require("resty.redis.connector").new({
|
||||
send_timeout = 500,
|
||||
read_timeout = 200,
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 6,
|
||||
keepalive_poolsize = 10,
|
||||
})
|
||||
|
||||
assert(rc.config.connect_timeout == 100, "connect_timeout should be 100")
|
||||
assert(rc.config.send_timeout == 500, "send_timeout should be 500")
|
||||
assert(rc.config.read_timeout == 200, "read_timeout should be 200")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
442
src/deps/src/lua-resty-redis-connector/t/connector.t
vendored
Normal file
442
src/deps/src/lua-resty-redis-connector/t/connector.t
vendored
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
use Test::Nginx::Socket 'no_plan';
|
||||
use Cwd qw(cwd);
|
||||
|
||||
my $pwd = cwd();
|
||||
|
||||
our $HttpConfig = qq{
|
||||
lua_package_path "$pwd/lib/?.lua;;";
|
||||
lua_socket_log_errors Off;
|
||||
|
||||
init_by_lua_block {
|
||||
require("luacov.runner").init()
|
||||
}
|
||||
};
|
||||
|
||||
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
|
||||
$ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
|
||||
$ENV{TEST_NGINX_REDIS_PORT_AUTH} ||= 6393;
|
||||
$ENV{TEST_NGINX_REDIS_SOCKET} ||= 'unix://tmp/redis/redis.sock';
|
||||
|
||||
no_long_string();
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: basic connect
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT
|
||||
})
|
||||
|
||||
local redis, err = assert(rc:connect(params),
|
||||
"connect should return positively")
|
||||
|
||||
assert(redis:set("dog", "an animal"),
|
||||
"redis:set should return positively")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 2: try_hosts
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors off;
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
connect_timeout = 100,
|
||||
})
|
||||
|
||||
local hosts = {
|
||||
{ host = "127.0.0.1", port = 1 },
|
||||
{ host = "127.0.0.1", port = 2 },
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT },
|
||||
}
|
||||
|
||||
local redis, err, previous_errors = rc:try_hosts(hosts)
|
||||
assert(redis and not err,
|
||||
"try_hosts should return a connection and no error")
|
||||
|
||||
assert(string.len(previous_errors[1]) > 0,
|
||||
"previous_errors[1] should contain an error")
|
||||
assert(string.len(previous_errors[2]) > 0,
|
||||
"previous_errors[2] should contain an error")
|
||||
|
||||
assert(redis:set("dog", "an animal"),
|
||||
"redis connection should be working")
|
||||
|
||||
redis:close()
|
||||
|
||||
local hosts = {
|
||||
{ host = "127.0.0.1", port = 1 },
|
||||
{ host = "127.0.0.1", port = 2 },
|
||||
}
|
||||
|
||||
local redis, err, previous_errors = rc:try_hosts(hosts)
|
||||
assert(not redis and err == "no hosts available",
|
||||
"no available hosts should return an error")
|
||||
|
||||
assert(string.len(previous_errors[1]) > 0,
|
||||
"previous_errors[1] should contain an error")
|
||||
assert(string.len(previous_errors[2]) > 0,
|
||||
"previous_errors[2] should contain an error")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 3: connect_to_host
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
|
||||
local host = { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT }
|
||||
|
||||
local redis, err = rc:connect_to_host(host)
|
||||
assert(redis and not err,
|
||||
"connect_to_host should return positively")
|
||||
|
||||
assert(redis:set("dog", "an animal"),
|
||||
"redis connection should be working")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 4: connect_to_host options ignore defaults
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 2,
|
||||
})
|
||||
|
||||
local redis, err = assert(rc:connect_to_host({
|
||||
host = "127.0.0.1",
|
||||
db = 1,
|
||||
port = $TEST_NGINX_REDIS_PORT
|
||||
}), "connect_to_host should return positively")
|
||||
|
||||
assert(redis:set("dog", "an animal") == "OK",
|
||||
"set should return 'OK'")
|
||||
|
||||
redis:select(2)
|
||||
assert(redis:get("dog") == ngx.null,
|
||||
"dog should not exist in db 2")
|
||||
|
||||
redis:select(1)
|
||||
assert(redis:get("dog") == "an animal",
|
||||
"dog should be 'an animal' in db 1")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 5: Test set_keepalive method
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
})
|
||||
|
||||
local redis = assert(rc:connect(),
|
||||
"rc:connect should return positively")
|
||||
local ok, err = rc:set_keepalive(redis)
|
||||
assert(not err, "set_keepalive error should be nil")
|
||||
|
||||
local ok, err = redis:set("foo", "bar")
|
||||
assert(not ok, "ok should be nil")
|
||||
assert(string.find(err, "closed"), "error should contain 'closed'")
|
||||
|
||||
local redis = assert(rc:connect(), "connect should return positively")
|
||||
assert(redis:subscribe("channel"), "subscribe should return positively")
|
||||
|
||||
local ok, err = rc:set_keepalive(redis)
|
||||
assert(not ok, "ok should be nil")
|
||||
assert(string.find(err, "subscribed state"),
|
||||
"error should contain 'subscribed state'")
|
||||
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 6: password
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
password = "foo",
|
||||
})
|
||||
|
||||
local redis, err = rc:connect()
|
||||
assert(not redis and string.find(err, "ERR") and string.find(err, "AUTH"),
|
||||
"connect should fail with password error")
|
||||
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
=== TEST 7: username and password
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
username = "x",
|
||||
password = "foo",
|
||||
})
|
||||
|
||||
local redis, err = rc:connect()
|
||||
assert(not redis and string.find(err, "WRONGPASS"),
|
||||
"connect should fail with invalid username-password error")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 8: Bad unix domain socket path should fail
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local redis, err = require("resty.redis.connector").new({
|
||||
path = "unix://GARBAGE_PATH_AKFDKAJSFKJSAFLKJSADFLKJSANCKAJSNCKJSANCLKAJS",
|
||||
}):connect()
|
||||
|
||||
assert(not redis and err == "no such file or directory",
|
||||
"bad domain socket should fail")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 8.1: Good unix domain socket path should succeed
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local redis, err = require("resty.redis.connector").new({
|
||||
path = "$TEST_NGINX_REDIS_SOCKET",
|
||||
}):connect()
|
||||
|
||||
assert (redis and not err,
|
||||
"connection should be valid")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 9: parse_dsn
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector")
|
||||
|
||||
local user_params = {
|
||||
url = "redis://foo@127.0.0.1:$TEST_NGINX_REDIS_PORT/4"
|
||||
}
|
||||
|
||||
local params, err = rc.parse_dsn(user_params)
|
||||
assert(params and not err,
|
||||
"url should parse without error: " .. tostring(err))
|
||||
|
||||
assert(params.host == "127.0.0.1", "host should be localhost")
|
||||
assert(tonumber(params.port) == $TEST_NGINX_REDIS_PORT,
|
||||
"port should be $TEST_NGINX_REDIS_PORT")
|
||||
assert(tonumber(params.db) == 4, "db should be 4")
|
||||
assert(params.password == "foo", "password should be foo")
|
||||
|
||||
|
||||
local user_params = {
|
||||
url = "sentinel://foo:bar@foomaster:s/2"
|
||||
}
|
||||
|
||||
local params, err = rc.parse_dsn(user_params)
|
||||
assert(params and not err,
|
||||
"url should parse without error: " .. tostring(err))
|
||||
|
||||
assert(params.master_name == "foomaster", "master_name should be foomaster")
|
||||
assert(params.role == "slave", "role should be slave")
|
||||
assert(tonumber(params.db) == 2, "db should be 2")
|
||||
assert(params.username == "foo", "username should be foo")
|
||||
assert(params.password == "bar", "password should be bar")
|
||||
|
||||
|
||||
local params = {
|
||||
url = "sentinels:/wrongformat",
|
||||
}
|
||||
|
||||
local ok, err = rc.parse_dsn(params)
|
||||
assert(not ok and err == "could not parse DSN: nil",
|
||||
"url should fail to parse")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 10: params override dsn components
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector")
|
||||
|
||||
local user_params = {
|
||||
url = "redis://foo@127.0.0.1:$TEST_NGINX_REDIS_PORT/4",
|
||||
db = 2,
|
||||
password = "bar",
|
||||
host = "example.com",
|
||||
}
|
||||
|
||||
local params, err = rc.parse_dsn(user_params)
|
||||
assert(params and not err,
|
||||
"url should parse without error: " .. tostring(err))
|
||||
|
||||
assert(tonumber(params.db) == 2, "db should be 2")
|
||||
assert(params.password == "bar", "password should be bar")
|
||||
assert(params.host == "example.com", "host should be example.com")
|
||||
|
||||
assert(tonumber(params.port) == $TEST_NGINX_REDIS_PORT, "port should still be $TEST_NGINX_REDIS_PORT")
|
||||
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 11: Integration test for parse_dsn
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local user_params = {
|
||||
url = "redis://foo.example:$TEST_NGINX_REDIS_PORT/4",
|
||||
db = 2,
|
||||
host = "127.0.0.1",
|
||||
}
|
||||
|
||||
local rc, err = require("resty.redis.connector").new(user_params)
|
||||
assert(rc and not err, "new should return positively")
|
||||
|
||||
local redis, err = rc:connect()
|
||||
assert(redis and not err, "connect should return positively")
|
||||
assert(redis:set("cat", "dog") and redis:get("cat") == "dog")
|
||||
|
||||
local redis, err = rc:connect({
|
||||
url = "redis://foo.example:$TEST_NGINX_REDIS_PORT/4",
|
||||
db = 2,
|
||||
host = "127.0.0.1",
|
||||
})
|
||||
assert(redis and not err, "connect should return positively")
|
||||
assert(redis:set("cat", "dog") and redis:get("cat") == "dog")
|
||||
|
||||
|
||||
local rc2, err = require("resty.redis.connector").new()
|
||||
local redis, err = rc2:connect({
|
||||
url = "redis://foo.example:$TEST_NGINX_REDIS_PORT/4",
|
||||
db = 2,
|
||||
host = "127.0.0.1",
|
||||
})
|
||||
assert(redis and not err, "connect should return positively")
|
||||
assert(redis:set("cat", "dog") and redis:get("cat") == "dog")
|
||||
|
||||
local redis, err = rc2:connect({
|
||||
url = "redis://redisuser:redisuserpass@127.0.0.1:$TEST_NGINX_REDIS_PORT_AUTH/"
|
||||
})
|
||||
assert(redis and not err, "connect should return positively")
|
||||
local username = assert(redis:acl("whoami"))
|
||||
assert(username == "redisuser", "should connect as 'redisuser' but got " .. tostring(username))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 12: DSN without DB
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local user_params = {
|
||||
url = "redis://foo.example:$TEST_NGINX_REDIS_PORT",
|
||||
host = "127.0.0.1",
|
||||
}
|
||||
|
||||
local rc, err = require("resty.redis.connector").new(user_params)
|
||||
assert(rc and not err, "new should return positively")
|
||||
|
||||
local redis, err = rc:connect()
|
||||
assert(redis and not err, "connect should return positively")
|
||||
assert(redis:set("cat", "dog") and redis:get("cat") == "dog")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
156
src/deps/src/lua-resty-redis-connector/t/proxy.t
vendored
Normal file
156
src/deps/src/lua-resty-redis-connector/t/proxy.t
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
use Test::Nginx::Socket 'no_plan';
|
||||
use Cwd qw(cwd);
|
||||
|
||||
my $pwd = cwd();
|
||||
|
||||
our $HttpConfig = qq{
|
||||
lua_package_path "$pwd/lib/?.lua;;";
|
||||
lua_socket_log_errors Off;
|
||||
|
||||
init_by_lua_block {
|
||||
require("luacov.runner").init()
|
||||
}
|
||||
};
|
||||
|
||||
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
|
||||
$ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
|
||||
|
||||
no_long_string();
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: Proxy mode disables commands
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
connection_is_proxied = true
|
||||
})
|
||||
|
||||
local redis, err = assert(rc:connect(params),
|
||||
"connect should return positively")
|
||||
|
||||
assert(redis:set("dog", "an animal"),
|
||||
"redis:set should return positively")
|
||||
|
||||
local ok, err = redis:multi()
|
||||
assert(ok == nil, "redis:multi should return nil")
|
||||
assert(err == "Command multi is disabled")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 2: Proxy mode disables custom commands
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
connection_is_proxied = true,
|
||||
disabled_commands = { "foobar", "hget"}
|
||||
})
|
||||
|
||||
local redis, err = assert(rc:connect(params),
|
||||
"connect should return positively")
|
||||
|
||||
assert(redis:set("dog", "an animal"),
|
||||
"redis:set should return positively")
|
||||
|
||||
assert(redis:multi(),
|
||||
"redis:multi should return positively")
|
||||
|
||||
local ok, err = redis:hget()
|
||||
assert(ok == nil, "redis:hget should return nil")
|
||||
assert(err == "Command hget is disabled")
|
||||
|
||||
local ok, err = redis:foobar()
|
||||
assert(ok == nil, "redis:foobar should return nil")
|
||||
assert(err == "Command foobar is disabled")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
=== TEST 3: Proxy mode does not switch DB
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local redis = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
db = 2
|
||||
}):connect()
|
||||
|
||||
local proxy = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
connection_is_proxied = true,
|
||||
db = 2
|
||||
}):connect()
|
||||
|
||||
assert(redis:set("proxy", "test"),
|
||||
"redis:set should return positively")
|
||||
|
||||
assert(proxy:get("proxy") == ngx.null,
|
||||
"proxy key should not exist in proxy")
|
||||
|
||||
redis:seelct(2)
|
||||
assert(redis:get("proxy") == "test",
|
||||
"proxy key should be 'test' in db 1")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 4: Commands are disabled without proxy mode
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new({
|
||||
port = $TEST_NGINX_REDIS_PORT,
|
||||
disabled_commands = { "foobar", "hget"}
|
||||
})
|
||||
|
||||
local redis, err = assert(rc:connect(params),
|
||||
"connect should return positively")
|
||||
|
||||
assert(redis:set("dog", "an animal"),
|
||||
"redis:set should return positively")
|
||||
|
||||
assert(redis:multi(),
|
||||
"redis:multi should return positively")
|
||||
|
||||
local ok, err = redis:hget()
|
||||
assert(ok == nil, "redis:hget should return nil")
|
||||
assert(err == "Command hget is disabled")
|
||||
|
||||
local ok, err = redis:foobar()
|
||||
assert(ok == nil, "redis:foobar should return nil")
|
||||
assert(err == "Command foobar is disabled")
|
||||
|
||||
redis:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
279
src/deps/src/lua-resty-redis-connector/t/sentinel.t
vendored
Normal file
279
src/deps/src/lua-resty-redis-connector/t/sentinel.t
vendored
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
use Test::Nginx::Socket 'no_plan';
|
||||
use Cwd qw(cwd);
|
||||
|
||||
my $pwd = cwd();
|
||||
|
||||
our $HttpConfig = qq{
|
||||
lua_package_path "$pwd/lib/?.lua;;";
|
||||
lua_socket_log_errors Off;
|
||||
|
||||
init_by_lua_block {
|
||||
require("luacov.runner").init()
|
||||
}
|
||||
};
|
||||
|
||||
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
|
||||
$ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
|
||||
$ENV{TEST_NGINX_REDIS_PORT_SL1} ||= 6381;
|
||||
$ENV{TEST_NGINX_REDIS_PORT_SL2} ||= 6382;
|
||||
$ENV{TEST_NGINX_SENTINEL_PORT1} ||= 6390;
|
||||
$ENV{TEST_NGINX_SENTINEL_PORT2} ||= 6391;
|
||||
$ENV{TEST_NGINX_SENTINEL_PORT3} ||= 6392;
|
||||
$ENV{TEST_NGINX_SENTINEL_PORT_AUTH} ||= 6393;
|
||||
|
||||
no_long_string();
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: Get the master
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
local rs = require("resty.redis.sentinel")
|
||||
|
||||
local sentinel, err = rc:connect{ url = "redis://127.0.0.1:$TEST_NGINX_SENTINEL_PORT1" }
|
||||
assert(sentinel and not err, "sentinel should connect without errors but got " .. tostring(err))
|
||||
|
||||
local master, err = rs.get_master(sentinel, "mymaster")
|
||||
|
||||
assert(master and not err, "get_master should return the master")
|
||||
|
||||
assert(master.host == "127.0.0.1" and tonumber(master.port) == $TEST_NGINX_REDIS_PORT,
|
||||
"host should be 127.0.0.1 and port should be $TEST_NGINX_REDIS_PORT")
|
||||
|
||||
master, err = rs.get_master(sentinel, "invalid-mymaster")
|
||||
|
||||
assert(not master and err, "invalid master name should result in error")
|
||||
|
||||
sentinel:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 1b: Get the master directly
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
|
||||
local master, err = rc:connect({
|
||||
url = "sentinel://mymaster:m/3",
|
||||
sentinels = {
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT1 }
|
||||
}
|
||||
})
|
||||
|
||||
assert(master and not err, "get_master should return the master")
|
||||
assert(master:set("foo", "bar"), "set should run without error")
|
||||
assert(master:get("foo") == "bar", "get(foo) should return bar")
|
||||
|
||||
master:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 2: Get slaves
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
local rs = require("resty.redis.sentinel")
|
||||
|
||||
local sentinel, err = rc:connect{ url = "redis://127.0.0.1:$TEST_NGINX_SENTINEL_PORT1" }
|
||||
assert(sentinel and not err, "sentinel should connect without error")
|
||||
|
||||
local slaves, err = rs.get_slaves(sentinel, "mymaster")
|
||||
|
||||
assert(slaves and not err, "slaves should be returned without error")
|
||||
|
||||
local slaveports = { ["$TEST_NGINX_REDIS_PORT_SL1"] = false, ["$TEST_NGINX_REDIS_PORT_SL2"] = false }
|
||||
|
||||
for _,slave in ipairs(slaves) do
|
||||
slaveports[tostring(slave.port)] = true
|
||||
end
|
||||
|
||||
assert(slaveports["$TEST_NGINX_REDIS_PORT_SL1"] == true and slaveports["$TEST_NGINX_REDIS_PORT_SL2"] == true,
|
||||
"slaves should both be found")
|
||||
|
||||
slaves, err = rs.get_slaves(sentinel, "invalid-mymaster")
|
||||
|
||||
assert(not slaves and err, "invalid master name should result in error")
|
||||
|
||||
sentinel:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 3: Get only healthy slaves
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
|
||||
local sentinel, err = rc:connect({ url = "redis://127.0.0.1:$TEST_NGINX_SENTINEL_PORT1" })
|
||||
assert(sentinel and not err, "sentinel should connect without error")
|
||||
|
||||
local slaves, err = require("resty.redis.sentinel").get_slaves(
|
||||
sentinel,
|
||||
"mymaster"
|
||||
)
|
||||
|
||||
assert(slaves and not err, "slaves should be returned without error")
|
||||
|
||||
local slaveports = { ["$TEST_NGINX_REDIS_PORT_SL1"] = false, ["$TEST_NGINX_REDIS_PORT_SL2"] = false }
|
||||
|
||||
for _,slave in ipairs(slaves) do
|
||||
slaveports[tostring(slave.port)] = true
|
||||
end
|
||||
|
||||
assert(slaveports["$TEST_NGINX_REDIS_PORT_SL1"] == true and slaveports["$TEST_NGINX_REDIS_PORT_SL2"] == true,
|
||||
"slaves should both be found")
|
||||
|
||||
-- connect to one and remove it
|
||||
local r = require("resty.redis.connector").new():connect({
|
||||
port = $TEST_NGINX_REDIS_PORT_SL1,
|
||||
})
|
||||
r:slaveof("127.0.0.1", 7000)
|
||||
|
||||
ngx.sleep(9)
|
||||
|
||||
local slaves, err = require("resty.redis.sentinel").get_slaves(
|
||||
sentinel,
|
||||
"mymaster"
|
||||
)
|
||||
|
||||
assert(slaves and not err, "slaves should be returned without error")
|
||||
|
||||
local slaveports = { ["$TEST_NGINX_REDIS_PORT_SL1"] = false, ["$TEST_NGINX_REDIS_PORT_SL2"] = false }
|
||||
|
||||
for _,slave in ipairs(slaves) do
|
||||
slaveports[tostring(slave.port)] = true
|
||||
end
|
||||
|
||||
assert(slaveports["$TEST_NGINX_REDIS_PORT_SL1"] == false and slaveports["$TEST_NGINX_REDIS_PORT_SL2"] == true,
|
||||
"only $TEST_NGINX_REDIS_PORT_SL2 should be found")
|
||||
|
||||
r:slaveof("127.0.0.1", $TEST_NGINX_REDIS_PORT)
|
||||
|
||||
sentinel:close()
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- timeout: 10
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 4: connector.connect_via_sentinel
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
|
||||
local params = {
|
||||
sentinels = {
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT1 },
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT2 },
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT3 },
|
||||
},
|
||||
master_name = "mymaster",
|
||||
role = "master",
|
||||
}
|
||||
|
||||
local redis, err = rc:connect_via_sentinel(params)
|
||||
assert(redis and not err, "redis should connect without error")
|
||||
|
||||
params.role = "slave"
|
||||
|
||||
local redis, err = rc:connect_via_sentinel(params)
|
||||
assert(redis and not err, "redis should connect without error")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
=== TEST 5: regression for slave sorting (iss12)
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
lua_socket_log_errors Off;
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
|
||||
local params = {
|
||||
sentinels = {
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT1 },
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT2 },
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT3 },
|
||||
},
|
||||
master_name = "mymaster",
|
||||
role = "slave",
|
||||
}
|
||||
|
||||
-- hotwire get_slaves to expose sorting issue
|
||||
local sentinel = require("resty.redis.sentinel")
|
||||
sentinel.get_slaves = function()
|
||||
return {
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT_SL1 },
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT_SL2 },
|
||||
{ host = "134.123.51.2", port = $TEST_NGINX_REDIS_PORT_SL1 },
|
||||
}
|
||||
end
|
||||
|
||||
local redis, err = rc:connect_via_sentinel(params)
|
||||
assert(redis and not err, "redis should connect without error")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
=== TEST 6: connect with acl
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local rc = require("resty.redis.connector").new()
|
||||
local redis, err = rc:connect({
|
||||
username = "redisuser",
|
||||
password = "redisuserpass",
|
||||
sentinels = {
|
||||
{ host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT_AUTH }
|
||||
},
|
||||
master_name = "mymaster",
|
||||
sentinel_username = "sentineluser",
|
||||
sentinel_username = "sentineluserpass",
|
||||
})
|
||||
assert(redis and not err, "redis should connect without error")
|
||||
local username = assert(redis:acl("whoami"))
|
||||
assert(username == "redisuser", "should connect as 'redisuser' but got " .. tostring(username))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
63
src/deps/src/lua-resty-redis-connector/util/lua-releng
vendored
Executable file
63
src/deps/src/lua-resty-redis-connector/util/lua-releng
vendored
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub file_contains ($$);
|
||||
|
||||
my $version;
|
||||
for my $file (map glob, qw{ *.lua lib/*.lua lib/*/*.lua lib/*/*/*.lua lib/*/*/*/*.lua lib/*/*/*/*/*.lua }) {
|
||||
# Check the sanity of each .lua file
|
||||
open my $in, $file or
|
||||
die "ERROR: Can't open $file for reading: $!\n";
|
||||
my $found_ver;
|
||||
while (<$in>) {
|
||||
my ($ver, $skipping);
|
||||
if (/(?x) (?:_VERSION) \s* = .*? ([\d\.]*\d+) (.*? SKIP)?/) {
|
||||
my $orig_ver = $ver = $1;
|
||||
$found_ver = 1;
|
||||
# $skipping = $2;
|
||||
$ver =~ s{^(\d+)\.(\d{3})(\d{3})$}{join '.', int($1), int($2), int($3)}e;
|
||||
warn "$file: $orig_ver ($ver)\n";
|
||||
|
||||
} elsif (/(?x) (?:_VERSION) \s* = \s* ([a-zA-Z_]\S*)/) {
|
||||
warn "$file: $1\n";
|
||||
$found_ver = 1;
|
||||
last;
|
||||
}
|
||||
|
||||
if ($ver and $version and !$skipping) {
|
||||
if ($version ne $ver) {
|
||||
# die "$file: $ver != $version\n";
|
||||
}
|
||||
} elsif ($ver and !$version) {
|
||||
$version = $ver;
|
||||
}
|
||||
}
|
||||
if (!$found_ver) {
|
||||
warn "WARNING: No \"_VERSION\" or \"version\" field found in `$file`.\n";
|
||||
}
|
||||
close $in;
|
||||
|
||||
#print "Checking use of Lua global variables in file $file ...\n";
|
||||
system("luac -p -l $file | grep ETGLOBAL | grep -vE '(require|type|tostring|error|ngx|ndk|jit|setmetatable|getmetatable|string|table|io|os|print|tonumber|math|pcall|xpcall|unpack|pairs|ipairs|assert|module|package|coroutine|[gs]etfenv|next|select|rawset|rawget|debug)\$'");
|
||||
#file_contains($file, "attempt to write to undeclared variable");
|
||||
system("grep -H -n -E --color '.{120}' $file");
|
||||
}
|
||||
|
||||
sub file_contains ($$) {
|
||||
my ($file, $regex) = @_;
|
||||
open my $in, $file
|
||||
or die "Cannot open $file fo reading: $!\n";
|
||||
my $content = do { local $/; <$in> };
|
||||
close $in;
|
||||
#print "$content";
|
||||
return scalar ($content =~ /$regex/);
|
||||
}
|
||||
|
||||
if (-d 't') {
|
||||
for my $file (map glob, qw{ t/*.t t/*/*.t t/*/*/*.t }) {
|
||||
system(qq{grep -H -n --color -E '\\--- ?(ONLY|LAST)' $file});
|
||||
}
|
||||
}
|
||||
|
||||
27
tests/core/redis-sentinel/Dockerfile
Normal file
27
tests/core/redis-sentinel/Dockerfile
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
FROM python:3.12.1-alpine3.18@sha256:af0d8da43677e3000ebdf4045508d891a87e7bd2d3ec87bc6e40403be97291b8
|
||||
|
||||
# Install firefox and geckodriver
|
||||
RUN apk add --no-cache --virtual .build-deps curl grep zip wget && \
|
||||
apk add --no-cache firefox
|
||||
|
||||
# Installing geckodriver for firefox...
|
||||
RUN GECKODRIVER_VERSION=`curl -i https://github.com/mozilla/geckodriver/releases/latest | grep -Po 'v[0-9]+\.[0-9]+\.[0-9]+'` && \
|
||||
wget -O geckodriver.tar.gz -w 5 https://github.com/mozilla/geckodriver/releases/download/$GECKODRIVER_VERSION/geckodriver-$GECKODRIVER_VERSION-linux64.tar.gz && \
|
||||
tar -C /usr/local/bin -xzvf geckodriver.tar.gz && \
|
||||
chmod +x /usr/local/bin/geckodriver && \
|
||||
rm geckodriver.tar.gz
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN MAKEFLAGS="-j $(nproc)" pip install --no-cache-dir --require-hashes --no-deps -r requirements.txt && \
|
||||
rm -f requirements.txt
|
||||
|
||||
WORKDIR /opt/tests
|
||||
|
||||
COPY main.py .
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT [ "python3", "main.py" ]
|
||||
9
tests/core/redis-sentinel/Dockerfile.redis
Normal file
9
tests/core/redis-sentinel/Dockerfile.redis
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
FROM redis:7-alpine@sha256:2d148c557c85309c7cf1bbf15ebc21d5fc370ab1cb913a6c19b74bd29d10801c
|
||||
|
||||
RUN apk add --no-cache bash openssl
|
||||
|
||||
COPY entrypoint.sh .
|
||||
|
||||
RUN chmod +x entrypoint.sh
|
||||
|
||||
ENTRYPOINT [ "./entrypoint.sh" ]
|
||||
23
tests/core/redis-sentinel/docker-compose.test.yml
Normal file
23
tests/core/redis-sentinel/docker-compose.test.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: "3.5"
|
||||
|
||||
services:
|
||||
tests:
|
||||
build: .
|
||||
environment:
|
||||
PYTHONUNBUFFERED: "1"
|
||||
USE_REVERSE_SCAN: "no"
|
||||
USE_ANTIBOT: "no"
|
||||
|
||||
REDIS_SENTINEL_HOSTS: "bw-sentinel-1 bw-sentinel-2 bw-sentinel-3"
|
||||
REDIS_SENTINEL_MASTER: "mymasterset"
|
||||
REDIS_DATABASE: "0"
|
||||
REDIS_SSL: "no"
|
||||
extra_hosts:
|
||||
- "www.example.com:1.0.0.2"
|
||||
networks:
|
||||
bw-services:
|
||||
ipv4_address: 1.0.0.3
|
||||
|
||||
networks:
|
||||
bw-services:
|
||||
external: true
|
||||
135
tests/core/redis-sentinel/docker-compose.yml
Normal file
135
tests/core/redis-sentinel/docker-compose.yml
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
version: "3.5"
|
||||
|
||||
services:
|
||||
bw:
|
||||
image: bunkerity/bunkerweb:1.5.4
|
||||
pull_policy: never
|
||||
depends_on:
|
||||
- bw-redis
|
||||
labels:
|
||||
- "bunkerweb.INSTANCE=yes"
|
||||
volumes:
|
||||
- ./index.html:/var/www/html/index.html
|
||||
environment:
|
||||
API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24 1.0.0.3"
|
||||
HTTP_PORT: "80"
|
||||
USE_BUNKERNET: "no"
|
||||
SEND_ANONYMOUS_REPORT: "no"
|
||||
BLACKLIST_IP_URLS: ""
|
||||
LOG_LEVEL: "info"
|
||||
SESSIONS_NAME: "test"
|
||||
USE_REVERSE_SCAN: "no"
|
||||
USE_ANTIBOT: "no"
|
||||
USE_GREYLIST: "yes"
|
||||
GREYLIST_IP: "0.0.0.0/0"
|
||||
WHITELIST_COUNTRY: "AU"
|
||||
|
||||
# ? REDIS settings
|
||||
USE_REDIS: "yes"
|
||||
REDIS_SENTINEL_HOSTS: "bw-sentinel-1 bw-sentinel-2 bw-sentinel-3"
|
||||
REDIS_SENTINEL_MASTER: "mymasterset"
|
||||
REDIS_DATABASE: "0"
|
||||
REDIS_SSL: "no"
|
||||
CUSTOM_CONF_SERVER_HTTP_ready: |
|
||||
location /ready {
|
||||
default_type 'text/plain';
|
||||
rewrite_by_lua_block {
|
||||
ngx.print('ready')
|
||||
ngx.flush(true)
|
||||
ngx.exit(ngx.HTTP_OK)
|
||||
}
|
||||
}
|
||||
networks:
|
||||
bw-universe:
|
||||
bw-services:
|
||||
ipv4_address: 1.0.0.2
|
||||
|
||||
bw-scheduler:
|
||||
image: bunkerity/bunkerweb-scheduler:1.5.4
|
||||
pull_policy: never
|
||||
depends_on:
|
||||
- bw
|
||||
- bw-docker
|
||||
environment:
|
||||
DOCKER_HOST: "tcp://bw-docker:2375"
|
||||
LOG_LEVEL: "info"
|
||||
networks:
|
||||
- bw-universe
|
||||
- bw-docker
|
||||
|
||||
bw-docker:
|
||||
image: tecnativa/docker-socket-proxy:nightly
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
CONTAINERS: "1"
|
||||
networks:
|
||||
- bw-docker
|
||||
|
||||
bw-redis-master:
|
||||
image: 'bitnami/redis:latest'
|
||||
environment:
|
||||
- REDIS_REPLICATION_MODE=master
|
||||
- ALLOW_EMPTY_PASSWORD=yes
|
||||
networks:
|
||||
- bw-services
|
||||
|
||||
bw-redis-slave:
|
||||
image: 'bitnami/redis:latest'
|
||||
environment:
|
||||
- REDIS_REPLICATION_MODE=slave
|
||||
- REDIS_MASTER_HOST=bw-redis-master
|
||||
- ALLOW_EMPTY_PASSWORD=yes
|
||||
depends_on:
|
||||
- bw-redis-master
|
||||
networks:
|
||||
- bw-services
|
||||
|
||||
bw-sentinel-1:
|
||||
image: 'bitnami/redis-sentinel:latest'
|
||||
environment:
|
||||
- REDIS_MASTER_HOST=bw-redis-master
|
||||
- REDIS_MASTER_SET=mymasterset
|
||||
depends_on:
|
||||
- bw-redis-master
|
||||
- bw-redis-slave
|
||||
networks:
|
||||
- bw-services
|
||||
|
||||
bw-sentinel-2:
|
||||
image: 'bitnami/redis-sentinel:latest'
|
||||
environment:
|
||||
- REDIS_MASTER_HOST=bw-redis-master
|
||||
- REDIS_MASTER_SET=mymasterset
|
||||
depends_on:
|
||||
- bw-redis-master
|
||||
- bw-redis-slave
|
||||
networks:
|
||||
- bw-services
|
||||
|
||||
bw-sentinel-3:
|
||||
image: 'bitnami/redis-sentinel:latest'
|
||||
environment:
|
||||
- REDIS_MASTER_HOST=bw-redis-master
|
||||
- REDIS_MASTER_SET=mymasterset
|
||||
depends_on:
|
||||
- bw-redis-master
|
||||
- bw-redis-slave
|
||||
networks:
|
||||
- bw-services
|
||||
|
||||
networks:
|
||||
bw-universe:
|
||||
name: bw-universe
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 10.20.30.0/24
|
||||
bw-services:
|
||||
name: bw-services
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 1.0.0.0/24
|
||||
bw-docker:
|
||||
name: bw-docker
|
||||
31
tests/core/redis-sentinel/entrypoint.sh
Normal file
31
tests/core/redis-sentinel/entrypoint.sh
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
command="redis-server"
|
||||
|
||||
if [ "$REDIS_SSL" = "yes" ]; then
|
||||
mkdir /tls
|
||||
|
||||
openssl genrsa -out /tls/ca.key 4096
|
||||
openssl req \
|
||||
-x509 -new -nodes -sha256 \
|
||||
-key /tls/ca.key \
|
||||
-days 365 \
|
||||
-subj /CN=bw-redis/ \
|
||||
-out /tls/ca.crt
|
||||
|
||||
openssl req \
|
||||
-x509 -nodes -newkey rsa:4096 \
|
||||
-keyout /tls/redis.key \
|
||||
-out /tls/redis.pem \
|
||||
-days 365 \
|
||||
-subj /CN=bw-redis/
|
||||
|
||||
chmod -R 640 /tls
|
||||
|
||||
command+=" --tls-port ${REDIS_PORT:-6379} --port 0 --tls-cert-file /tls/redis.pem --tls-key-file /tls/redis.key --tls-ca-cert-file /tls/ca.crt --tls-auth-clients no"
|
||||
else
|
||||
command+=" --port ${REDIS_PORT:-6379}"
|
||||
fi
|
||||
|
||||
$command
|
||||
0
tests/core/redis-sentinel/index.html
Normal file
0
tests/core/redis-sentinel/index.html
Normal file
397
tests/core/redis-sentinel/main.py
Normal file
397
tests/core/redis-sentinel/main.py
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
from fastapi import FastAPI
|
||||
from multiprocessing import Process
|
||||
from os import getenv
|
||||
from redis import Redis
|
||||
from requests import get
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from time import sleep
|
||||
from traceback import format_exc
|
||||
from contextlib import suppress
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
from uvicorn import run
|
||||
|
||||
fastapi_proc = None
|
||||
|
||||
ip_to_check = "1.0.0.3" if getenv("TEST_TYPE", "docker") == "docker" else "127.0.0.1"
|
||||
|
||||
try:
|
||||
ready = False
|
||||
retries = 0
|
||||
while not ready:
|
||||
with suppress(RequestException):
|
||||
resp = get("http://www.example.com/ready", headers={"Host": "www.example.com"})
|
||||
status_code = resp.status_code
|
||||
text = resp.text
|
||||
|
||||
if status_code >= 500:
|
||||
print("❌ An error occurred with the server, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
ready = status_code < 400 or status_code == 403 and text == "ready"
|
||||
|
||||
if retries > 10:
|
||||
print("❌ The service took too long to be ready, exiting ...", flush=True)
|
||||
exit(1)
|
||||
elif not ready:
|
||||
retries += 1
|
||||
print("⚠️ Waiting for the service to be ready, retrying in 5s ...", flush=True)
|
||||
sleep(5)
|
||||
|
||||
redis_host = getenv("REDIS_HOST", "127.0.0.1")
|
||||
|
||||
if not redis_host:
|
||||
print("❌ Redis host is not set, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
redis_port = getenv("REDIS_PORT", "6379")
|
||||
|
||||
if not redis_port.isdigit():
|
||||
print("❌ Redis port doesn't seem to be a number, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
redis_port = int(redis_port)
|
||||
|
||||
redis_db = getenv("REDIS_DATABASE", "0")
|
||||
|
||||
if not redis_db.isdigit():
|
||||
print("❌ Redis database doesn't seem to be a number, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
redis_db = int(redis_db)
|
||||
|
||||
redis_ssl = getenv("REDIS_SSL", "no") == "yes"
|
||||
|
||||
print(
|
||||
f"ℹ️ Trying to connect to Redis with the following parameters:\nhost: {redis_host}\nport: {redis_port}\ndb: {redis_db}\nssl: {redis_ssl}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
redis_client = Redis(
|
||||
host=redis_host,
|
||||
port=redis_port,
|
||||
db=redis_db,
|
||||
ssl=redis_ssl,
|
||||
socket_timeout=1,
|
||||
ssl_cert_reqs=None,
|
||||
)
|
||||
|
||||
if not redis_client.ping():
|
||||
print("❌ Redis is not reachable, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
use_reverse_scan = getenv("USE_REVERSE_SCAN", "no") == "yes"
|
||||
|
||||
if use_reverse_scan:
|
||||
if ip_to_check == "1.0.0.3":
|
||||
print("ℹ️ Testing Reverse Scan, starting FastAPI ...", flush=True)
|
||||
app = FastAPI()
|
||||
fastapi_proc = Process(target=run, args=(app,), kwargs=dict(host="0.0.0.0", port=8080))
|
||||
fastapi_proc.start()
|
||||
|
||||
sleep(2)
|
||||
|
||||
print(
|
||||
"ℹ️ FastAPI started, sending a request to http://www.example.com ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
response = get(
|
||||
"http://www.example.com",
|
||||
headers={"Host": "www.example.com"},
|
||||
)
|
||||
|
||||
if response.status_code != 403:
|
||||
response.raise_for_status()
|
||||
|
||||
print("❌ The request was not blocked, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
sleep(0.5)
|
||||
|
||||
print("ℹ️ The request was blocked, checking Redis ...", flush=True)
|
||||
|
||||
port_to_check = "8080" if ip_to_check == "1.0.0.3" else "80"
|
||||
|
||||
key_value = redis_client.get(f"plugin_reverse_scan_{ip_to_check}:{port_to_check}")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The Reverse Scan key ("plugin_reverse_scan_{ip_to_check}:{port_to_check}") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
elif key_value != b"open":
|
||||
print(
|
||||
f'❌ The Reverse Scan key ("plugin_reverse_scan_{ip_to_check}:{port_to_check}") was found, but the value is not "open" ({key_value.decode()}), exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(
|
||||
f"✅ The Reverse Scan key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
exit(0)
|
||||
|
||||
use_antibot = getenv("USE_ANTIBOT", "no") != "no"
|
||||
|
||||
if use_antibot:
|
||||
print("ℹ️ Testing Antibot ...", flush=True)
|
||||
|
||||
firefox_options = Options()
|
||||
firefox_options.add_argument("--headless")
|
||||
|
||||
print("ℹ️ Starting Firefox ...", flush=True)
|
||||
with webdriver.Firefox(options=firefox_options) as driver:
|
||||
driver.delete_all_cookies()
|
||||
driver.maximize_window()
|
||||
|
||||
print("ℹ️ Navigating to http://www.example.com ...", flush=True)
|
||||
driver.get("http://www.example.com")
|
||||
|
||||
sleep(0.5)
|
||||
|
||||
print("ℹ️ Checking Redis ...", flush=True)
|
||||
|
||||
keys = redis_client.keys("sessions_:test:*")
|
||||
|
||||
if not keys:
|
||||
print(
|
||||
f"❌ No Antibot keys were found, exiting ...\nkeys: {redis_client.keys()}",
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
key_value = redis_client.get(keys[0])
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f"❌ The Antibot key ({keys[0].decode()}) was not found, exiting ...\nkeys: {redis_client.keys()}",
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(
|
||||
f"✅ The Antibot key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
exit(0)
|
||||
|
||||
print(
|
||||
"ℹ️ Sending a request to http://www.example.com/?id=/etc/passwd ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
response = get(
|
||||
"http://www.example.com/?id=/etc/passwd",
|
||||
headers={"Host": "www.example.com"},
|
||||
)
|
||||
|
||||
if response.status_code != 403:
|
||||
response.raise_for_status()
|
||||
|
||||
print("❌ The request was not blocked, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
sleep(0.5)
|
||||
|
||||
print("ℹ️ The request was blocked, checking Redis ...", flush=True)
|
||||
|
||||
key_value = redis_client.get(f"plugin_bad_behavior_{ip_to_check}")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The Bad Behavior key ("plugin_bad_behavior_{ip_to_check}") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(
|
||||
f"✅ The Bad Behavior key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print(
|
||||
"ℹ️ Sending another request to http://www.example.com/?id=/etc/passwd ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
response = get(
|
||||
"http://www.example.com/?id=/etc/passwd",
|
||||
headers={"Host": "www.example.com"},
|
||||
)
|
||||
|
||||
if response.status_code != 403:
|
||||
response.raise_for_status()
|
||||
|
||||
print("❌ The request was not blocked, exiting ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
sleep(0.5)
|
||||
|
||||
second_key_value = redis_client.get(f"plugin_bad_behavior_{ip_to_check}")
|
||||
|
||||
if second_key_value <= key_value:
|
||||
print(
|
||||
f'❌ The Bad Behavior key ("plugin_bad_behavior_{ip_to_check}") was not incremented, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(
|
||||
f"✅ The Bad Behavior key was incremented, the value is {second_key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print(
|
||||
"ℹ️ Sending requests to http://www.example.com until we reach the limit ...",
|
||||
flush=True,
|
||||
)
|
||||
status_code = 0
|
||||
|
||||
while status_code != 429:
|
||||
response = get(
|
||||
"http://www.example.com",
|
||||
headers={"Host": "www.example.com"},
|
||||
)
|
||||
|
||||
if response.status_code not in (200, 429):
|
||||
response.raise_for_status()
|
||||
|
||||
status_code = response.status_code
|
||||
|
||||
sleep(0.5)
|
||||
|
||||
key_value = redis_client.get(f"plugin_limit_www.example.com{ip_to_check}/")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The limit key ("plugin_limit_www.example.com{ip_to_check}/") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(
|
||||
f"✅ The limit key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print(
|
||||
"ℹ️ Checking if the country key was created and has the correct value ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
key_value = redis_client.get(f"plugin_country_www.example.com{ip_to_check}")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The country key ("plugin_country_www.example.com{ip_to_check}") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(
|
||||
f"✅ The country key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print(
|
||||
"ℹ️ Checking if the whitelist key was created and has the correct value ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
key_value = redis_client.get(f"plugin_whitelist_www.example.comip{ip_to_check}")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The whitelist key ("plugin_whitelist_www.example.comip{ip_to_check}") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
if key_value != b"ok":
|
||||
print(
|
||||
f'❌ The whitelist key ("plugin_whitelist_www.example.comip{ip_to_check}") was found, but the value is not "ok" ({key_value.decode()}), exiting ...\nkeys: {redis_client.keys()}',
|
||||
)
|
||||
|
||||
print(
|
||||
f"✅ The whitelist key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print(
|
||||
"ℹ️ Checking if the blacklist key was created and has the correct value ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
key_value = redis_client.get(f"plugin_blacklist_www.example.comip{ip_to_check}")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The blacklist key ("plugin_blacklist_www.example.comip{ip_to_check}") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
if key_value != b"ok":
|
||||
print(
|
||||
f'❌ The blacklist key ("plugin_blacklist_www.example.comip{ip_to_check}") was found, but the value is not "ok" ({key_value.decode()}), exiting ...\nkeys: {redis_client.keys()}',
|
||||
)
|
||||
|
||||
print(
|
||||
f"✅ The blacklist key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print(
|
||||
"ℹ️ Checking if the greylist key was created and has the correct value ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
key_value = redis_client.get(f"plugin_greylist_www.example.comip{ip_to_check}")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The greylist key ("plugin_greylist_www.example.comip{ip_to_check}") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
if key_value != b"ip":
|
||||
print(
|
||||
f'❌ The greylist key ("plugin_greylist_www.example.comip{ip_to_check}") was found, but the value is not "ip" ({key_value.decode()}), exiting ...\nkeys: {redis_client.keys()}',
|
||||
)
|
||||
|
||||
print(
|
||||
f"✅ The greylist key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
if ip_to_check == "1.0.0.3":
|
||||
print(
|
||||
"ℹ️ Checking if the dnsbl keys were created ...",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
key_value = redis_client.get(f"plugin_dnsbl_www.example.com{ip_to_check}")
|
||||
|
||||
if key_value is None:
|
||||
print(
|
||||
f'❌ The dnsbl key ("plugin_dnsbl_www.example.com{ip_to_check}") was not found, exiting ...\nkeys: {redis_client.keys()}',
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(
|
||||
f"✅ The dnsbl key was found, the value is {key_value.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
except SystemExit as e:
|
||||
exit(e.code)
|
||||
except:
|
||||
print(f"❌ Something went wrong, exiting ...\n{format_exc()}", flush=True)
|
||||
exit(1)
|
||||
finally:
|
||||
if fastapi_proc:
|
||||
fastapi_proc.terminate()
|
||||
8
tests/core/redis-sentinel/ready.conf
Normal file
8
tests/core/redis-sentinel/ready.conf
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
location /ready {
|
||||
default_type 'text/plain';
|
||||
rewrite_by_lua_block {
|
||||
ngx.print('ready')
|
||||
ngx.flush(true)
|
||||
ngx.exit(ngx.HTTP_OK)
|
||||
}
|
||||
}
|
||||
5
tests/core/redis-sentinel/requirements.in
Normal file
5
tests/core/redis-sentinel/requirements.in
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
fastapi==0.108.0
|
||||
redis==5.0.1
|
||||
requests==2.31.0
|
||||
selenium==4.16.0
|
||||
uvicorn[standard]==0.25.0
|
||||
612
tests/core/redis-sentinel/requirements.txt
Normal file
612
tests/core/redis-sentinel/requirements.txt
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.9
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
annotated-types==0.6.0 \
|
||||
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||
# via pydantic
|
||||
anyio==4.2.0 \
|
||||
--hash=sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee \
|
||||
--hash=sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f
|
||||
# via
|
||||
# starlette
|
||||
# watchfiles
|
||||
async-timeout==4.0.3 \
|
||||
--hash=sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f \
|
||||
--hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028
|
||||
# via redis
|
||||
attrs==23.1.0 \
|
||||
--hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
|
||||
--hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
|
||||
# via
|
||||
# outcome
|
||||
# trio
|
||||
certifi==2023.11.17 \
|
||||
--hash=sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1 \
|
||||
--hash=sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474
|
||||
# via
|
||||
# requests
|
||||
# selenium
|
||||
charset-normalizer==3.3.2 \
|
||||
--hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
|
||||
--hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \
|
||||
--hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \
|
||||
--hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \
|
||||
--hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \
|
||||
--hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \
|
||||
--hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \
|
||||
--hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \
|
||||
--hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \
|
||||
--hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \
|
||||
--hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \
|
||||
--hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \
|
||||
--hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \
|
||||
--hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \
|
||||
--hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \
|
||||
--hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \
|
||||
--hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \
|
||||
--hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \
|
||||
--hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \
|
||||
--hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \
|
||||
--hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \
|
||||
--hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \
|
||||
--hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \
|
||||
--hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \
|
||||
--hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \
|
||||
--hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \
|
||||
--hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \
|
||||
--hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \
|
||||
--hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \
|
||||
--hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \
|
||||
--hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \
|
||||
--hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \
|
||||
--hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \
|
||||
--hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \
|
||||
--hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \
|
||||
--hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \
|
||||
--hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \
|
||||
--hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \
|
||||
--hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \
|
||||
--hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \
|
||||
--hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \
|
||||
--hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \
|
||||
--hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \
|
||||
--hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \
|
||||
--hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \
|
||||
--hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \
|
||||
--hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \
|
||||
--hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \
|
||||
--hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \
|
||||
--hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \
|
||||
--hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \
|
||||
--hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \
|
||||
--hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \
|
||||
--hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \
|
||||
--hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \
|
||||
--hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \
|
||||
--hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \
|
||||
--hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \
|
||||
--hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \
|
||||
--hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \
|
||||
--hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \
|
||||
--hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \
|
||||
--hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \
|
||||
--hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \
|
||||
--hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \
|
||||
--hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \
|
||||
--hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \
|
||||
--hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \
|
||||
--hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \
|
||||
--hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \
|
||||
--hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \
|
||||
--hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \
|
||||
--hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \
|
||||
--hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \
|
||||
--hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \
|
||||
--hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \
|
||||
--hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \
|
||||
--hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \
|
||||
--hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \
|
||||
--hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \
|
||||
--hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \
|
||||
--hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \
|
||||
--hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \
|
||||
--hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \
|
||||
--hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \
|
||||
--hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \
|
||||
--hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \
|
||||
--hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \
|
||||
--hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \
|
||||
--hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561
|
||||
# via requests
|
||||
click==8.1.7 \
|
||||
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
|
||||
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
|
||||
# via uvicorn
|
||||
exceptiongroup==1.2.0 \
|
||||
--hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \
|
||||
--hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68
|
||||
# via
|
||||
# anyio
|
||||
# trio
|
||||
# trio-websocket
|
||||
fastapi==0.108.0 \
|
||||
--hash=sha256:5056e504ac6395bf68493d71fcfc5352fdbd5fda6f88c21f6420d80d81163296 \
|
||||
--hash=sha256:8c7bc6d315da963ee4cdb605557827071a9a7f95aeb8fcdd3bde48cdc8764dd7
|
||||
# via -r requirements.in
|
||||
h11==0.14.0 \
|
||||
--hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
|
||||
--hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
|
||||
# via
|
||||
# uvicorn
|
||||
# wsproto
|
||||
httptools==0.6.1 \
|
||||
--hash=sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563 \
|
||||
--hash=sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142 \
|
||||
--hash=sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d \
|
||||
--hash=sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b \
|
||||
--hash=sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4 \
|
||||
--hash=sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb \
|
||||
--hash=sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658 \
|
||||
--hash=sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084 \
|
||||
--hash=sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2 \
|
||||
--hash=sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97 \
|
||||
--hash=sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837 \
|
||||
--hash=sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3 \
|
||||
--hash=sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58 \
|
||||
--hash=sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da \
|
||||
--hash=sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d \
|
||||
--hash=sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90 \
|
||||
--hash=sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0 \
|
||||
--hash=sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1 \
|
||||
--hash=sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2 \
|
||||
--hash=sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e \
|
||||
--hash=sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0 \
|
||||
--hash=sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf \
|
||||
--hash=sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc \
|
||||
--hash=sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3 \
|
||||
--hash=sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503 \
|
||||
--hash=sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a \
|
||||
--hash=sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3 \
|
||||
--hash=sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949 \
|
||||
--hash=sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84 \
|
||||
--hash=sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb \
|
||||
--hash=sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a \
|
||||
--hash=sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f \
|
||||
--hash=sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e \
|
||||
--hash=sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81 \
|
||||
--hash=sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185 \
|
||||
--hash=sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3
|
||||
# via uvicorn
|
||||
idna==3.6 \
|
||||
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
# trio
|
||||
outcome==1.3.0.post0 \
|
||||
--hash=sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8 \
|
||||
--hash=sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b
|
||||
# via trio
|
||||
pydantic==2.5.3 \
|
||||
--hash=sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a \
|
||||
--hash=sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4
|
||||
# via fastapi
|
||||
pydantic-core==2.14.6 \
|
||||
--hash=sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556 \
|
||||
--hash=sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e \
|
||||
--hash=sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411 \
|
||||
--hash=sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245 \
|
||||
--hash=sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c \
|
||||
--hash=sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66 \
|
||||
--hash=sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd \
|
||||
--hash=sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d \
|
||||
--hash=sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b \
|
||||
--hash=sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06 \
|
||||
--hash=sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948 \
|
||||
--hash=sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341 \
|
||||
--hash=sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0 \
|
||||
--hash=sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f \
|
||||
--hash=sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a \
|
||||
--hash=sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2 \
|
||||
--hash=sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51 \
|
||||
--hash=sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80 \
|
||||
--hash=sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8 \
|
||||
--hash=sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d \
|
||||
--hash=sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8 \
|
||||
--hash=sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb \
|
||||
--hash=sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590 \
|
||||
--hash=sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87 \
|
||||
--hash=sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534 \
|
||||
--hash=sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b \
|
||||
--hash=sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145 \
|
||||
--hash=sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba \
|
||||
--hash=sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b \
|
||||
--hash=sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2 \
|
||||
--hash=sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e \
|
||||
--hash=sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052 \
|
||||
--hash=sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622 \
|
||||
--hash=sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab \
|
||||
--hash=sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b \
|
||||
--hash=sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66 \
|
||||
--hash=sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e \
|
||||
--hash=sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4 \
|
||||
--hash=sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e \
|
||||
--hash=sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec \
|
||||
--hash=sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c \
|
||||
--hash=sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed \
|
||||
--hash=sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937 \
|
||||
--hash=sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f \
|
||||
--hash=sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9 \
|
||||
--hash=sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4 \
|
||||
--hash=sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96 \
|
||||
--hash=sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277 \
|
||||
--hash=sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23 \
|
||||
--hash=sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7 \
|
||||
--hash=sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b \
|
||||
--hash=sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91 \
|
||||
--hash=sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d \
|
||||
--hash=sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e \
|
||||
--hash=sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1 \
|
||||
--hash=sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2 \
|
||||
--hash=sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160 \
|
||||
--hash=sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9 \
|
||||
--hash=sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670 \
|
||||
--hash=sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7 \
|
||||
--hash=sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c \
|
||||
--hash=sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb \
|
||||
--hash=sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42 \
|
||||
--hash=sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d \
|
||||
--hash=sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8 \
|
||||
--hash=sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1 \
|
||||
--hash=sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6 \
|
||||
--hash=sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8 \
|
||||
--hash=sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf \
|
||||
--hash=sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e \
|
||||
--hash=sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a \
|
||||
--hash=sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9 \
|
||||
--hash=sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1 \
|
||||
--hash=sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40 \
|
||||
--hash=sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2 \
|
||||
--hash=sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d \
|
||||
--hash=sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f \
|
||||
--hash=sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f \
|
||||
--hash=sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af \
|
||||
--hash=sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7 \
|
||||
--hash=sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda \
|
||||
--hash=sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a \
|
||||
--hash=sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95 \
|
||||
--hash=sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0 \
|
||||
--hash=sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60 \
|
||||
--hash=sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149 \
|
||||
--hash=sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975 \
|
||||
--hash=sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4 \
|
||||
--hash=sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe \
|
||||
--hash=sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94 \
|
||||
--hash=sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03 \
|
||||
--hash=sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c \
|
||||
--hash=sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b \
|
||||
--hash=sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a \
|
||||
--hash=sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24 \
|
||||
--hash=sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391 \
|
||||
--hash=sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c \
|
||||
--hash=sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab \
|
||||
--hash=sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd \
|
||||
--hash=sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786 \
|
||||
--hash=sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08 \
|
||||
--hash=sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8 \
|
||||
--hash=sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6 \
|
||||
--hash=sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0 \
|
||||
--hash=sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421
|
||||
# via pydantic
|
||||
pysocks==1.7.1 \
|
||||
--hash=sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299 \
|
||||
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
||||
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||
# via urllib3
|
||||
python-dotenv==1.0.0 \
|
||||
--hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \
|
||||
--hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a
|
||||
# via uvicorn
|
||||
pyyaml==6.0.1 \
|
||||
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
|
||||
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
|
||||
--hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
|
||||
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
|
||||
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
|
||||
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
|
||||
--hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
|
||||
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
|
||||
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
|
||||
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
|
||||
--hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
|
||||
--hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
|
||||
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
|
||||
--hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
|
||||
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
|
||||
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
|
||||
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
|
||||
--hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
|
||||
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
|
||||
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
|
||||
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
|
||||
--hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
|
||||
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
|
||||
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
|
||||
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
|
||||
--hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
|
||||
--hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
|
||||
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
|
||||
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
|
||||
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
|
||||
--hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
|
||||
--hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
|
||||
--hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
|
||||
--hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
|
||||
--hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
|
||||
--hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
|
||||
--hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
|
||||
--hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
|
||||
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
|
||||
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
|
||||
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
|
||||
--hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
|
||||
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
|
||||
--hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
|
||||
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
|
||||
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
|
||||
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
|
||||
--hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
|
||||
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
|
||||
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
|
||||
# via uvicorn
|
||||
redis==5.0.1 \
|
||||
--hash=sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f \
|
||||
--hash=sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f
|
||||
# via -r requirements.in
|
||||
requests==2.31.0 \
|
||||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.16.0 \
|
||||
--hash=sha256:aec71f4e6ed6cb3ec25c9c1b5ed56ae31b6da0a7f17474c7566d303f84e6219f \
|
||||
--hash=sha256:b2e987a445306151f7be0e6dfe2aa72a479c2ac6a91b9d5ef2d6dd4e49ad0435
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
--hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384
|
||||
# via
|
||||
# anyio
|
||||
# trio
|
||||
sortedcontainers==2.4.0 \
|
||||
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
|
||||
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
|
||||
# via trio
|
||||
starlette==0.32.0.post1 \
|
||||
--hash=sha256:cd0cb10ddb49313f609cedfac62c8c12e56c7314b66d89bb077ba228bada1b09 \
|
||||
--hash=sha256:e54e2b7e2fb06dff9eac40133583f10dfa05913f5a85bf26f427c7a40a9a3d02
|
||||
# via fastapi
|
||||
trio==0.23.2 \
|
||||
--hash=sha256:5a0b566fa5d50cf231cfd6b08f3b03aa4179ff004b8f3144059587039e2b26d3 \
|
||||
--hash=sha256:da1d35b9a2b17eb32cae2e763b16551f9aa6703634735024e32f325c9285069e
|
||||
# via
|
||||
# selenium
|
||||
# trio-websocket
|
||||
trio-websocket==0.11.1 \
|
||||
--hash=sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f \
|
||||
--hash=sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638
|
||||
# via selenium
|
||||
typing-extensions==4.9.0 \
|
||||
--hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \
|
||||
--hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd
|
||||
# via
|
||||
# anyio
|
||||
# fastapi
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# starlette
|
||||
# uvicorn
|
||||
urllib3==2.1.0 \
|
||||
--hash=sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3 \
|
||||
--hash=sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54
|
||||
# via
|
||||
# requests
|
||||
# selenium
|
||||
uvicorn==0.25.0 \
|
||||
--hash=sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2 \
|
||||
--hash=sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c
|
||||
# via
|
||||
# -r requirements.in
|
||||
# uvicorn
|
||||
uvloop==0.19.0 \
|
||||
--hash=sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd \
|
||||
--hash=sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec \
|
||||
--hash=sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b \
|
||||
--hash=sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc \
|
||||
--hash=sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797 \
|
||||
--hash=sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5 \
|
||||
--hash=sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2 \
|
||||
--hash=sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d \
|
||||
--hash=sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be \
|
||||
--hash=sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd \
|
||||
--hash=sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12 \
|
||||
--hash=sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17 \
|
||||
--hash=sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef \
|
||||
--hash=sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24 \
|
||||
--hash=sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428 \
|
||||
--hash=sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1 \
|
||||
--hash=sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849 \
|
||||
--hash=sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593 \
|
||||
--hash=sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd \
|
||||
--hash=sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67 \
|
||||
--hash=sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6 \
|
||||
--hash=sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3 \
|
||||
--hash=sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd \
|
||||
--hash=sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8 \
|
||||
--hash=sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7 \
|
||||
--hash=sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533 \
|
||||
--hash=sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957 \
|
||||
--hash=sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650 \
|
||||
--hash=sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e \
|
||||
--hash=sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7 \
|
||||
--hash=sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256
|
||||
# via uvicorn
|
||||
watchfiles==0.21.0 \
|
||||
--hash=sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc \
|
||||
--hash=sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365 \
|
||||
--hash=sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0 \
|
||||
--hash=sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e \
|
||||
--hash=sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124 \
|
||||
--hash=sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c \
|
||||
--hash=sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317 \
|
||||
--hash=sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094 \
|
||||
--hash=sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7 \
|
||||
--hash=sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235 \
|
||||
--hash=sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c \
|
||||
--hash=sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c \
|
||||
--hash=sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c \
|
||||
--hash=sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235 \
|
||||
--hash=sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293 \
|
||||
--hash=sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa \
|
||||
--hash=sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef \
|
||||
--hash=sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19 \
|
||||
--hash=sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8 \
|
||||
--hash=sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d \
|
||||
--hash=sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915 \
|
||||
--hash=sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429 \
|
||||
--hash=sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097 \
|
||||
--hash=sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe \
|
||||
--hash=sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0 \
|
||||
--hash=sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d \
|
||||
--hash=sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99 \
|
||||
--hash=sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1 \
|
||||
--hash=sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a \
|
||||
--hash=sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895 \
|
||||
--hash=sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94 \
|
||||
--hash=sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562 \
|
||||
--hash=sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab \
|
||||
--hash=sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360 \
|
||||
--hash=sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1 \
|
||||
--hash=sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7 \
|
||||
--hash=sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f \
|
||||
--hash=sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03 \
|
||||
--hash=sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01 \
|
||||
--hash=sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58 \
|
||||
--hash=sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052 \
|
||||
--hash=sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e \
|
||||
--hash=sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765 \
|
||||
--hash=sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6 \
|
||||
--hash=sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137 \
|
||||
--hash=sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85 \
|
||||
--hash=sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca \
|
||||
--hash=sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f \
|
||||
--hash=sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214 \
|
||||
--hash=sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7 \
|
||||
--hash=sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7 \
|
||||
--hash=sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3 \
|
||||
--hash=sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b \
|
||||
--hash=sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7 \
|
||||
--hash=sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6 \
|
||||
--hash=sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994 \
|
||||
--hash=sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9 \
|
||||
--hash=sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec \
|
||||
--hash=sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128 \
|
||||
--hash=sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c \
|
||||
--hash=sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2 \
|
||||
--hash=sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078 \
|
||||
--hash=sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3 \
|
||||
--hash=sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e \
|
||||
--hash=sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a \
|
||||
--hash=sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6 \
|
||||
--hash=sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49 \
|
||||
--hash=sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b \
|
||||
--hash=sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28 \
|
||||
--hash=sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9 \
|
||||
--hash=sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586 \
|
||||
--hash=sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400 \
|
||||
--hash=sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165 \
|
||||
--hash=sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303 \
|
||||
--hash=sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d
|
||||
# via uvicorn
|
||||
websockets==12.0 \
|
||||
--hash=sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b \
|
||||
--hash=sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6 \
|
||||
--hash=sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df \
|
||||
--hash=sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b \
|
||||
--hash=sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205 \
|
||||
--hash=sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892 \
|
||||
--hash=sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53 \
|
||||
--hash=sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2 \
|
||||
--hash=sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed \
|
||||
--hash=sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c \
|
||||
--hash=sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd \
|
||||
--hash=sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b \
|
||||
--hash=sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931 \
|
||||
--hash=sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30 \
|
||||
--hash=sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370 \
|
||||
--hash=sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be \
|
||||
--hash=sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec \
|
||||
--hash=sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf \
|
||||
--hash=sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62 \
|
||||
--hash=sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b \
|
||||
--hash=sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402 \
|
||||
--hash=sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f \
|
||||
--hash=sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123 \
|
||||
--hash=sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9 \
|
||||
--hash=sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603 \
|
||||
--hash=sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45 \
|
||||
--hash=sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558 \
|
||||
--hash=sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4 \
|
||||
--hash=sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438 \
|
||||
--hash=sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137 \
|
||||
--hash=sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480 \
|
||||
--hash=sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447 \
|
||||
--hash=sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8 \
|
||||
--hash=sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04 \
|
||||
--hash=sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c \
|
||||
--hash=sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb \
|
||||
--hash=sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967 \
|
||||
--hash=sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b \
|
||||
--hash=sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d \
|
||||
--hash=sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def \
|
||||
--hash=sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c \
|
||||
--hash=sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92 \
|
||||
--hash=sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2 \
|
||||
--hash=sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113 \
|
||||
--hash=sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b \
|
||||
--hash=sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28 \
|
||||
--hash=sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7 \
|
||||
--hash=sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d \
|
||||
--hash=sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f \
|
||||
--hash=sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468 \
|
||||
--hash=sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8 \
|
||||
--hash=sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae \
|
||||
--hash=sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611 \
|
||||
--hash=sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d \
|
||||
--hash=sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9 \
|
||||
--hash=sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca \
|
||||
--hash=sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f \
|
||||
--hash=sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2 \
|
||||
--hash=sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077 \
|
||||
--hash=sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2 \
|
||||
--hash=sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6 \
|
||||
--hash=sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374 \
|
||||
--hash=sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc \
|
||||
--hash=sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e \
|
||||
--hash=sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53 \
|
||||
--hash=sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399 \
|
||||
--hash=sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547 \
|
||||
--hash=sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3 \
|
||||
--hash=sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870 \
|
||||
--hash=sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5 \
|
||||
--hash=sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8 \
|
||||
--hash=sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7
|
||||
# via uvicorn
|
||||
wsproto==1.2.0 \
|
||||
--hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \
|
||||
--hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736
|
||||
# via trio-websocket
|
||||
327
tests/core/redis-sentinel/test-todo.sh
Executable file
327
tests/core/redis-sentinel/test-todo.sh
Executable file
|
|
@ -0,0 +1,327 @@
|
|||
#!/bin/bash
|
||||
|
||||
integration=$1
|
||||
|
||||
if [ -z "$integration" ] ; then
|
||||
echo "🧰 Please provide an integration name as argument ❌"
|
||||
exit 1
|
||||
elif [ "$integration" != "docker" ] && [ "$integration" != "linux" ] ; then
|
||||
echo "🧰 Integration \"$integration\" is not supported ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🧰 Building redis stack for integration \"$integration\" ..."
|
||||
|
||||
# Starting stack
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
docker compose pull bw-docker
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Pull failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🧰 Building custom redis image ..."
|
||||
docker compose build bw-redis
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Build failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🧰 Building tests images ..."
|
||||
docker compose -f docker-compose.test.yml build
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Build failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
sudo systemctl stop bunkerweb
|
||||
sudo sed -i "/^USE_BLACKLIST=/d" /etc/bunkerweb/variables.env
|
||||
echo "BLACKLIST_IP_URLS=" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "SESSIONS_NAME=test" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "USE_REVERSE_SCAN=no" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "REVERSE_SCAN_PORTS=80" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "USE_ANTIBOT=no" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "USE_GREYLIST=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "GREYLIST_IP=0.0.0.0/0" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "WHITELIST_COUNTRY=AU" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
|
||||
echo "🧰 Installing Redis ..."
|
||||
sudo apt install --no-install-recommends -y redis
|
||||
redis-server --daemonize yes
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Redis start failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
echo "🧰 Redis installed ✅"
|
||||
|
||||
echo "🧰 Generating redis certs ..."
|
||||
mkdir tls
|
||||
openssl genrsa -out tls/ca.key 4096
|
||||
openssl req \
|
||||
-x509 -new -nodes -sha256 \
|
||||
-key tls/ca.key \
|
||||
-days 365 \
|
||||
-subj /CN=bw-redis/ \
|
||||
-out tls/ca.crt
|
||||
openssl req \
|
||||
-x509 -nodes -newkey rsa:4096 \
|
||||
-keyout tls/redis.key \
|
||||
-out tls/redis.pem \
|
||||
-days 365 \
|
||||
-subj /CN=bw-redis/
|
||||
sudo chmod -R 777 tls
|
||||
echo "🧰 Certs generated ✅"
|
||||
|
||||
echo "USE_REDIS=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "REDIS_HOST=127.0.0.1" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "REDIS_PORT=6379" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "REDIS_DATABASE=0" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "REDIS_SSL=no" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
sudo touch /var/www/html/index.html
|
||||
export TEST_TYPE="linux"
|
||||
sudo cp ready.conf /etc/bunkerweb/configs/server-http
|
||||
fi
|
||||
|
||||
manual=0
|
||||
end=0
|
||||
cleanup_stack () {
|
||||
exit_code=$?
|
||||
if [[ $end -eq 1 || $exit_code = 1 ]] || [[ $end -eq 0 && $exit_code = 0 ]] && [ $manual = 0 ] ; then
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@USE_REVERSE_SCAN: "yes"@USE_REVERSE_SCAN: "no"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@USE_ANTIBOT: "cookie"@USE_ANTIBOT: "no"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@REDIS_PORT: "[0-9]*"@REDIS_PORT: "6379"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@REDIS_DATABASE: "1"@REDIS_DATABASE: "0"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@REDIS_SSL: "yes"@REDIS_SSL: "no"@' {} \;
|
||||
else
|
||||
sudo rm -rf tls
|
||||
sudo sed -i 's@USE_REVERSE_SCAN=.*$@USE_REVERSE_SCAN=no@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@USE_ANTIBOT=.*$@USE_ANTIBOT=no@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@REDIS_PORT=.*$@REDIS_PORT=6379@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@REDIS_DATABASE=.*$@REDIS_DATABASE=0@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@REDIS_SSL=.*$@REDIS_SSL=no@' /etc/bunkerweb/variables.env
|
||||
unset USE_REVERSE_SCAN
|
||||
unset USE_ANTIBOT
|
||||
unset REDIS_PORT
|
||||
unset REDIS_DATABASE
|
||||
unset REDIS_SSL
|
||||
sudo killall redis-server
|
||||
fi
|
||||
if [[ $end -eq 1 && $exit_code = 0 ]] ; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "🧰 Cleaning up current stack ..."
|
||||
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
docker compose down -v --remove-orphans
|
||||
else
|
||||
sudo systemctl stop bunkerweb
|
||||
sudo truncate -s 0 /var/log/bunkerweb/error.log
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Cleanup failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🧰 Cleaning up current stack done ✅"
|
||||
}
|
||||
|
||||
# Cleanup stack on exit
|
||||
trap cleanup_stack EXIT
|
||||
|
||||
for test in "activated" "reverse_scan" "antibot" "tweaked"
|
||||
do
|
||||
if [ "$test" = "activated" ] ; then
|
||||
echo "🧰 Running tests with redis with default values ..."
|
||||
elif [ "$test" = "reverse_scan" ] ; then
|
||||
echo "🧰 Running tests with redis with reverse scan activated ..."
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@USE_REVERSE_SCAN: "no"@USE_REVERSE_SCAN: "yes"@' {} \;
|
||||
else
|
||||
sudo sed -i 's@USE_REVERSE_SCAN=.*$@USE_REVERSE_SCAN=yes@' /etc/bunkerweb/variables.env
|
||||
export USE_REVERSE_SCAN="yes"
|
||||
fi
|
||||
elif [ "$test" = "antibot" ] ; then
|
||||
echo "🧰 Running tests with redis with antibot cookie activated ..."
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@USE_REVERSE_SCAN: "yes"@USE_REVERSE_SCAN: "no"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@USE_ANTIBOT: "no"@USE_ANTIBOT: "cookie"@' {} \;
|
||||
else
|
||||
sudo sed -i 's@USE_REVERSE_SCAN=.*$@USE_REVERSE_SCAN=no@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@USE_ANTIBOT=.*$@USE_ANTIBOT=cookie@' /etc/bunkerweb/variables.env
|
||||
export USE_REVERSE_SCAN="no"
|
||||
export USE_ANTIBOT="cookie"
|
||||
fi
|
||||
elif [ "$test" = "tweaked" ] ; then
|
||||
echo "🧰 Running tests with redis' settings tweaked ..."
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@USE_ANTIBOT: "cookie"@USE_ANTIBOT: "no"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@REDIS_PORT: "[0-9]*"@REDIS_PORT: "6380"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@REDIS_DATABASE: "0"@REDIS_DATABASE: "1"@' {} \;
|
||||
find . -type f -name 'docker-compose.*' -exec sed -i 's@REDIS_SSL: "no"@REDIS_SSL: "yes"@' {} \;
|
||||
else
|
||||
sudo sed -i 's@USE_ANTIBOT=.*$@USE_ANTIBOT=no@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@REDIS_PORT=.*$@REDIS_PORT=6380@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@REDIS_DATABASE=.*$@REDIS_DATABASE=1@' /etc/bunkerweb/variables.env
|
||||
sudo sed -i 's@REDIS_SSL=.*$@REDIS_SSL=yes@' /etc/bunkerweb/variables.env
|
||||
unset USE_ANTIBOT
|
||||
export REDIS_PORT="6380"
|
||||
export REDIS_DATABASE="1"
|
||||
export REDIS_SSL="yes"
|
||||
|
||||
echo "🧰 Stopping redis ..."
|
||||
sudo killall redis-server
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Redis stop failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
echo "🧰 Redis stopped ✅"
|
||||
echo "🧰 Starting redis with tweaked settings ..."
|
||||
redis-server --tls-port 6380 --port 0 --tls-cert-file tls/redis.pem --tls-key-file tls/redis.key --tls-ca-cert-file tls/ca.crt --tls-auth-clients no --daemonize yes
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Redis start failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
echo "🧰 Redis started ✅"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "🧰 Starting stack ..."
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
docker compose up -d
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Up failed, retrying ... ⚠️"
|
||||
manual=1
|
||||
cleanup_stack
|
||||
manual=0
|
||||
docker compose up -d
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Up failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
sudo systemctl start bunkerweb
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Start failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if stack is healthy
|
||||
echo "🧰 Waiting for stack to be healthy ..."
|
||||
i=0
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
while [ $i -lt 120 ] ; do
|
||||
containers=("redis-bw-1" "redis-bw-scheduler-1")
|
||||
healthy="true"
|
||||
for container in "${containers[@]}" ; do
|
||||
check="$(docker inspect --format "{{json .State.Health }}" "$container" | grep "healthy")"
|
||||
if [ "$check" = "" ] ; then
|
||||
healthy="false"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$healthy" = "true" ] ; then
|
||||
echo "🧰 Docker stack is healthy ✅"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i+1))
|
||||
done
|
||||
if [ $i -ge 120 ] ; then
|
||||
docker compose logs
|
||||
echo "🧰 Docker stack is not healthy ❌"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
healthy="false"
|
||||
retries=0
|
||||
while [[ $healthy = "false" && $retries -lt 5 ]] ; do
|
||||
while [ $i -lt 120 ] ; do
|
||||
if sudo grep -q "BunkerWeb is ready" "/var/log/bunkerweb/error.log" ; then
|
||||
echo "🧰 Linux stack is healthy ✅"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i+1))
|
||||
done
|
||||
if [ $i -ge 120 ] ; then
|
||||
sudo journalctl -u bunkerweb --no-pager
|
||||
echo "🛡️ Showing BunkerWeb error logs ..."
|
||||
sudo cat /var/log/bunkerweb/error.log
|
||||
echo "🛡️ Showing BunkerWeb access logs ..."
|
||||
sudo cat /var/log/bunkerweb/access.log
|
||||
echo "🧰 Linux stack is not healthy ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if sudo journalctl -u bunkerweb --no-pager | grep -q "SYSTEMCTL - ❌ " ; then
|
||||
echo "🧰 ⚠ Linux stack got an issue, restarting ..."
|
||||
sudo journalctl --rotate
|
||||
sudo journalctl --vacuum-time=1s
|
||||
manual=1
|
||||
cleanup_stack
|
||||
manual=0
|
||||
sudo systemctl start bunkerweb
|
||||
retries=$((retries+1))
|
||||
else
|
||||
healthy="true"
|
||||
fi
|
||||
done
|
||||
if [ "$retries" -ge 5 ] ; then
|
||||
echo "🧰 Linux stack could not be healthy ❌"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start tests
|
||||
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
docker compose -f docker-compose.test.yml up --abort-on-container-exit --exit-code-from tests
|
||||
else
|
||||
python3 main.py
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "🧰 Test \"$test\" failed ❌"
|
||||
echo "🛡️ Showing BunkerWeb and BunkerWeb Scheduler logs ..."
|
||||
if [ "$integration" == "docker" ] ; then
|
||||
docker compose logs bw bw-scheduler
|
||||
else
|
||||
sudo journalctl -u bunkerweb --no-pager
|
||||
echo "🛡️ Showing BunkerWeb error logs ..."
|
||||
sudo cat /var/log/bunkerweb/error.log
|
||||
echo "🛡️ Showing BunkerWeb access logs ..."
|
||||
sudo cat /var/log/bunkerweb/access.log
|
||||
echo "🛡️ Showing Geckodriver logs ..."
|
||||
sudo cat geckodriver.log
|
||||
fi
|
||||
exit 1
|
||||
else
|
||||
echo "🧰 Test \"$test\" succeeded ✅"
|
||||
fi
|
||||
|
||||
manual=1
|
||||
cleanup_stack
|
||||
manual=0
|
||||
|
||||
echo " "
|
||||
done
|
||||
|
||||
end=1
|
||||
echo "🧰 Tests are done ! ✅"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from contextlib import suppress
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
from os import getenv, listdir
|
||||
from os import getenv, listdir, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
|
|
@ -20,11 +20,20 @@ from selenium.webdriver.remote.webelement import WebElement
|
|||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.common.exceptions import ElementClickInterceptedException, TimeoutException, WebDriverException
|
||||
|
||||
default_server = "127.0.0.1"
|
||||
|
||||
integration_path = Path(sep, "usr", "share", "bunkerweb", "INTEGRATION")
|
||||
os_release_path = Path(sep, "etc", "os-release")
|
||||
if getenv("KUBERNETES_MODE", "no").lower() == "yes" or getenv("SWARM_MODE", "no").lower() == "yes" or getenv("AUTOCONF_MODE", "no").lower() == "yes":
|
||||
default_server = "192.168.0.2"
|
||||
elif os_release_path.is_file() and "Alpine" in os_release_path.read_text(encoding="utf-8"):
|
||||
default_server = "192.168.0.2"
|
||||
|
||||
ready = False
|
||||
retries = 0
|
||||
while not ready:
|
||||
with suppress(RequestException):
|
||||
status_code = get("http://127.0.0.1/setup").status_code
|
||||
status_code = get(f"http://{default_server}/setup").status_code
|
||||
|
||||
if status_code > 500 and status_code != 502:
|
||||
print("An error occurred with the server, exiting ...", flush=True)
|
||||
|
|
@ -179,9 +188,9 @@ with driver_func() as driver:
|
|||
driver.maximize_window()
|
||||
driver_wait = WebDriverWait(driver, 60)
|
||||
|
||||
print("Navigating to http://127.0.0.1/setup ...", flush=True)
|
||||
print(f"Navigating to http://{default_server}/setup ...", flush=True)
|
||||
|
||||
driver.get("http://127.0.0.1/setup")
|
||||
driver.get(f"http://{default_server}/setup")
|
||||
|
||||
### WIZARD PAGE
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue