mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Merge pull request #691 from bunkerity/dev
Merge branch "dev" into branch "ui"
This commit is contained in:
commit
85ef4e4dea
71 changed files with 1590 additions and 1344 deletions
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
|
|
@ -21,11 +21,11 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
|
||||
uses: github/codeql-action/init@fdcae64e1484d349b3366718cdfef3d404390e85 # v2.22.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config-file: ./.github/codeql.yml
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
|
||||
uses: github/codeql-action/analyze@fdcae64e1484d349b3366718cdfef3d404390e85 # v2.22.1
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
|||
2
.github/workflows/dev-update-mmdb.yml
vendored
2
.github/workflows/dev-update-mmdb.yml
vendored
|
|
@ -52,7 +52,7 @@ jobs:
|
|||
rm -f asn.mmdb country.mmdb
|
||||
gunzip asn.mmdb.gz country.mmdb.gz
|
||||
- name: Commit and push changes
|
||||
uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a # v4.16.0
|
||||
uses: stefanzweifel/git-auto-commit-action@8756aa072ef5b4a080af5dc8fef36c5d586e521d # v5.0.0
|
||||
with:
|
||||
branch: dev
|
||||
commit_message: "Monthly mmdb update"
|
||||
|
|
|
|||
2
.github/workflows/push-packagecloud.yml
vendored
2
.github/workflows/push-packagecloud.yml
vendored
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
- name: Check out repository code
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- name: Install ruby
|
||||
uses: ruby/setup-ruby@52b8784594ec115fd17094752708121dc5dabb47 # v1.154.0
|
||||
uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0
|
||||
with:
|
||||
ruby-version: "3.0"
|
||||
- name: Install packagecloud
|
||||
|
|
|
|||
4
.github/workflows/scorecards-analysis.yml
vendored
4
.github/workflows/scorecards-analysis.yml
vendored
|
|
@ -19,12 +19,12 @@ jobs:
|
|||
with:
|
||||
persist-credentials: false
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
|
||||
uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
publish_results: true
|
||||
- name: "Upload SARIF results to code scanning"
|
||||
uses: github/codeql-action/upload-sarif@ddccb873888234080b77e9bc2d4764d5ccaaccf9 # v2.21.9
|
||||
uses: github/codeql-action/upload-sarif@fdcae64e1484d349b3366718cdfef3d404390e85 # v2.22.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
|||
2
.luacheckrc
Normal file
2
.luacheckrc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
globals = {"ngx", "delay", "unpack"}
|
||||
ignore = {"411"}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
exclude: (^LICENSE.md$|^src/VERSION$|^src/(bw/misc/root-ca.pem$|deps/src/|common/core/modsecurity/files|ui/static/js/(editor/|utils/purify/|tsparticles\.bundle\.min\.js))|\.(svg|drawio|patch\d?|ascii|tf|tftpl)$)
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: f71fa2c1f9cf5cb705f73dffe4b21f7c61470ba9 # frozen: v4.4.0
|
||||
rev: c4a0b883114b00d8d76b479c820ce7950211c99b # frozen: v4.5.0
|
||||
hooks:
|
||||
- id: requirements-txt-fixer
|
||||
- id: trailing-whitespace
|
||||
|
|
@ -26,6 +26,19 @@ repos:
|
|||
- id: prettier
|
||||
name: Prettier Code Formatter
|
||||
|
||||
- repo: https://github.com/JohnnyMorganz/StyLua
|
||||
rev: 27e6b388796604181e810ef05c9fb15a9f7a7769 # frozen: v0.18.2
|
||||
hooks:
|
||||
- id: stylua-github
|
||||
exclude: ^src/(bw/lua/middleclass.lua|common/core/antibot/captcha.lua)$
|
||||
|
||||
- repo: https://github.com/lunarmodules/luacheck
|
||||
rev: ababb6d403d634eb74d2c541035e9ede966e710d # frozen: v1.1.1
|
||||
hooks:
|
||||
- id: luacheck
|
||||
exclude: ^src/(bw/lua/middleclass.lua|common/core/antibot/captcha.lua)$
|
||||
args: ["--std", "min", "--codes", "--ranges", "--no-cache"]
|
||||
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 10f4af6dbcf93456ba7df762278ae61ba3120dc6 # frozen: 6.1.0
|
||||
hooks:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
mkdocs==1.5.3
|
||||
mkdocs-material==9.4.3
|
||||
pytablewriter==1.1.0
|
||||
mkdocs-material==9.4.5
|
||||
pytablewriter==1.2.0
|
||||
mike==1.1.2
|
||||
mkdocs-print-site-plugin==2.3.6
|
||||
|
|
|
|||
|
|
@ -143,9 +143,9 @@ jinja2==3.1.2 \
|
|||
# mike
|
||||
# mkdocs
|
||||
# mkdocs-material
|
||||
markdown==3.4.4 \
|
||||
--hash=sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6 \
|
||||
--hash=sha256:a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941
|
||||
markdown==3.5 \
|
||||
--hash=sha256:4afb124395ce5fc34e6d9886dab977fd9ae987fc6e85689f08278cf0c69d4bf3 \
|
||||
--hash=sha256:a807eb2e4778d9156c8f07876c6e4d50b5494c5665c4834f67b06459dfd877b3
|
||||
# via
|
||||
# mkdocs
|
||||
# mkdocs-material
|
||||
|
|
@ -236,9 +236,9 @@ mkdocs==1.5.3 \
|
|||
# -r requirements.in
|
||||
# mike
|
||||
# mkdocs-material
|
||||
mkdocs-material==9.4.3 \
|
||||
--hash=sha256:3274a47a4e55a541b25bd8fa4937cf3f3c82a51763453511661e0052062758b9 \
|
||||
--hash=sha256:5c9abc3f6ba8f88be1f9f13df23d695ca4dddbdd8a3538e4e6279c055c3936bc
|
||||
mkdocs-material==9.4.5 \
|
||||
--hash=sha256:0922e3e34d95dbf3f0c84fc817a233e1cbd5874830c6fd821d525ba90e0ce077 \
|
||||
--hash=sha256:efaa52843a8c64b99bcf5a97cf4ba57d410424828e3087b13c9b3954e8adb5ff
|
||||
# via
|
||||
# -r requirements.in
|
||||
# mkdocs-print-site-plugin
|
||||
|
|
@ -279,9 +279,9 @@ pymdown-extensions==10.3 \
|
|||
--hash=sha256:77a82c621c58a83efc49a389159181d570e370fff9f810d3a4766a75fc678b66 \
|
||||
--hash=sha256:94a0d8a03246712b64698af223848fd80aaf1ae4c4be29c8c61939b0467b5722
|
||||
# via mkdocs-material
|
||||
pytablewriter==1.1.0 \
|
||||
--hash=sha256:3e0f05c6479f0817ca8cdfb27d509026a6894031170760cf99b31a2d1c24c21a \
|
||||
--hash=sha256:95938cc210f7905895c1b62c465176da1ef7bb7b9e3fa7d25a6fad6bcb1903ed
|
||||
pytablewriter==1.2.0 \
|
||||
--hash=sha256:0204a4bb684a22140d640f2599f09e137bcdc18b3dd49426f4a555016e246b46 \
|
||||
--hash=sha256:4a30e2bb4bf5bc1069b1d2b2bc41947577c4517ab0875b23a5b194d296f543d8
|
||||
# via -r requirements.in
|
||||
python-dateutil==2.8.2 \
|
||||
--hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
|
||||
|
|
|
|||
|
|
@ -1,20 +1,46 @@
|
|||
local dummy = {}
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
|
||||
local logger = require "logger"
|
||||
local dummy = class("dummy", plugin)
|
||||
|
||||
dummy.init = function(self)
|
||||
logger.log(ngx.NOTICE, "DUMMY", "init() called")
|
||||
return true, "success"
|
||||
function dummy:initialize()
|
||||
plugin.initialize(self, "dummy")
|
||||
self.dummy = "dummy"
|
||||
end
|
||||
|
||||
dummy.access = function(self)
|
||||
logger.log(ngx.NOTICE, "DUMMY", "access() called")
|
||||
return true, "success", false, nil
|
||||
function dummy:init()
|
||||
self.logger:log(ngx.NOTICE, "init called")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
dummy.log = function(self)
|
||||
logger.log(ngx.NOTICE, "DUMMY", "log() called")
|
||||
return true, "success"
|
||||
function dummy:set()
|
||||
self.logger:log(ngx.NOTICE, "set called")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
function dummy:access()
|
||||
self.logger:log(ngx.NOTICE, "access called")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
function dummy:log()
|
||||
self.logger:log(ngx.NOTICE, "log called")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
function dummy:log_default()
|
||||
self.logger:log(ngx.NOTICE, "log_default called")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
function dummy:preread()
|
||||
self.logger:log(ngx.NOTICE, "preread called")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
function dummy:log_stream()
|
||||
self.logger:log(ngx.NOTICE, "log_stream called")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
return dummy
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"name": "Dummy example plugin",
|
||||
"description": "Just an example plugin.",
|
||||
"version": "0.1",
|
||||
"stream": "no",
|
||||
"settings": {
|
||||
"DUMMY_SETTING": {
|
||||
"context": "multisite",
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ ansible==8.4.0 \
|
|||
--hash=sha256:d601d89a4306934e7c0aae05195fd72c0719287fde165982d0ebac282b4280f1 \
|
||||
--hash=sha256:f33c492690592fad12684e9897f6de2da15c9f6e1ecb79137703a06470af2ce6
|
||||
# via -r requirements-ansible.in
|
||||
ansible-core==2.15.4 \
|
||||
--hash=sha256:5c57089405406f3004e948127b518b65509e280d524f61f91cc6360303fc388b \
|
||||
--hash=sha256:c1a8aaede985f79e5932ba2163639379f7d8025bfd9b28378db1649a4ef541ed
|
||||
ansible-core==2.15.5 \
|
||||
--hash=sha256:3efa234de5fce79ec98853f3369535b27cacd7ce498495b996030cd15c373735 \
|
||||
--hash=sha256:8cc539cb8d4349af3ffd901c70722f7a7a203ae6427ddac95ffdf546a6e41602
|
||||
# via ansible
|
||||
cffi==1.16.0 \
|
||||
--hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
local class = require "middleclass"
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local cjson = require "cjson"
|
||||
local upload = require "resty.upload"
|
||||
local rsignal = require "resty.signal"
|
||||
local process = require "ngx.process"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local process = require "ngx.process"
|
||||
local rsignal = require "resty.signal"
|
||||
local upload = require "resty.upload"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local api = class("api")
|
||||
local api = class("api")
|
||||
|
||||
api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
|
||||
api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
|
||||
|
||||
function api:initialize()
|
||||
self.datastore = datastore:new()
|
||||
|
|
@ -26,6 +26,7 @@ function api:initialize()
|
|||
end
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function api:log_cmd(cmd, status, stdout, stderr)
|
||||
local level = ngx.NOTICE
|
||||
local prefix = "success"
|
||||
|
|
@ -33,7 +34,7 @@ function api:log_cmd(cmd, status, stdout, stderr)
|
|||
level = ngx.ERR
|
||||
prefix = "error"
|
||||
end
|
||||
self.logger:log(level, prefix .. " while running command " .. command)
|
||||
self.logger:log(level, prefix .. " while running command " .. cmd)
|
||||
self.logger:log(level, "stdout = " .. stdout)
|
||||
self.logger:log(level, "stdout = " .. stderr)
|
||||
end
|
||||
|
|
@ -41,6 +42,7 @@ 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)
|
||||
-- Timeout
|
||||
|
|
@ -51,6 +53,7 @@ function api:cmd(cmd)
|
|||
return status == 0, reason, status
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function api:response(http_status, api_status, msg)
|
||||
local resp = {}
|
||||
resp["status"] = api_status
|
||||
|
|
@ -101,6 +104,7 @@ api.global.POST["^/confs$"] = function(self)
|
|||
form:set_timeout(1000)
|
||||
local file = io.open(tmp, "w+")
|
||||
while true do
|
||||
-- luacheck: ignore 421
|
||||
local typ, res, err = form:read()
|
||||
if not typ then
|
||||
file:close()
|
||||
|
|
@ -117,9 +121,9 @@ api.global.POST["^/confs$"] = function(self)
|
|||
file:close()
|
||||
local cmds = {
|
||||
"rm -rf " .. destination .. "/*",
|
||||
"tar xzf " .. tmp .. " -C " .. destination
|
||||
"tar xzf " .. tmp .. " -C " .. destination,
|
||||
}
|
||||
for i, cmd in ipairs(cmds) do
|
||||
for _, cmd in ipairs(cmds) do
|
||||
local status = os.execute(cmd)
|
||||
if status ~= 0 then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||
|
|
@ -176,17 +180,23 @@ end
|
|||
|
||||
api.global.GET["^/bans$"] = function(self)
|
||||
local data = {}
|
||||
for i, k in ipairs(self.datastore:keys()) do
|
||||
for _, k in ipairs(self.datastore:keys()) do
|
||||
if k:find("^bans_ip_") then
|
||||
local reason, err = self.datastore:get(k)
|
||||
if err then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
|
||||
"can't access " .. k .. " from datastore : " .. reason)
|
||||
return self:response(
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
"error",
|
||||
"can't access " .. k .. " from datastore : " .. reason
|
||||
)
|
||||
end
|
||||
local ok, ttl = self.datastore:ttl(k)
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
|
||||
"can't access ttl " .. k .. " from datastore : " .. ttl)
|
||||
return self:response(
|
||||
ngx.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) }
|
||||
table.insert(data, ban)
|
||||
|
|
@ -196,7 +206,7 @@ api.global.GET["^/bans$"] = function(self)
|
|||
end
|
||||
|
||||
api.global.GET["^/variables$"] = function(self)
|
||||
local variables, err = datastore:get('variables', true)
|
||||
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)
|
||||
end
|
||||
|
|
@ -219,9 +229,9 @@ function api:do_api_call()
|
|||
if status ~= ngx.HTTP_OK then
|
||||
ret = false
|
||||
end
|
||||
if (#resp["msg"] == 0) then
|
||||
if #resp["msg"] == 0 then
|
||||
resp["msg"] = ""
|
||||
elseif (type(resp["msg"]) == "table") then
|
||||
elseif type(resp["msg"]) == "table" then
|
||||
resp["data"] = resp["msg"]
|
||||
resp["msg"] = resp["status"]
|
||||
end
|
||||
|
|
@ -231,10 +241,10 @@ function api:do_api_call()
|
|||
end
|
||||
local list, err = self.datastore:get("plugins", true)
|
||||
if not list then
|
||||
local status, resp = self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't list loaded plugins : " .. err)
|
||||
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)
|
||||
end
|
||||
for i, plugin in ipairs(list) do
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,42 +1,38 @@
|
|||
local mlcache = require "resty.mlcache"
|
||||
local class = require "middleclass"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
local cachestore = class("cachestore")
|
||||
local logger = require "bunkerweb.logger"
|
||||
local mlcache = require "resty.mlcache"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = class("cachestore")
|
||||
|
||||
-- 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"
|
||||
local shm = "cachestore"
|
||||
local ipc_shm = "cachestore_ipc"
|
||||
local shm_miss = "cachestore_miss"
|
||||
local shm_locks = "cachestore_locks"
|
||||
if not ngx.shared.cachestore then
|
||||
shm = "cachestore_stream"
|
||||
ipc_shm = "cachestore_ipc_stream"
|
||||
shm_miss = "cachestore_miss_stream"
|
||||
shm = "cachestore_stream"
|
||||
ipc_shm = "cachestore_ipc_stream"
|
||||
shm_miss = "cachestore_miss_stream"
|
||||
shm_locks = "cachestore_locks_stream"
|
||||
end
|
||||
local cache, err = mlcache.new(
|
||||
"cachestore",
|
||||
shm,
|
||||
{
|
||||
lru_size = 100,
|
||||
ttl = 30,
|
||||
neg_ttl = 0.1,
|
||||
shm_set_tries = 3,
|
||||
shm_miss = shm_miss,
|
||||
shm_locks = shm_locks,
|
||||
resty_lock_opts = {
|
||||
exptime = 30,
|
||||
timeout = 5,
|
||||
step = 0.001,
|
||||
ratio = 2,
|
||||
max_step = 0.5
|
||||
},
|
||||
ipc_shm = ipc_shm
|
||||
}
|
||||
)
|
||||
local cache, err = mlcache.new("cachestore", shm, {
|
||||
lru_size = 100,
|
||||
ttl = 30,
|
||||
neg_ttl = 0.1,
|
||||
shm_set_tries = 3,
|
||||
shm_miss = shm_miss,
|
||||
shm_locks = shm_locks,
|
||||
resty_lock_opts = {
|
||||
exptime = 30,
|
||||
timeout = 5,
|
||||
step = 0.001,
|
||||
ratio = 2,
|
||||
max_step = 0.5,
|
||||
},
|
||||
ipc_shm = ipc_shm,
|
||||
})
|
||||
local module_logger = logger:new("CACHESTORE")
|
||||
if not cache then
|
||||
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
|
||||
|
|
@ -57,10 +53,12 @@ function cachestore:initialize(use_redis, new_cs, ctx)
|
|||
end
|
||||
|
||||
function cachestore:get(key)
|
||||
-- luacheck: ignore 432
|
||||
local callback = function(key, cs)
|
||||
-- Connect to redis
|
||||
-- luacheck: ignore 431
|
||||
local clusterstore = cs or require "bunkerweb.clusterstore":new(false)
|
||||
local ok, err, reused = clusterstore:connect()
|
||||
local ok, err, _ = clusterstore:connect()
|
||||
if not ok then
|
||||
return nil, "can't connect to redis : " .. err, nil
|
||||
end
|
||||
|
|
@ -96,6 +94,7 @@ function cachestore:get(key)
|
|||
local callback_no_miss = function()
|
||||
return nil, nil, -1
|
||||
end
|
||||
-- luacheck: ignore 431
|
||||
local value, err, hit_level
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local cs = nil
|
||||
|
|
@ -114,13 +113,14 @@ function cachestore:get(key)
|
|||
end
|
||||
|
||||
function cachestore:set(key, value, ex)
|
||||
-- luacheck: ignore 431
|
||||
local ok, err
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local ok, err = self:set_redis(key, value, ex)
|
||||
ok, err = self:set_redis(key, value, ex)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
end
|
||||
local ok, err
|
||||
if ex then
|
||||
ok, err = self.cache:set(key, { ttl = ex }, value)
|
||||
else
|
||||
|
|
@ -134,13 +134,14 @@ end
|
|||
|
||||
function cachestore:set_redis(key, value, ex)
|
||||
-- Connect to redis
|
||||
local ok, err, reused = self.clusterstore:connect()
|
||||
-- luacheck: ignore 431
|
||||
local ok, err, _ = self.clusterstore:connect()
|
||||
if not ok then
|
||||
return false, "can't connect to redis : " .. err
|
||||
end
|
||||
-- Set value with ttl
|
||||
local default_ex = ex or 30
|
||||
local ok, err = self.clusterstore:call("set", key, value, "EX", default_ex)
|
||||
local _, err = self.clusterstore:call("set", key, value, "EX", default_ex)
|
||||
if err then
|
||||
self.clusterstore:close()
|
||||
return false, "SET failed : " .. err
|
||||
|
|
@ -149,14 +150,16 @@ function cachestore:set_redis(key, value, ex)
|
|||
return true
|
||||
end
|
||||
|
||||
function cachestore:delete(key, value, ex)
|
||||
function cachestore:delete(key)
|
||||
-- luacheck: ignore 431
|
||||
local ok, err
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local ok, err = self.del_redis(key)
|
||||
ok, err = self:del_redis(key)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
end
|
||||
local ok, err = self.cache:delete(key)
|
||||
ok, err = self.cache:delete(key)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
|
|
@ -165,12 +168,13 @@ end
|
|||
|
||||
function cachestore:del_redis(key)
|
||||
-- Connect to redis
|
||||
-- luacheck: ignore 431
|
||||
local ok, err = self.clusterstore:connect()
|
||||
if not ok then
|
||||
return false, "can't connect to redis : " .. err
|
||||
end
|
||||
-- Set value with ttl
|
||||
local ok, err = self.clusterstore:del(key)
|
||||
local _, err = self.clusterstore:del(key)
|
||||
if err then
|
||||
self.clusterstore:close()
|
||||
return false, "DEL failed : " .. err
|
||||
|
|
|
|||
|
|
@ -1,135 +1,138 @@
|
|||
local class = require "middleclass"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local redis = require "resty.redis"
|
||||
local class = require "middleclass"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local redis = require "resty.redis"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local clusterstore = class("clusterstore")
|
||||
|
||||
function clusterstore:initialize(pool)
|
||||
-- Instantiate logger
|
||||
self.logger = logger:new("CLUSTERSTORE")
|
||||
-- Get variables
|
||||
local variables = {
|
||||
["REDIS_HOST"] = "",
|
||||
["REDIS_PORT"] = "",
|
||||
["REDIS_DATABASE"] = "",
|
||||
["REDIS_SSL"] = "",
|
||||
["REDIS_TIMEOUT"] = "",
|
||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = ""
|
||||
}
|
||||
-- Set them for later user
|
||||
self.variables = {}
|
||||
for k, v in pairs(variables) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.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
|
||||
-- Instantiate logger
|
||||
self.logger = logger:new("CLUSTERSTORE")
|
||||
-- Get variables
|
||||
local variables = {
|
||||
["REDIS_HOST"] = "",
|
||||
["REDIS_PORT"] = "",
|
||||
["REDIS_DATABASE"] = "",
|
||||
["REDIS_SSL"] = "",
|
||||
["REDIS_TIMEOUT"] = "",
|
||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = "",
|
||||
}
|
||||
-- Set them for later user
|
||||
self.variables = {}
|
||||
for k, _ in pairs(variables) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.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()
|
||||
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
|
||||
local options = {
|
||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||
}
|
||||
if self.pool then
|
||||
options.pool = "bw-redis"
|
||||
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
|
||||
local select, err = self.redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
||||
if err then
|
||||
self:close()
|
||||
return false, err
|
||||
end
|
||||
end
|
||||
return true, "success", times
|
||||
-- Check if we are already connected
|
||||
if self.redis_client then
|
||||
return true, "already connected", self.redis_client:get_reused_times()
|
||||
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
|
||||
local options = {
|
||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||
}
|
||||
if self.pool then
|
||||
options.pool = "bw-redis"
|
||||
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
|
||||
end
|
||||
end
|
||||
return true, "success", 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
|
||||
end
|
||||
return false, "not connected"
|
||||
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
|
||||
end
|
||||
return false, "not connected"
|
||||
end
|
||||
|
||||
function clusterstore:call(method, ...)
|
||||
-- Check if we are connected
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
end
|
||||
-- Call method
|
||||
return self.redis_client[method](self.redis_client, ...)
|
||||
-- Check if we are connected
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
end
|
||||
-- Call method
|
||||
return self.redis_client[method](self.redis_client, ...)
|
||||
end
|
||||
|
||||
function clusterstore:multi(calls)
|
||||
-- Check if we are connected
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
end
|
||||
-- Start transaction
|
||||
local ok, err = self.redis_client:multi()
|
||||
if not ok then
|
||||
return false, "multi() failed : " .. err
|
||||
end
|
||||
-- Loop on calls
|
||||
for i, call in ipairs(calls) do
|
||||
local method = call[1]
|
||||
local args = unpack(call[2])
|
||||
local ok, err = self.redis_client[method](self.redis_client, args)
|
||||
if not ok then
|
||||
return false, method + "() failed : " .. err
|
||||
end
|
||||
end
|
||||
-- Exec transaction
|
||||
local exec, err = self.redis_client:exec()
|
||||
if not exec then
|
||||
return false, "exec() failed : " .. err
|
||||
end
|
||||
if type(exec) ~= "table" then
|
||||
return false, "exec() result is not a table"
|
||||
end
|
||||
return true, "success", exec
|
||||
-- Check if we are connected
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
end
|
||||
-- Start transaction
|
||||
local ok, err = self.redis_client:multi()
|
||||
if not ok then
|
||||
return false, "multi() failed : " .. err
|
||||
end
|
||||
-- Loop on calls
|
||||
for _, call in ipairs(calls) do
|
||||
local method = call[1]
|
||||
local args = unpack(call[2])
|
||||
ok, err = self.redis_client[method](self.redis_client, args)
|
||||
if not ok then
|
||||
return false, method + "() failed : " .. err
|
||||
end
|
||||
end
|
||||
-- Exec transaction
|
||||
local exec, err = self.redis_client:exec()
|
||||
if not exec then
|
||||
return false, "exec() failed : " .. err
|
||||
end
|
||||
if type(exec) ~= "table" then
|
||||
return false, "exec() result is not a table"
|
||||
end
|
||||
return true, "success", exec
|
||||
end
|
||||
|
||||
return clusterstore
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
local class = require "middleclass"
|
||||
local lrucache = require "resty.lrucache"
|
||||
local class = require "middleclass"
|
||||
local lrucache = require "resty.lrucache"
|
||||
local datastore = class("datastore")
|
||||
|
||||
local lru, err = lrucache.new(100000)
|
||||
local lru, err = lrucache.new(100000)
|
||||
if not lru then
|
||||
require "bunkerweb.logger":new("DATASTORE"):log(ngx.ERR,
|
||||
"failed to instantiate LRU cache : " .. (err or "unknown error"))
|
||||
require "bunkerweb.logger"
|
||||
:new("DATASTORE")
|
||||
:log(ngx.ERR, "failed to instantiate LRU cache : " .. (err or "unknown error"))
|
||||
end
|
||||
|
||||
function datastore:initialize()
|
||||
|
|
@ -16,11 +17,13 @@ function datastore:initialize()
|
|||
end
|
||||
|
||||
function datastore:get(key, worker)
|
||||
-- luacheck: ignore 431
|
||||
local value, err
|
||||
if worker then
|
||||
local value, err = lru:get(key)
|
||||
value, err = lru:get(key)
|
||||
return value, err or "not found"
|
||||
end
|
||||
local value, err = self.dict:get(key)
|
||||
value, err = self.dict:get(key)
|
||||
if not value and not err then
|
||||
err = "not found"
|
||||
end
|
||||
|
|
@ -52,10 +55,11 @@ function datastore:keys(worker)
|
|||
return self.dict:get_keys(0)
|
||||
end
|
||||
|
||||
function datastore:ttl(key)
|
||||
function datastore:ttl(key, worker)
|
||||
if worker then
|
||||
return false, "not supported by LRU"
|
||||
end
|
||||
-- luacheck: ignore 431
|
||||
local ttl, err = self.dict:ttl(key)
|
||||
if not ttl then
|
||||
return false, err
|
||||
|
|
@ -64,13 +68,13 @@ function datastore:ttl(key)
|
|||
end
|
||||
|
||||
function datastore:delete_all(pattern, worker)
|
||||
local keys = {}
|
||||
local keys
|
||||
if worker then
|
||||
keys = lru:keys(0)
|
||||
else
|
||||
keys = self.dict:get_keys(0)
|
||||
end
|
||||
for i, key in ipairs(keys) do
|
||||
for _, key in ipairs(keys) do
|
||||
if key:match(pattern) then
|
||||
self.dict:delete(key)
|
||||
end
|
||||
|
|
@ -78,6 +82,7 @@ function datastore:delete_all(pattern, worker)
|
|||
return true, "success"
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function datastore:flush_lru()
|
||||
lru:flush_all()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,263 +1,263 @@
|
|||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local cjson = require "cjson"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local helpers = {}
|
||||
local helpers = {}
|
||||
|
||||
helpers.load_plugin = function(json)
|
||||
-- Open file
|
||||
local file, err, nb = io.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"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid JSON at " .. json .. " : " .. err
|
||||
end
|
||||
-- Check fields
|
||||
local missing_fields = {}
|
||||
local required_fields = { "id", "name", "description", "version", "settings", "stream" }
|
||||
for i, field in ipairs(required_fields) do
|
||||
if plugin[field] == nil then
|
||||
table.insert(missing_fields, field)
|
||||
end
|
||||
end
|
||||
if #missing_fields > 0 then
|
||||
return false, "missing field(s) " .. cjson.encode(missing_fields) .. " for JSON at " .. json
|
||||
end
|
||||
-- Try require
|
||||
local plugin_lua, err = helpers.require_plugin(plugin.id)
|
||||
if plugin_lua == false then
|
||||
return false, err
|
||||
end
|
||||
-- Fill phases
|
||||
local phases = utils.get_phases()
|
||||
plugin.phases = {}
|
||||
if plugin_lua then
|
||||
for i, phase in ipairs(phases) do
|
||||
if plugin_lua[phase] ~= nil then
|
||||
table.insert(plugin.phases, phase)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Return plugin
|
||||
return true, plugin
|
||||
helpers.load_plugin = function(json)
|
||||
-- Open file
|
||||
local file, err, nb = io.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"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid JSON at " .. json .. " : " .. err
|
||||
end
|
||||
-- Check fields
|
||||
local missing_fields = {}
|
||||
local required_fields = { "id", "name", "description", "version", "settings", "stream" }
|
||||
for _, field in ipairs(required_fields) do
|
||||
if plugin[field] == nil then
|
||||
table.insert(missing_fields, field)
|
||||
end
|
||||
end
|
||||
if #missing_fields > 0 then
|
||||
return false, "missing field(s) " .. cjson.encode(missing_fields) .. " for JSON at " .. json
|
||||
end
|
||||
-- Try require
|
||||
local plugin_lua, err = helpers.require_plugin(plugin.id)
|
||||
if plugin_lua == false then
|
||||
return false, err
|
||||
end
|
||||
-- Fill phases
|
||||
local phases = utils.get_phases()
|
||||
plugin.phases = {}
|
||||
if plugin_lua then
|
||||
for _, phase in ipairs(phases) do
|
||||
if plugin_lua[phase] ~= nil then
|
||||
table.insert(plugin.phases, phase)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Return plugin
|
||||
return true, plugin
|
||||
end
|
||||
|
||||
helpers.order_plugins = function(plugins)
|
||||
-- Extract orders
|
||||
local file, err, nb = io.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"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid order.json : " .. err
|
||||
end
|
||||
-- Compute plugins/id/phases table
|
||||
local plugins_phases = {}
|
||||
for i, plugin in ipairs(plugins) do
|
||||
plugins_phases[plugin.id] = {}
|
||||
for j, phase in ipairs(plugin.phases) do
|
||||
plugins_phases[plugin.id][phase] = true
|
||||
end
|
||||
end
|
||||
-- Order result
|
||||
local result_orders = {}
|
||||
for i, phase in ipairs(utils.get_phases()) do
|
||||
result_orders[phase] = {}
|
||||
end
|
||||
-- Fill order first
|
||||
for phase, order in pairs(orders) do
|
||||
for i, id in ipairs(order) do
|
||||
local plugin = plugins_phases[id]
|
||||
if plugin and plugin[phase] then
|
||||
table.insert(result_orders[phase], id)
|
||||
plugin[phase] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Then append missing plugins to the end
|
||||
for i, phase in ipairs(utils.get_phases()) do
|
||||
for id, plugin in pairs(plugins_phases) do
|
||||
if plugin[phase] then
|
||||
table.insert(result_orders[phase], id)
|
||||
plugin[phase] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
return true, result_orders
|
||||
helpers.order_plugins = function(plugins)
|
||||
-- Extract orders
|
||||
local file, err, nb = io.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"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid order.json : " .. err
|
||||
end
|
||||
-- Compute plugins/id/phases table
|
||||
local plugins_phases = {}
|
||||
for _, plugin in ipairs(plugins) do
|
||||
plugins_phases[plugin.id] = {}
|
||||
for _, phase in ipairs(plugin.phases) do
|
||||
plugins_phases[plugin.id][phase] = true
|
||||
end
|
||||
end
|
||||
-- Order result
|
||||
local result_orders = {}
|
||||
for _, phase in ipairs(utils.get_phases()) do
|
||||
result_orders[phase] = {}
|
||||
end
|
||||
-- Fill order first
|
||||
for phase, order in pairs(orders) do
|
||||
for _, id in ipairs(order) do
|
||||
local plugin = plugins_phases[id]
|
||||
if plugin and plugin[phase] then
|
||||
table.insert(result_orders[phase], id)
|
||||
plugin[phase] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Then append missing plugins to the end
|
||||
for _, phase in ipairs(utils.get_phases()) do
|
||||
for id, plugin in pairs(plugins_phases) do
|
||||
if plugin[phase] then
|
||||
table.insert(result_orders[phase], id)
|
||||
plugin[phase] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
return true, result_orders
|
||||
end
|
||||
|
||||
helpers.require_plugin = function(id)
|
||||
-- Require call
|
||||
local ok, plugin_lua = pcall(require, id .. "/" .. id)
|
||||
if not ok then
|
||||
if plugin_lua:match("not found") then
|
||||
return nil, "plugin " .. id .. " doesn't have LUA code"
|
||||
end
|
||||
return false, "require error for plugin " .. id .. " : " .. plugin_lua
|
||||
end
|
||||
-- New call
|
||||
if plugin_lua.new == nil then
|
||||
return false, "missing new() method for plugin " .. id
|
||||
end
|
||||
-- Return plugin
|
||||
return plugin_lua, "require() call successful for plugin " .. id
|
||||
-- Require call
|
||||
local ok, plugin_lua = pcall(require, id .. "/" .. id)
|
||||
if not ok then
|
||||
if plugin_lua:match("not found") then
|
||||
return nil, "plugin " .. id .. " doesn't have LUA code"
|
||||
end
|
||||
return false, "require error for plugin " .. id .. " : " .. plugin_lua
|
||||
end
|
||||
-- New call
|
||||
if plugin_lua.new == nil then
|
||||
return false, "missing new() method for plugin " .. id
|
||||
end
|
||||
-- Return plugin
|
||||
return plugin_lua, "require() call successful for plugin " .. id
|
||||
end
|
||||
|
||||
helpers.new_plugin = function(plugin_lua, ctx)
|
||||
-- Require call
|
||||
local ok, plugin_obj = pcall(plugin_lua.new, plugin_lua, ctx)
|
||||
if not ok then
|
||||
return false, "new error for plugin " .. plugin_lua.name .. " : " .. plugin_obj
|
||||
end
|
||||
return true, plugin_obj
|
||||
helpers.new_plugin = function(plugin_lua, ctx)
|
||||
-- Require call
|
||||
local ok, plugin_obj = pcall(plugin_lua.new, plugin_lua, ctx)
|
||||
if not ok then
|
||||
return false, "new error for plugin " .. plugin_lua.name .. " : " .. plugin_obj
|
||||
end
|
||||
return true, plugin_obj
|
||||
end
|
||||
|
||||
helpers.call_plugin = function(plugin, method)
|
||||
-- Check if method is present
|
||||
if plugin[method] == nil then
|
||||
return nil, "missing " .. method .. "() method for plugin " .. plugin:get_id()
|
||||
end
|
||||
-- Call method
|
||||
local ok, ret = pcall(plugin[method], plugin)
|
||||
if not ok then
|
||||
return false, plugin:get_id() .. ":" .. method .. "() failed : " .. ret
|
||||
end
|
||||
if ret == nil then
|
||||
return false, plugin:get_id() .. ":" .. method .. "() returned nil value"
|
||||
end
|
||||
-- Check values
|
||||
local missing_values = {}
|
||||
local required_values = { "ret", "msg" }
|
||||
for i, value in ipairs(required_values) do
|
||||
if ret[value] == nil then
|
||||
table.insert(missing_values, value)
|
||||
end
|
||||
end
|
||||
if #missing_values > 0 then
|
||||
return false, "missing required return value(s) : " .. cjson.encode(missing_values)
|
||||
end
|
||||
-- Return
|
||||
return true, ret
|
||||
helpers.call_plugin = function(plugin, method)
|
||||
-- Check if method is present
|
||||
if plugin[method] == nil then
|
||||
return nil, "missing " .. method .. "() method for plugin " .. plugin:get_id()
|
||||
end
|
||||
-- Call method
|
||||
local ok, ret = pcall(plugin[method], plugin)
|
||||
if not ok then
|
||||
return false, plugin:get_id() .. ":" .. method .. "() failed : " .. ret
|
||||
end
|
||||
if ret == nil then
|
||||
return false, plugin:get_id() .. ":" .. method .. "() returned nil value"
|
||||
end
|
||||
-- Check values
|
||||
local missing_values = {}
|
||||
local required_values = { "ret", "msg" }
|
||||
for _, value in ipairs(required_values) do
|
||||
if ret[value] == nil then
|
||||
table.insert(missing_values, value)
|
||||
end
|
||||
end
|
||||
if #missing_values > 0 then
|
||||
return false, "missing required return value(s) : " .. cjson.encode(missing_values)
|
||||
end
|
||||
-- Return
|
||||
return true, ret
|
||||
end
|
||||
|
||||
helpers.fill_ctx = function()
|
||||
-- Return errors as table
|
||||
local errors = {}
|
||||
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"
|
||||
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)
|
||||
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")
|
||||
return true, "ctx filled", errors, ctx
|
||||
helpers.fill_ctx = function()
|
||||
-- Return errors as table
|
||||
local errors = {}
|
||||
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"
|
||||
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)
|
||||
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")
|
||||
return true, "ctx filled", errors, ctx
|
||||
end
|
||||
|
||||
function helpers.load_variables(all_variables, plugins)
|
||||
-- Extract settings from plugins and global ones
|
||||
local all_settings = {}
|
||||
for i, plugin in ipairs(plugins) do
|
||||
if plugin.settings then
|
||||
for setting, data in pairs(plugin.settings) do
|
||||
all_settings[setting] = data
|
||||
end
|
||||
end
|
||||
end
|
||||
local file = io.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"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid settings.json : " .. err
|
||||
end
|
||||
for setting, data in pairs(settings) do
|
||||
all_settings[setting] = data
|
||||
end
|
||||
-- Extract vars
|
||||
local variables = { ["global"] = {} }
|
||||
local multisite = all_variables["MULTISITE"] == "yes"
|
||||
local server_names = {}
|
||||
if multisite then
|
||||
for server_name in all_variables["SERVER_NAME"]:gmatch("%S+") do
|
||||
variables[server_name] = {}
|
||||
table.insert(server_names, server_name)
|
||||
end
|
||||
end
|
||||
for setting, data in pairs(all_settings) do
|
||||
if all_variables[setting] then
|
||||
variables["global"][setting] = all_variables[setting]
|
||||
end
|
||||
if data.multiple then
|
||||
for variable, value in pairs(all_variables) do
|
||||
local _, server_name, multiple_setting = variable:match("((%S*_?)(" .. setting .. "_%d+))")
|
||||
if multiple_setting then
|
||||
if multisite and server_name and server_name:match("%S+_$") then
|
||||
variables[server_name:sub(1, -2)][multiple_setting] = value
|
||||
else
|
||||
variables["global"][multiple_setting] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if multisite then
|
||||
for i, server_name in ipairs(server_names) do
|
||||
local key = server_name .. "_" .. setting
|
||||
if all_variables[key] then
|
||||
variables[server_name][setting] = all_variables[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true, variables
|
||||
-- Extract settings from plugins and global ones
|
||||
local all_settings = {}
|
||||
for _, plugin in ipairs(plugins) do
|
||||
if plugin.settings then
|
||||
for setting, data in pairs(plugin.settings) do
|
||||
all_settings[setting] = data
|
||||
end
|
||||
end
|
||||
end
|
||||
local file = io.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"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid settings.json : " .. settings
|
||||
end
|
||||
for setting, data in pairs(settings) do
|
||||
all_settings[setting] = data
|
||||
end
|
||||
-- Extract vars
|
||||
local variables = { ["global"] = {} }
|
||||
local multisite = all_variables["MULTISITE"] == "yes"
|
||||
local server_names = {}
|
||||
if multisite then
|
||||
for server_name in all_variables["SERVER_NAME"]:gmatch("%S+") do
|
||||
variables[server_name] = {}
|
||||
table.insert(server_names, server_name)
|
||||
end
|
||||
end
|
||||
for setting, data in pairs(all_settings) do
|
||||
if all_variables[setting] then
|
||||
variables["global"][setting] = all_variables[setting]
|
||||
end
|
||||
if data.multiple then
|
||||
for variable, value in pairs(all_variables) do
|
||||
local _, server_name, multiple_setting = variable:match("((%S*_?)(" .. setting .. "_%d+))")
|
||||
if multiple_setting then
|
||||
if multisite and server_name and server_name:match("%S+_$") then
|
||||
variables[server_name:sub(1, -2)][multiple_setting] = value
|
||||
else
|
||||
variables["global"][multiple_setting] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if multisite then
|
||||
for _, server_name in ipairs(server_names) do
|
||||
local key = server_name .. "_" .. setting
|
||||
if all_variables[key] then
|
||||
variables[server_name][setting] = all_variables[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true, variables
|
||||
end
|
||||
|
||||
return helpers
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
local class = require "middleclass"
|
||||
local errlog = require "ngx.errlog"
|
||||
local class = require "middleclass"
|
||||
local logger = class("logger")
|
||||
|
||||
function logger:initialize(prefix)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
local geoip = require "geoip.mmdb"
|
||||
|
||||
return {
|
||||
country_db = geoip.load_database("/var/cache/bunkerweb/country.mmdb"),
|
||||
asn_db = geoip.load_database("/var/cache/bunkerweb/asn.mmdb")
|
||||
country_db = geoip.load_database "/var/cache/bunkerweb/country.mmdb",
|
||||
asn_db = geoip.load_database "/var/cache/bunkerweb/asn.mmdb",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,87 +1,88 @@
|
|||
local class = require "middleclass"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local class = require "middleclass"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local plugin = class("plugin")
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local plugin = class("plugin")
|
||||
|
||||
function plugin:initialize(id, ctx)
|
||||
-- Store common, values
|
||||
self.id = id
|
||||
local multisite = false
|
||||
local current_phase = ngx.get_phase()
|
||||
for i, check_phase in ipairs({ "set", "access", "content", "header_filter", "log", "preread", "log_stream",
|
||||
"log_default" }) do
|
||||
if current_phase == check_phase then
|
||||
multisite = true
|
||||
break
|
||||
end
|
||||
end
|
||||
self.is_request = multisite
|
||||
-- Store common objects
|
||||
self.logger = logger:new(self.id)
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.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)
|
||||
else
|
||||
self.datastore = datastore:new()
|
||||
self.cachestore = cachestore:new(use_redis == "yes", true)
|
||||
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)
|
||||
return
|
||||
end
|
||||
-- Store variables
|
||||
self.variables = {}
|
||||
self.multiples = {}
|
||||
for k, v in pairs(metadata.settings) do
|
||||
local value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.ERR, "can't get " .. k .. " variable : " .. err)
|
||||
end
|
||||
self.variables[k] = value
|
||||
-- if v.multiple then
|
||||
-- local multiples, err = utils.get_multiple_variables(k)
|
||||
-- if not multiples then
|
||||
-- self.logger:log(ngx.ERR, "can't get " .. k .. " multiple variable : " .. err)
|
||||
-- self.multiples[k] = {}
|
||||
-- else
|
||||
-- self.multiples[k] = multiples
|
||||
-- end
|
||||
-- end
|
||||
end
|
||||
-- Is loading
|
||||
local is_loading, err = utils.get_variable("IS_LOADING", false)
|
||||
if is_loading == nil then
|
||||
self.logger:log(ngx.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
|
||||
self.kind = "stream"
|
||||
end
|
||||
-- Store common, values
|
||||
self.id = id
|
||||
local multisite = false
|
||||
local current_phase = ngx.get_phase()
|
||||
for _, check_phase in ipairs {
|
||||
"set",
|
||||
"access",
|
||||
"content",
|
||||
"header_filter",
|
||||
"log",
|
||||
"preread",
|
||||
"log_stream",
|
||||
"log_default",
|
||||
} do
|
||||
if current_phase == check_phase then
|
||||
multisite = true
|
||||
break
|
||||
end
|
||||
end
|
||||
self.is_request = multisite
|
||||
-- Store common objects
|
||||
self.logger = logger:new(self.id)
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.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)
|
||||
else
|
||||
self.datastore = datastore:new()
|
||||
self.cachestore = cachestore:new(use_redis == "yes", true)
|
||||
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)
|
||||
return
|
||||
end
|
||||
-- Store variables
|
||||
self.variables = {}
|
||||
self.multiples = {}
|
||||
local value
|
||||
for k, v in pairs(metadata.settings) do
|
||||
value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.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)
|
||||
if is_loading == nil then
|
||||
self.logger:log(ngx.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
|
||||
self.kind = "stream"
|
||||
end
|
||||
end
|
||||
|
||||
function plugin:get_id()
|
||||
return self.id
|
||||
return self.id
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function plugin:ret(ret, msg, status, redirect)
|
||||
return { ret = ret, msg = msg, status = status, redirect = redirect }
|
||||
return { ret = ret, msg = msg, status = status, redirect = redirect }
|
||||
end
|
||||
|
||||
return plugin
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
local cdatastore = require "bunkerweb.datastore"
|
||||
local mmdb = require "bunkerweb.mmdb"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local mmdb = require "bunkerweb.mmdb"
|
||||
|
||||
local ipmatcher = require "resty.ipmatcher"
|
||||
local resolver = require "resty.dns.resolver"
|
||||
local session = require "resty.session"
|
||||
local cjson = require "cjson"
|
||||
local cjson = require "cjson"
|
||||
local ipmatcher = require "resty.ipmatcher"
|
||||
local resolver = require "resty.dns.resolver"
|
||||
local session = require "resty.session"
|
||||
|
||||
local logger = clogger:new("UTILS")
|
||||
local datastore = cdatastore:new()
|
||||
local logger = clogger:new("UTILS")
|
||||
local datastore = cdatastore:new()
|
||||
|
||||
local utils = {}
|
||||
local utils = {}
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
utils.get_variable = function(var, site_search)
|
||||
utils.get_variable = function(var, site_search)
|
||||
-- Default site search to true
|
||||
if site_search == nil then
|
||||
site_search = true
|
||||
end
|
||||
-- Get global value
|
||||
local variables, err = datastore:get('variables', true)
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return nil, "can't access variables from datastore : " .. err
|
||||
end
|
||||
|
|
@ -33,9 +33,9 @@ utils.get_variable = function(var, site_search)
|
|||
return value, "success"
|
||||
end
|
||||
|
||||
utils.has_variable = function(var, value)
|
||||
utils.has_variable = function(var, value)
|
||||
-- Get global variable
|
||||
local variables, err = datastore:get('variables', true)
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||
end
|
||||
|
|
@ -56,9 +56,9 @@ utils.has_variable = function(var, value)
|
|||
return variables["global"][var] == value, "success"
|
||||
end
|
||||
|
||||
utils.has_not_variable = function(var, value)
|
||||
utils.has_not_variable = function(var, value)
|
||||
-- Get global variable
|
||||
local variables, err = datastore:get('variables', true)
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||
end
|
||||
|
|
@ -80,9 +80,9 @@ utils.has_not_variable = function(var, value)
|
|||
end
|
||||
|
||||
utils.get_multiple_variables = function(vars)
|
||||
local variables, err = datastore:get('variables', true)
|
||||
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 " .. vars .. " from datastore : " .. err
|
||||
end
|
||||
local result = {}
|
||||
-- Loop on scoped vars
|
||||
|
|
@ -90,7 +90,7 @@ utils.get_multiple_variables = function(vars)
|
|||
result[scope] = {}
|
||||
-- Loop on vars
|
||||
for variable, value in pairs(scoped_vars) do
|
||||
for i, var in ipairs(vars) do
|
||||
for _, var in ipairs(vars) do
|
||||
if variable:find("^" .. var .. "_?[0-9]*$") then
|
||||
result[scope][variable] = value
|
||||
end
|
||||
|
|
@ -100,7 +100,7 @@ utils.get_multiple_variables = function(vars)
|
|||
return result
|
||||
end
|
||||
|
||||
utils.is_ip_in_networks = function(ip, networks)
|
||||
utils.is_ip_in_networks = function(ip, networks)
|
||||
-- Instantiate ipmatcher
|
||||
local ipm, err = ipmatcher.new(networks)
|
||||
if not ipm then
|
||||
|
|
@ -114,15 +114,15 @@ utils.is_ip_in_networks = function(ip, networks)
|
|||
return matched
|
||||
end
|
||||
|
||||
utils.is_ipv4 = function(ip)
|
||||
utils.is_ipv4 = function(ip)
|
||||
return ipmatcher.parse_ipv4(ip)
|
||||
end
|
||||
|
||||
utils.is_ipv6 = function(ip)
|
||||
utils.is_ipv6 = function(ip)
|
||||
return ipmatcher.parse_ipv6(ip)
|
||||
end
|
||||
|
||||
utils.ip_is_global = function(ip)
|
||||
utils.ip_is_global = function(ip)
|
||||
-- Reserved, non public IPs
|
||||
local reserved_ips = {
|
||||
"0.0.0.0/8",
|
||||
|
|
@ -154,7 +154,7 @@ utils.ip_is_global = function(ip)
|
|||
"2002::/16",
|
||||
"fc00::/7",
|
||||
"fe80::/10",
|
||||
"ff00::/8"
|
||||
"ff00::/8",
|
||||
}
|
||||
-- Instantiate ipmatcher
|
||||
local ipm, err = ipmatcher.new(reserved_ips)
|
||||
|
|
@ -169,9 +169,9 @@ utils.ip_is_global = function(ip)
|
|||
return not matched, "success"
|
||||
end
|
||||
|
||||
utils.get_integration = function()
|
||||
utils.get_integration = function()
|
||||
-- Check if already in datastore
|
||||
local integration, err = datastore:get("misc_integration", true)
|
||||
local integration, _ = datastore:get("misc_integration", true)
|
||||
if integration then
|
||||
return integration
|
||||
end
|
||||
|
|
@ -193,12 +193,12 @@ utils.get_integration = function()
|
|||
integration = "autoconf"
|
||||
else
|
||||
-- Already present (e.g. : linux)
|
||||
local f, err = io.open("/usr/share/bunkerweb/INTEGRATION", "r")
|
||||
local f, _ = io.open("/usr/share/bunkerweb/INTEGRATION", "r")
|
||||
if f then
|
||||
integration = f:read("*a"):gsub("[\n\r]", "")
|
||||
f:close()
|
||||
else
|
||||
local f, err = io.open("/etc/os-release", "r")
|
||||
f, _ = io.open("/etc/os-release", "r")
|
||||
if f then
|
||||
local data = f:read("*a")
|
||||
f:close()
|
||||
|
|
@ -222,9 +222,9 @@ utils.get_integration = function()
|
|||
return integration
|
||||
end
|
||||
|
||||
utils.get_version = function()
|
||||
utils.get_version = function()
|
||||
-- Check if already in datastore
|
||||
local version, err = datastore:get("misc_version", true)
|
||||
local version, _ = datastore:get("misc_version", true)
|
||||
if version then
|
||||
return version
|
||||
end
|
||||
|
|
@ -244,7 +244,7 @@ utils.get_version = function()
|
|||
return version
|
||||
end
|
||||
|
||||
utils.get_reason = function(ctx)
|
||||
utils.get_reason = function(ctx)
|
||||
-- ngx.ctx
|
||||
if ctx.bw.reason then
|
||||
return ctx.bw.reason
|
||||
|
|
@ -258,7 +258,7 @@ utils.get_reason = function(ctx)
|
|||
return "modsecurity"
|
||||
end
|
||||
-- datastore ban
|
||||
local banned, err = datastore:get("bans_ip_" .. ngx.var.remote_addr)
|
||||
local banned, _ = datastore:get("bans_ip_" .. ngx.var.remote_addr)
|
||||
if banned then
|
||||
return banned
|
||||
end
|
||||
|
|
@ -269,9 +269,9 @@ utils.get_reason = function(ctx)
|
|||
return nil
|
||||
end
|
||||
|
||||
utils.get_resolvers = function()
|
||||
utils.get_resolvers = function()
|
||||
-- Get resolvers from datastore if existing
|
||||
local resolvers, err = datastore:get("misc_resolvers", true)
|
||||
local resolvers, _ = datastore:get("misc_resolvers", true)
|
||||
if resolvers then
|
||||
return resolvers
|
||||
end
|
||||
|
|
@ -282,7 +282,7 @@ utils.get_resolvers = function()
|
|||
return "unknown"
|
||||
end
|
||||
-- Make table for resolver1 resolver2 ... string
|
||||
local resolvers = {}
|
||||
resolvers = {}
|
||||
for str_resolver in variables["global"]["DNS_RESOLVERS"]:gmatch("%S+") do
|
||||
table.insert(resolvers, str_resolver)
|
||||
end
|
||||
|
|
@ -294,7 +294,7 @@ utils.get_resolvers = function()
|
|||
return resolvers
|
||||
end
|
||||
|
||||
utils.get_rdns = function(ip)
|
||||
utils.get_rdns = function(ip)
|
||||
-- Check cache
|
||||
local cachestore = utils.new_cachestore()
|
||||
local ok, value = cachestore:get("rdns_" .. ip)
|
||||
|
|
@ -312,7 +312,7 @@ utils.get_rdns = function(ip)
|
|||
local rdns, err = resolver:new {
|
||||
nameservers = resolvers,
|
||||
retrans = 1,
|
||||
timeout = 1000
|
||||
timeout = 1000,
|
||||
}
|
||||
if not rdns then
|
||||
return false, err
|
||||
|
|
@ -330,21 +330,21 @@ utils.get_rdns = function(ip)
|
|||
ret_err = answers.errstr
|
||||
end
|
||||
-- Extract all PTR
|
||||
for i, answer in ipairs(answers) do
|
||||
for _, answer in ipairs(answers) do
|
||||
if answer.ptrdname then
|
||||
table.insert(ptrs, answer.ptrdname)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Save to cache
|
||||
local ok, err = cachestore:set("rdns_" .. ip, cjson.encode(ptrs), 3600)
|
||||
ok, err = cachestore:set("rdns_" .. ip, cjson.encode(ptrs), 3600)
|
||||
if not ok then
|
||||
logger:log(ngx.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)
|
||||
-- Check cache
|
||||
local cachestore = utils.new_cachestore()
|
||||
local ok, value = cachestore:get("dns_" .. fqdn)
|
||||
|
|
@ -366,7 +366,7 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
local res, err = resolver:new {
|
||||
nameservers = resolvers,
|
||||
retrans = 1,
|
||||
timeout = 1000
|
||||
timeout = 1000,
|
||||
}
|
||||
if not res then
|
||||
return false, err
|
||||
|
|
@ -374,6 +374,7 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
-- Get query types : AAAA and A if using IPv6 / only A if not using IPv6
|
||||
local qtypes = {}
|
||||
if ipv6 then
|
||||
-- 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)
|
||||
|
|
@ -386,9 +387,10 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
local res_answers = {}
|
||||
local res_errors = {}
|
||||
local ans_errors = {}
|
||||
for i, qtype in ipairs(qtypes) do
|
||||
local answers
|
||||
for _, qtype in ipairs(qtypes) do
|
||||
-- Query FQDN
|
||||
local answers, err = res:query(fqdn, { qtype = qtype }, {})
|
||||
answers, err = res:query(fqdn, { qtype = qtype }, {})
|
||||
local qtype_str = qtype == res.TYPE_AAAA and "AAAA" or "A"
|
||||
if not answers then
|
||||
res_errors[qtype_str] = err
|
||||
|
|
@ -403,22 +405,23 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
end
|
||||
-- Extract all IPs
|
||||
local ips = {}
|
||||
for i, answers in ipairs(res_answers) do
|
||||
for j, answer in ipairs(answers) do
|
||||
-- luacheck: ignore 421
|
||||
for _, answers in ipairs(res_answers) do
|
||||
for _, answer in ipairs(answers) do
|
||||
if answer.address then
|
||||
table.insert(ips, answer.address)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Save to cache
|
||||
local ok, err = cachestore:set("dns_" .. fqdn, cjson.encode(ips), 3600)
|
||||
ok, err = cachestore:set("dns_" .. fqdn, cjson.encode(ips), 3600)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't set dns into cachestore : " .. err)
|
||||
end
|
||||
return ips, cjson.encode(res_errors) .. " " .. cjson.encode(ans_errors)
|
||||
end
|
||||
|
||||
utils.get_country = function(ip)
|
||||
utils.get_country = function(ip)
|
||||
-- Check if mmdb is loaded
|
||||
if not mmdb.country_db then
|
||||
return false, "mmdb country not loaded"
|
||||
|
|
@ -434,7 +437,7 @@ utils.get_country = function(ip)
|
|||
return result.country.iso_code, "success"
|
||||
end
|
||||
|
||||
utils.get_asn = function(ip)
|
||||
utils.get_asn = function(ip)
|
||||
-- Check if mmdp is loaded
|
||||
if not mmdb.asn_db then
|
||||
return false, "mmdb asn not loaded"
|
||||
|
|
@ -450,22 +453,28 @@ utils.get_asn = function(ip)
|
|||
return result.autonomous_system_number, "success"
|
||||
end
|
||||
|
||||
utils.rand = function(nb, no_numbers)
|
||||
utils.rand = function(nb, no_numbers)
|
||||
local charset = {}
|
||||
-- lowers, uppers and numbers
|
||||
if not no_numbers then
|
||||
for i = 48, 57 do table.insert(charset, string.char(i)) end
|
||||
for i = 48, 57 do
|
||||
table.insert(charset, string.char(i))
|
||||
end
|
||||
end
|
||||
for i = 65, 90 do
|
||||
table.insert(charset, string.char(i))
|
||||
end
|
||||
for i = 97, 122 do
|
||||
table.insert(charset, string.char(i))
|
||||
end
|
||||
for i = 65, 90 do table.insert(charset, string.char(i)) end
|
||||
for i = 97, 122 do table.insert(charset, string.char(i)) end
|
||||
local result = ""
|
||||
for i = 1, nb do
|
||||
for _ = 1, nb do
|
||||
result = result .. charset[math.random(1, #charset)]
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
utils.get_deny_status = function(ctx)
|
||||
utils.get_deny_status = function(ctx)
|
||||
-- Stream case
|
||||
if ctx.bw and ctx.bw.kind == "stream" then
|
||||
return 444
|
||||
|
|
@ -479,10 +488,10 @@ utils.get_deny_status = function(ctx)
|
|||
return tonumber(variables["global"]["DENY_HTTP_STATUS"])
|
||||
end
|
||||
|
||||
utils.check_session = function(ctx)
|
||||
local _session, err, exists, refreshed = session.start({ audience = "metadata" })
|
||||
utils.check_session = function(ctx)
|
||||
local _session, _, exists, _ = session.start({ audience = "metadata" })
|
||||
if exists then
|
||||
for i, check in ipairs(ctx.bw.sessions_checks) do
|
||||
for _, check in ipairs(ctx.bw.sessions_checks) do
|
||||
local key = check[1]
|
||||
local value = check[2]
|
||||
if _session:get(key) ~= value then
|
||||
|
|
@ -496,7 +505,7 @@ utils.check_session = function(ctx)
|
|||
end
|
||||
end
|
||||
else
|
||||
for i, check in ipairs(ctx.bw.sessions_checks) do
|
||||
for _, check in ipairs(ctx.bw.sessions_checks) do
|
||||
_session:set(check[1], check[2])
|
||||
end
|
||||
local ok, err = _session:save()
|
||||
|
|
@ -509,7 +518,7 @@ utils.check_session = function(ctx)
|
|||
return true, exists
|
||||
end
|
||||
|
||||
utils.get_session = function(audience, ctx)
|
||||
utils.get_session = function(audience, ctx)
|
||||
-- Check session
|
||||
if not ctx.bw.sessions_is_checked then
|
||||
local ok, err = utils.check_session(ctx)
|
||||
|
|
@ -518,14 +527,15 @@ utils.get_session = function(audience, ctx)
|
|||
end
|
||||
end
|
||||
-- Open session with specific audience
|
||||
local _session, err, exists = session.open({ audience = audience })
|
||||
local _session, err, _ = session.open({ audience = audience })
|
||||
if err then
|
||||
logger:log(ngx.INFO, "session:open() error : " .. err)
|
||||
end
|
||||
return _session
|
||||
end
|
||||
|
||||
utils.get_session_data = function(_session, site, ctx)
|
||||
-- luacheck: ignore 214
|
||||
utils.get_session_data = function(_session, site, ctx)
|
||||
local site_only = site == nil or site
|
||||
local data = _session:get_data()
|
||||
if site_only then
|
||||
|
|
@ -534,7 +544,8 @@ utils.get_session_data = function(_session, site, ctx)
|
|||
return data
|
||||
end
|
||||
|
||||
utils.set_session_data = function(_session, data, site, ctx)
|
||||
-- luacheck: ignore 214
|
||||
utils.set_session_data = function(_session, data, site, ctx)
|
||||
local site_only = site == nil or site
|
||||
if site_only then
|
||||
local all_data = _session:get_data()
|
||||
|
|
@ -546,7 +557,7 @@ utils.set_session_data = function(_session, data, site, ctx)
|
|||
return _session:save()
|
||||
end
|
||||
|
||||
utils.is_banned = function(ip)
|
||||
utils.is_banned = function(ip)
|
||||
-- Check on local datastore
|
||||
local reason, err = datastore:get("bans_ip_" .. ip)
|
||||
if not reason and err ~= "not found" then
|
||||
|
|
@ -599,7 +610,7 @@ utils.is_banned = function(ip)
|
|||
elseif data[1] ~= ngx.null then
|
||||
clusterstore:close()
|
||||
-- Update local cache
|
||||
local ok, err = datastore:set("bans_ip_" .. ip, data[1], data[2])
|
||||
ok, err = datastore:set("bans_ip_" .. ip, data[1], data[2])
|
||||
if not ok then
|
||||
return nil, "datastore:set() error : " .. err
|
||||
end
|
||||
|
|
@ -609,7 +620,7 @@ utils.is_banned = function(ip)
|
|||
return false, "not banned"
|
||||
end
|
||||
|
||||
utils.add_ban = function(ip, reason, ttl)
|
||||
utils.add_ban = function(ip, reason, ttl)
|
||||
-- Set on local datastore
|
||||
local ok, err = datastore:set("bans_ip_" .. ip, reason, ttl)
|
||||
if not ok then
|
||||
|
|
@ -624,12 +635,12 @@ utils.add_ban = function(ip, reason, ttl)
|
|||
end
|
||||
-- Connect
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local ok, err = clusterstore:connect()
|
||||
ok, err = clusterstore:connect()
|
||||
if not ok then
|
||||
return false, "can't connect to redis server : " .. err
|
||||
end
|
||||
-- SET call
|
||||
local ok, err = clusterstore:call("set", "bans_ip_" .. ip, reason, "EX", ttl)
|
||||
ok, err = clusterstore:call("set", "bans_ip_" .. ip, reason, "EX", ttl)
|
||||
if not ok then
|
||||
clusterstore:close()
|
||||
return false, "redis SET failed : " .. err
|
||||
|
|
@ -638,7 +649,7 @@ utils.add_ban = function(ip, reason, ttl)
|
|||
return true, "success"
|
||||
end
|
||||
|
||||
utils.new_cachestore = function()
|
||||
utils.new_cachestore = function()
|
||||
-- Check if redis is used
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
|
|
@ -650,7 +661,7 @@ utils.new_cachestore = function()
|
|||
return require "bunkerweb.cachestore":new(use_redis, true)
|
||||
end
|
||||
|
||||
utils.regex_match = function(str, regex, options)
|
||||
utils.regex_match = function(str, regex, options)
|
||||
local all_options = "o"
|
||||
if options then
|
||||
all_options = all_options .. options
|
||||
|
|
@ -663,7 +674,7 @@ utils.regex_match = function(str, regex, options)
|
|||
return match
|
||||
end
|
||||
|
||||
utils.get_phases = function()
|
||||
utils.get_phases = function()
|
||||
return {
|
||||
"init",
|
||||
"init_worker",
|
||||
|
|
@ -673,18 +684,18 @@ utils.get_phases = function()
|
|||
"log",
|
||||
"preread",
|
||||
"log_stream",
|
||||
"log_default"
|
||||
"log_default",
|
||||
}
|
||||
end
|
||||
|
||||
utils.is_cosocket_available = function()
|
||||
utils.is_cosocket_available = function()
|
||||
local phases = {
|
||||
"timer",
|
||||
"access",
|
||||
"preread"
|
||||
"preread",
|
||||
}
|
||||
local current_phase = ngx.get_phase()
|
||||
for i, phase in ipairs(phases) do
|
||||
for _, phase in ipairs(phases) do
|
||||
if current_phase == phase then
|
||||
return true
|
||||
end
|
||||
|
|
@ -692,8 +703,8 @@ utils.is_cosocket_available = function()
|
|||
return false
|
||||
end
|
||||
|
||||
utils.kill_all_threads = function(threads)
|
||||
for i, thread in ipairs(threads) do
|
||||
utils.kill_all_threads = function(threads)
|
||||
for _, thread in ipairs(threads) do
|
||||
local ok, err = ngx.thread.kill(thread)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while killing thread : " .. err)
|
||||
|
|
@ -701,7 +712,7 @@ utils.kill_all_threads = function(threads)
|
|||
end
|
||||
end
|
||||
|
||||
utils.get_ctx_obj = function(obj)
|
||||
utils.get_ctx_obj = function(obj)
|
||||
if ngx.ctx and ngx.ctx.bw then
|
||||
return ngx.ctx.bw[obj]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local captcha = require "antibot.captcha"
|
||||
local base64 = require "base64"
|
||||
local sha256 = require "resty.sha256"
|
||||
local str = require "resty.string"
|
||||
local http = require "resty.http"
|
||||
local base64 = require "base64"
|
||||
local captcha = require "antibot.captcha"
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local http = require "resty.http"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local sha256 = require "resty.sha256"
|
||||
local str = require "resty.string"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local template = nil
|
||||
if ngx.shared.datastore then
|
||||
template = require "resty.template"
|
||||
|
|
@ -51,40 +51,41 @@ function antibot:header()
|
|||
return self:ret(true, "Not antibot uri")
|
||||
end
|
||||
|
||||
|
||||
local header = "Content-Security-Policy"
|
||||
if self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes" then
|
||||
header = header .. "-Report-Only"
|
||||
end
|
||||
|
||||
if self.session_data.type == "recaptcha" then
|
||||
ngx.header[header] =
|
||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
||||
self.session_data.nonce_script ..
|
||||
"' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'unsafe-inline' http: https:; img-src https://www.gstatic.com/recaptcha/ 'self' data:; frame-src https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/; style-src 'self' 'nonce-" ..
|
||||
self.session_data.nonce_style ..
|
||||
"'; font-src 'self' https://fonts.gstatic.com data:; base-uri 'self';"
|
||||
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||
.. self.session_data.nonce_script
|
||||
.. "' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'unsafe-inline' http: https:;"
|
||||
.. " img-src https://www.gstatic.com/recaptcha/ 'self' data:; "
|
||||
.. " frame-src https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/;"
|
||||
.. " style-src 'self' 'nonce-"
|
||||
.. self.session_data.nonce_style
|
||||
.. "'; font-src 'self' https://fonts.gstatic.com data:; base-uri 'self';"
|
||||
elseif self.session_data.type == "hcaptcha" then
|
||||
ngx.header[header] =
|
||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
||||
self.session_data.nonce_script ..
|
||||
"' https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' http: https:; img-src 'self' data:; frame-src https://hcaptcha.com https://*.hcaptcha.com; style-src 'self' 'nonce-" ..
|
||||
self.session_data.nonce_style ..
|
||||
"' https://hcaptcha.com https://*.hcaptcha.com; connect-src https://hcaptcha.com https://*.hcaptcha.com; font-src 'self' data:; base-uri 'self';"
|
||||
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||
.. self.session_data.nonce_script
|
||||
.. "' https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' http: https:; img-src 'self' data:;"
|
||||
.. " frame-src https://hcaptcha.com https://*.hcaptcha.com; style-src 'self' 'nonce-"
|
||||
.. self.session_data.nonce_style
|
||||
.. "' https://hcaptcha.com https://*.hcaptcha.com; connect-src https://hcaptcha.com https://*.hcaptcha.com; "
|
||||
.. " font-src 'self' data:; base-uri 'self';"
|
||||
elseif self.session_data.type == "turnstile" then
|
||||
ngx.header[header] =
|
||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
||||
self.session_data.nonce_script ..
|
||||
"' https://challenges.cloudflare.com 'unsafe-inline' http: https:; img-src 'self' data:; frame-src https://challenges.cloudflare.com; style-src 'self' 'nonce-" ..
|
||||
self.session_data.nonce_style ..
|
||||
"'; font-src 'self' data:; base-uri 'self';"
|
||||
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||
.. self.session_data.nonce_script
|
||||
.. "' https://challenges.cloudflare.com 'unsafe-inline' http: https:; img-src 'self' data:;"
|
||||
.. " frame-src https://challenges.cloudflare.com; style-src 'self' 'nonce-"
|
||||
.. self.session_data.nonce_style
|
||||
.. "'; font-src 'self' data:; base-uri 'self';"
|
||||
else
|
||||
ngx.header[header] =
|
||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
||||
self.session_data.nonce_script ..
|
||||
"' 'unsafe-inline' http: https:; img-src 'self' data:; style-src 'self' 'nonce-" ..
|
||||
self.session_data.nonce_style ..
|
||||
"'; font-src 'self' data:; base-uri 'self';"
|
||||
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||
.. self.session_data.nonce_script
|
||||
.. "' 'unsafe-inline' http: https:; img-src 'self' data:; style-src 'self' 'nonce-"
|
||||
.. self.session_data.nonce_style
|
||||
.. "'; font-src 'self' data:; base-uri 'self';"
|
||||
end
|
||||
return self:ret(true, "Successfully overridden CSP header")
|
||||
end
|
||||
|
|
@ -138,6 +139,7 @@ function antibot:access()
|
|||
|
||||
-- Check challenge
|
||||
if self.ctx.bw.request_method == "POST" then
|
||||
-- luacheck: ignore 421
|
||||
local ok, err, redirect = self:check_challenge()
|
||||
local set_ok, set_err = self:set_session_data()
|
||||
if not set_ok then
|
||||
|
|
@ -152,7 +154,7 @@ function antibot:access()
|
|||
return self:ret(true, "check challenge redirect : " .. redirect, nil, redirect)
|
||||
end
|
||||
self:prepare_challenge()
|
||||
local ok, err = self:set_session_data()
|
||||
ok, err = self:set_session_data()
|
||||
if not ok then
|
||||
return self:ret(false, "can't save session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
|
|
@ -215,7 +217,9 @@ function antibot:check_session()
|
|||
return
|
||||
end
|
||||
-- Check if new prepare is needed
|
||||
if not resolved and (time_resolve > time or time - time_resolve > tonumber(self.variables["ANTIBOT_TIME_RESOLVE"])) then
|
||||
if
|
||||
not resolved and (time_resolve > time or time - time_resolve > tonumber(self.variables["ANTIBOT_TIME_RESOLVE"]))
|
||||
then
|
||||
self.session_data = {}
|
||||
self.session_updated = true
|
||||
return
|
||||
|
|
@ -312,7 +316,7 @@ function antibot:check_challenge()
|
|||
return nil, "challenge not prepared"
|
||||
end
|
||||
|
||||
local resolved = false
|
||||
local resolved
|
||||
|
||||
self.session_data.prepared = false
|
||||
self.session_updated = true
|
||||
|
|
@ -364,12 +368,15 @@ function antibot:check_challenge()
|
|||
end
|
||||
local res, err = httpc:request_uri("https://www.google.com/recaptcha/api/siteverify", {
|
||||
method = "POST",
|
||||
body = "secret=" ..
|
||||
self.variables["ANTIBOT_RECAPTCHA_SECRET"] ..
|
||||
"&response=" .. args["token"] .. "&remoteip=" .. self.ctx.bw.remote_addr,
|
||||
body = "secret="
|
||||
.. self.variables["ANTIBOT_RECAPTCHA_SECRET"]
|
||||
.. "&response="
|
||||
.. args["token"]
|
||||
.. "&remoteip="
|
||||
.. self.ctx.bw.remote_addr,
|
||||
headers = {
|
||||
["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
}
|
||||
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||
},
|
||||
})
|
||||
httpc:close()
|
||||
if not res then
|
||||
|
|
@ -400,12 +407,15 @@ function antibot:check_challenge()
|
|||
end
|
||||
local res, err = httpc:request_uri("https://hcaptcha.com/siteverify", {
|
||||
method = "POST",
|
||||
body = "secret=" ..
|
||||
self.variables["ANTIBOT_HCAPTCHA_SECRET"] ..
|
||||
"&response=" .. args["token"] .. "&remoteip=" .. self.ctx.bw.remote_addr,
|
||||
body = "secret="
|
||||
.. self.variables["ANTIBOT_HCAPTCHA_SECRET"]
|
||||
.. "&response="
|
||||
.. args["token"]
|
||||
.. "&remoteip="
|
||||
.. self.ctx.bw.remote_addr,
|
||||
headers = {
|
||||
["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
}
|
||||
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||
},
|
||||
})
|
||||
httpc:close()
|
||||
if not res then
|
||||
|
|
@ -413,7 +423,7 @@ function antibot:check_challenge()
|
|||
end
|
||||
local ok, hdata = pcall(cjson.decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from hCaptcha API : " .. data, nil
|
||||
return nil, "error while decoding JSON from hCaptcha API : " .. hdata, nil
|
||||
end
|
||||
if not hdata.success then
|
||||
return false, "client failed challenge", nil
|
||||
|
|
@ -436,12 +446,15 @@ function antibot:check_challenge()
|
|||
end
|
||||
local res, err = httpc:request_uri("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
||||
method = "POST",
|
||||
body = "secret=" ..
|
||||
self.variables["ANTIBOT_TURNSTILE_SECRET"] ..
|
||||
"&response=" .. args["token"] .. "&remoteip=" .. self.ctx.bw.remote_addr,
|
||||
body = "secret="
|
||||
.. self.variables["ANTIBOT_TURNSTILE_SECRET"]
|
||||
.. "&response="
|
||||
.. args["token"]
|
||||
.. "&remoteip="
|
||||
.. self.ctx.bw.remote_addr,
|
||||
headers = {
|
||||
["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
}
|
||||
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||
},
|
||||
})
|
||||
httpc:close()
|
||||
if not res then
|
||||
|
|
@ -449,7 +462,7 @@ function antibot:check_challenge()
|
|||
end
|
||||
local ok, tdata = pcall(cjson.decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from Turnstile API : " .. data, nil
|
||||
return nil, "error while decoding JSON from Turnstile API : " .. tdata, nil
|
||||
end
|
||||
if not tdata.success then
|
||||
return false, "client failed challenge", nil
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local badbehavior = class("badbehavior", plugin)
|
||||
|
||||
|
|
@ -23,14 +23,20 @@ function badbehavior:log()
|
|||
return self:ret(true, "not increasing counter")
|
||||
end
|
||||
-- Check if we are already banned
|
||||
local banned, err = self.datastore:get("bans_ip_" .. self.ctx.bw.remote_addr)
|
||||
local banned, _ = self.datastore:get("bans_ip_" .. self.ctx.bw.remote_addr)
|
||||
if banned then
|
||||
return self:ret(true, "already banned")
|
||||
end
|
||||
-- Call increase function later and with cosocket enabled
|
||||
local ok, err = ngx.timer.at(0, badbehavior.increase, self.ctx.bw.remote_addr,
|
||||
tonumber(self.variables["BAD_BEHAVIOR_COUNT_TIME"]), tonumber(self.variables["BAD_BEHAVIOR_BAN_TIME"]),
|
||||
tonumber(self.variables["BAD_BEHAVIOR_THRESHOLD"]), self.use_redis)
|
||||
local ok, err = ngx.timer.at(
|
||||
0,
|
||||
badbehavior.increase,
|
||||
self.ctx.bw.remote_addr,
|
||||
tonumber(self.variables["BAD_BEHAVIOR_COUNT_TIME"]),
|
||||
tonumber(self.variables["BAD_BEHAVIOR_BAN_TIME"]),
|
||||
tonumber(self.variables["BAD_BEHAVIOR_THRESHOLD"]),
|
||||
self.use_redis
|
||||
)
|
||||
if not ok then
|
||||
return self:ret(false, "can't create increase timer : " .. err)
|
||||
end
|
||||
|
|
@ -45,6 +51,7 @@ function badbehavior:log_stream()
|
|||
return self:log()
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function badbehavior.increase(premature, ip, count_time, ban_time, threshold, use_redis)
|
||||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("badbehavior")
|
||||
|
|
@ -84,16 +91,28 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
end
|
||||
-- Store local ban
|
||||
if counter > threshold then
|
||||
local ok, err = utils.add_ban(ip, "bad behavior", ban_time)
|
||||
ok, err = utils.add_ban(ip, "bad behavior", ban_time)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(increase) can't save ban : " .. err)
|
||||
return
|
||||
end
|
||||
logger:log(ngx.WARN,
|
||||
"IP " .. ip .. " is banned for " .. ban_time .. "s (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
|
||||
logger:log(
|
||||
ngx.WARN,
|
||||
"IP "
|
||||
.. ip
|
||||
.. " is banned for "
|
||||
.. ban_time
|
||||
.. "s ("
|
||||
.. tostring(counter)
|
||||
.. "/"
|
||||
.. tostring(threshold)
|
||||
.. ")"
|
||||
)
|
||||
end
|
||||
logger:log(ngx.NOTICE,
|
||||
"increased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
|
||||
logger:log(
|
||||
ngx.NOTICE,
|
||||
"increased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||
)
|
||||
end
|
||||
|
||||
function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
||||
|
|
@ -126,7 +145,7 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
|||
-- Store local counter
|
||||
if counter <= 0 then
|
||||
counter = 0
|
||||
local ok, err = datastore:delete("plugin_badbehavior_count_" .. ip)
|
||||
datastore:delete("plugin_badbehavior_count_" .. ip)
|
||||
else
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter, count_time)
|
||||
if not ok then
|
||||
|
|
@ -134,8 +153,10 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
|||
return
|
||||
end
|
||||
end
|
||||
logger:log(ngx.NOTICE,
|
||||
"decreased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
|
||||
logger:log(
|
||||
ngx.NOTICE,
|
||||
"decreased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||
)
|
||||
end
|
||||
|
||||
function badbehavior.redis_increase(ip, count_time, ban_time)
|
||||
|
|
@ -168,9 +189,8 @@ function badbehavior.redis_increase(ip, count_time, ban_time)
|
|||
return false, err
|
||||
end
|
||||
-- Execute LUA script
|
||||
local counter, err = clusterstore:call("eval", redis_script, 2, "plugin_bad_behavior_" .. ip, "bans_ip" .. ip,
|
||||
count_time,
|
||||
ban_time)
|
||||
local counter, err =
|
||||
clusterstore:call("eval", redis_script, 2, "plugin_bad_behavior_" .. ip, "bans_ip" .. ip, count_time, ban_time)
|
||||
if not counter then
|
||||
clusterstore:close()
|
||||
return false, err
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
local ipmatcher = require "resty.ipmatcher"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local blacklist = class("blacklist", plugin)
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ function blacklist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(blacklists) do
|
||||
local f, err = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
||||
local f, _ = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(blacklists[kind], line)
|
||||
|
|
@ -102,7 +102,7 @@ function blacklist:access()
|
|||
end
|
||||
-- Check the caches
|
||||
local checks = {
|
||||
["IP"] = "ip" .. self.ctx.bw.remote_addr
|
||||
["IP"] = "ip" .. self.ctx.bw.remote_addr,
|
||||
}
|
||||
if self.ctx.bw.http_user_agent then
|
||||
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
||||
|
|
@ -113,14 +113,18 @@ function blacklist:access()
|
|||
local already_cached = {
|
||||
["IP"] = false,
|
||||
["URI"] = false,
|
||||
["UA"] = false
|
||||
["UA"] = false,
|
||||
}
|
||||
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)
|
||||
elseif cached and cached ~= "ok" then
|
||||
return self:ret(true, k .. " is in cached blacklist (info : " .. cached .. ")", utils.get_deny_status(self.ctx))
|
||||
return self:ret(
|
||||
true,
|
||||
k .. " is in cached blacklist (info : " .. cached .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
)
|
||||
end
|
||||
if ok and cached then
|
||||
already_cached[k] = true
|
||||
|
|
@ -131,18 +135,23 @@ function blacklist:access()
|
|||
return self:ret(false, "lists is nil")
|
||||
end
|
||||
-- Perform checks
|
||||
for k, v in pairs(checks) do
|
||||
for k, _ in pairs(checks) do
|
||||
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)
|
||||
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)
|
||||
end
|
||||
if blacklisted ~= "ok" then
|
||||
return self:ret(true, k .. " is blacklisted (info : " .. blacklisted .. ")", utils.get_deny_status(self.ctx))
|
||||
return self:ret(
|
||||
true,
|
||||
k .. " is blacklisted (info : " .. blacklisted .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -205,11 +214,11 @@ function blacklist:is_blacklisted_ip()
|
|||
end
|
||||
if not match then
|
||||
-- Check if IP is in blacklist
|
||||
local ipm, err = ipmatcher.new(self.lists["IP"])
|
||||
ipm, err = ipmatcher.new(self.lists["IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
local match, err = ipm:match(self.ctx.bw.remote_addr)
|
||||
match, err = ipm:match(self.ctx.bw.remote_addr)
|
||||
if err then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -225,13 +234,14 @@ function blacklist:is_blacklisted_ip()
|
|||
end
|
||||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
if rdns_list then
|
||||
-- Check if rDNS is in ignore list
|
||||
local ignore = false
|
||||
for i, rdns in ipairs(rdns_list) do
|
||||
for j, suffix in ipairs(self.lists["IGNORE_RDNS"]) do
|
||||
if rdns:sub(- #suffix) == suffix then
|
||||
for _, rdns in ipairs(rdns_list) do
|
||||
for _, suffix in ipairs(self.lists["IGNORE_RDNS"]) do
|
||||
if rdns:sub(-#suffix) == suffix then
|
||||
ignore = true
|
||||
break
|
||||
end
|
||||
|
|
@ -239,9 +249,9 @@ function blacklist:is_blacklisted_ip()
|
|||
end
|
||||
-- Check if rDNS is in blacklist
|
||||
if not ignore then
|
||||
for i, rdns in ipairs(rdns_list) do
|
||||
for j, suffix in ipairs(self.lists["RDNS"]) do
|
||||
if rdns:sub(- #suffix) == suffix then
|
||||
for _, rdns in ipairs(rdns_list) do
|
||||
for _, suffix in ipairs(self.lists["RDNS"]) do
|
||||
if rdns:sub(-#suffix) == suffix then
|
||||
return true, "rDNS " .. suffix
|
||||
end
|
||||
end
|
||||
|
|
@ -259,7 +269,7 @@ function blacklist:is_blacklisted_ip()
|
|||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
local ignore = false
|
||||
for i, ignore_asn in ipairs(self.lists["IGNORE_ASN"]) do
|
||||
for _, ignore_asn in ipairs(self.lists["IGNORE_ASN"]) do
|
||||
if ignore_asn == tostring(asn) then
|
||||
ignore = true
|
||||
break
|
||||
|
|
@ -267,7 +277,7 @@ function blacklist:is_blacklisted_ip()
|
|||
end
|
||||
-- Check if ASN is in blacklist
|
||||
if not ignore then
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
end
|
||||
|
|
@ -283,7 +293,7 @@ end
|
|||
function blacklist:is_blacklisted_uri()
|
||||
-- Check if URI is in ignore list
|
||||
local ignore = false
|
||||
for i, ignore_uri in ipairs(self.lists["IGNORE_URI"]) do
|
||||
for _, ignore_uri in ipairs(self.lists["IGNORE_URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, ignore_uri) then
|
||||
ignore = true
|
||||
break
|
||||
|
|
@ -291,7 +301,7 @@ function blacklist:is_blacklisted_uri()
|
|||
end
|
||||
-- Check if URI is in blacklist
|
||||
if not ignore then
|
||||
for i, uri in ipairs(self.lists["URI"]) do
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
|
|
@ -304,7 +314,7 @@ end
|
|||
function blacklist:is_blacklisted_ua()
|
||||
-- Check if UA is in ignore list
|
||||
local ignore = false
|
||||
for i, ignore_ua in ipairs(self.lists["IGNORE_USER_AGENT"]) do
|
||||
for _, ignore_ua in ipairs(self.lists["IGNORE_USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ignore_ua) then
|
||||
ignore = true
|
||||
break
|
||||
|
|
@ -312,7 +322,7 @@ function blacklist:is_blacklisted_ua()
|
|||
end
|
||||
-- Check if UA is in blacklist
|
||||
if not ignore then
|
||||
for i, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local http = require "resty.http"
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local http = require "resty.http"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local bunkernet = class("bunkernet", plugin)
|
||||
|
||||
|
|
@ -49,12 +49,15 @@ function bunkernet:init_worker()
|
|||
return self:ret(false, "missing instance ID")
|
||||
end
|
||||
-- Send ping request
|
||||
local ok, err, status, data = self:ping()
|
||||
local ok, err, status, _ = self:ping()
|
||||
if not ok then
|
||||
return self:ret(false, "error while sending request to API : " .. err)
|
||||
end
|
||||
if status ~= 200 then
|
||||
return self:ret(false, "received status " .. tostring(status) .. " from API using instance ID " .. self.bunkernet_id)
|
||||
return self:ret(
|
||||
false,
|
||||
"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")
|
||||
return self:ret(true, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||
|
|
@ -82,7 +85,7 @@ function bunkernet:init()
|
|||
local ret = true
|
||||
local i = 0
|
||||
local db = {
|
||||
ip = {}
|
||||
ip = {},
|
||||
}
|
||||
local f, err = io.open("/var/cache/bunkerweb/bunkernet/ip.list", "r")
|
||||
if not f then
|
||||
|
|
@ -128,6 +131,7 @@ function bunkernet:access()
|
|||
if db then
|
||||
-- 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)
|
||||
if present == nil then
|
||||
return self:ret(false, "can't check if ip is in db : " .. err)
|
||||
|
|
@ -166,8 +170,9 @@ function bunkernet:log(bypass_checks)
|
|||
return self:ret(true, "IP is not global")
|
||||
end
|
||||
-- TODO : check if IP has been reported recently
|
||||
-- luacheck: ignore 212 431
|
||||
local function report_callback(premature, obj, ip, reason, method, url, headers)
|
||||
local ok, err, status, data = obj:report(ip, reason, method, url, headers)
|
||||
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")
|
||||
elseif not ok then
|
||||
|
|
@ -177,8 +182,16 @@ function bunkernet:log(bypass_checks)
|
|||
end
|
||||
end
|
||||
|
||||
local hdr, err = ngx.timer.at(0, report_callback, self, self.ctx.bw.remote_addr, reason, self.ctx.bw.request_method,
|
||||
self.ctx.bw.request_uri, ngx.req.get_headers())
|
||||
local hdr, err = ngx.timer.at(
|
||||
0,
|
||||
report_callback,
|
||||
self,
|
||||
self.ctx.bw.remote_addr,
|
||||
reason,
|
||||
self.ctx.bw.request_method,
|
||||
self.ctx.bw.request_uri,
|
||||
ngx.req.get_headers()
|
||||
)
|
||||
if not hdr then
|
||||
return self:ret(false, "can't create report timer : " .. err)
|
||||
end
|
||||
|
|
@ -218,7 +231,7 @@ function bunkernet:request(method, url, data)
|
|||
local all_data = {
|
||||
id = self.bunkernet_id,
|
||||
version = self.version,
|
||||
integration = self.integration
|
||||
integration = self.integration,
|
||||
}
|
||||
if data then
|
||||
for k, v in pairs(data) do
|
||||
|
|
@ -230,8 +243,8 @@ function bunkernet:request(method, url, data)
|
|||
body = cjson.encode(all_data),
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
["User-Agent"] = "BunkerWeb/" .. self.version
|
||||
}
|
||||
["User-Agent"] = "BunkerWeb/" .. self.version,
|
||||
},
|
||||
})
|
||||
httpc:close()
|
||||
if not res then
|
||||
|
|
@ -257,7 +270,7 @@ function bunkernet:report(ip, reason, method, url, headers)
|
|||
reason = reason,
|
||||
method = method,
|
||||
url = url,
|
||||
headers = headers
|
||||
headers = headers,
|
||||
}
|
||||
return self:request("POST", "/report", data)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
local class = require "middleclass"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local cors = class("cors", plugin)
|
||||
local cors = class("cors", plugin)
|
||||
|
||||
function cors:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
|
|
@ -17,7 +17,7 @@ function cors:initialize(ctx)
|
|||
["CORS_MAX_AGE"] = "Access-Control-Max-Age",
|
||||
["CORS_ALLOW_CREDENTIALS"] = "Access-Control-Allow-Credentials",
|
||||
["CORS_ALLOW_METHODS"] = "Access-Control-Allow-Methods",
|
||||
["CORS_ALLOW_HEADERS"] = "Access-Control-Allow-Headers"
|
||||
["CORS_ALLOW_HEADERS"] = "Access-Control-Allow-Headers",
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -43,7 +43,12 @@ function cors:header()
|
|||
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"]) then
|
||||
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"])
|
||||
then
|
||||
self.logger:log(ngx.WARN, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
return self:ret(true, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
end
|
||||
|
|
@ -81,9 +86,17 @@ function cors:access()
|
|||
return self:ret(true, "service doesn't use CORS")
|
||||
end
|
||||
-- Deny as soon as possible if needed
|
||||
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"]) then
|
||||
return self:ret(true, "origin " .. self.ctx.bw.http_origin .. " is not allowed, denying access",
|
||||
utils.get_deny_status(self.ctx))
|
||||
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"])
|
||||
then
|
||||
return self:ret(
|
||||
true,
|
||||
"origin " .. self.ctx.bw.http_origin .. " is not allowed, denying access",
|
||||
utils.get_deny_status(self.ctx)
|
||||
)
|
||||
end
|
||||
-- Send CORS policy with a 204 (no content) status
|
||||
if self.ctx.bw.request_method == "OPTIONS" and self.ctx.bw.http_origin then
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local country = class("country", plugin)
|
||||
|
||||
|
|
@ -16,17 +16,28 @@ function country:access()
|
|||
return self:ret(true, "country not activated")
|
||||
end
|
||||
-- Check if IP is in cache
|
||||
local ok, data = self:is_in_cache(self.ctx.bw.remote_addr)
|
||||
local _, data = self:is_in_cache(self.ctx.bw.remote_addr)
|
||||
if data then
|
||||
data = cjson.decode(data)
|
||||
if data.result == "ok" then
|
||||
return self:ret(true,
|
||||
"client IP " ..
|
||||
self.ctx.bw.remote_addr .. " is in country cache (not blacklisted, country = " .. data.country .. ")")
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP "
|
||||
.. self.ctx.bw.remote_addr
|
||||
.. " is in country cache (not blacklisted, country = "
|
||||
.. data.country
|
||||
.. ")"
|
||||
)
|
||||
end
|
||||
return self:ret(true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is in country cache (blacklisted, country = " .. data.country .. ")",
|
||||
utils.get_deny_status(self.ctx))
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP "
|
||||
.. self.ctx.bw.remote_addr
|
||||
.. " is in country cache (blacklisted, country = "
|
||||
.. data.country
|
||||
.. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
)
|
||||
end
|
||||
|
||||
-- Don't go further if IP is not global
|
||||
|
|
@ -39,50 +50,64 @@ function country:access()
|
|||
end
|
||||
|
||||
-- Get the country of client
|
||||
local country, err = utils.get_country(self.ctx.bw.remote_addr)
|
||||
if not country then
|
||||
local country_data, err = utils.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
|
||||
|
||||
-- Process whitelist first
|
||||
if self.variables["WHITELIST_COUNTRY"] ~= "" then
|
||||
for wh_country in self.variables["WHITELIST_COUNTRY"]:gmatch("%S+") do
|
||||
if wh_country == country then
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ok")
|
||||
for wh_country in self.variables["WHITELIST_COUNTRY"]:gmatch "%S+" do
|
||||
if wh_country == country_data then
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ok")
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding item to cache : " .. err)
|
||||
end
|
||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is whitelisted (country = " .. country .. ")")
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is whitelisted (country = " .. country_data .. ")"
|
||||
)
|
||||
end
|
||||
end
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ko")
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ko")
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding item to cache : " .. err)
|
||||
end
|
||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is not whitelisted (country = " .. country .. ")",
|
||||
utils.get_deny_status(self.ctx))
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is not whitelisted (country = " .. country_data .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
)
|
||||
end
|
||||
|
||||
-- And then blacklist
|
||||
if self.variables["BLACKLIST_COUNTRY"] ~= "" then
|
||||
for bl_country in self.variables["BLACKLIST_COUNTRY"]:gmatch("%S+") do
|
||||
if bl_country == country then
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ko")
|
||||
for bl_country in self.variables["BLACKLIST_COUNTRY"]:gmatch "%S+" do
|
||||
if bl_country == country_data then
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ko")
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding item to cache : " .. err)
|
||||
end
|
||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is blacklisted (country = " .. country .. ")",
|
||||
utils.get_deny_status(self.ctx))
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is blacklisted (country = " .. country_data .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Country IP is not in blacklist
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ok")
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ok")
|
||||
if not ok then
|
||||
return self:ret(false, "error while caching IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
end
|
||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is not blacklisted (country = " .. country .. ")")
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is not blacklisted (country = " .. country_data .. ")"
|
||||
)
|
||||
end
|
||||
|
||||
function country:preread()
|
||||
|
|
@ -97,9 +122,12 @@ function country:is_in_cache(ip)
|
|||
return true, data
|
||||
end
|
||||
|
||||
function country:add_to_cache(ip, country, result)
|
||||
local ok, err = self.cachestore:set("plugin_country_" .. self.ctx.bw.server_name .. ip,
|
||||
cjson.encode({ country = country, result = result }), 86400)
|
||||
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 },
|
||||
86400
|
||||
)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +1,23 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local resolver = require "resty.dns.resolver"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local dnsbl = class("dnsbl", plugin)
|
||||
local dnsbl = class("dnsbl", plugin)
|
||||
|
||||
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)
|
||||
if not ips then
|
||||
return nil, server, err
|
||||
end
|
||||
for _, ip in ipairs(ips) do
|
||||
if ip:find "^127%.0%.0%." then
|
||||
return true, server
|
||||
end
|
||||
end
|
||||
return false, server
|
||||
end
|
||||
|
||||
function dnsbl:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
|
|
@ -26,14 +40,15 @@ function dnsbl:init_worker()
|
|||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, "127.0.0.2", server)
|
||||
local thread = ngx.thread.spawn(is_in_dnsbl, "127.0.0.2", server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
for data, thread in pairs(threads) do
|
||||
-- luacheck: ignore 421
|
||||
local ok, result, server, err = ngx.thread.wait(thread)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while waiting thread of " .. dnsbl .. " check : " .. result)
|
||||
self.logger:log(ngx.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)
|
||||
elseif not result then
|
||||
|
|
@ -65,14 +80,17 @@ function dnsbl:access()
|
|||
if cached == "ok" then
|
||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (not blacklisted)")
|
||||
end
|
||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")",
|
||||
utils.get_deny_status(self.ctx))
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
)
|
||||
end
|
||||
-- Loop on DNSBL list
|
||||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, self.ctx.bw.remote_addr, server)
|
||||
local thread = ngx.thread.spawn(is_in_dnsbl, self.ctx.bw.remote_addr, server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
|
|
@ -82,7 +100,7 @@ function dnsbl:access()
|
|||
while true do
|
||||
-- Compute threads to wait
|
||||
local wait_threads = {}
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
-- No server reported IP
|
||||
|
|
@ -90,6 +108,7 @@ function dnsbl:access()
|
|||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
-- luacheck: ignore 421
|
||||
local ok, result, server, err = ngx.thread.wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
|
|
@ -115,7 +134,7 @@ function dnsbl:access()
|
|||
-- Kill other threads
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
|
|
@ -159,18 +178,4 @@ function dnsbl:add_to_cache(ip, value)
|
|||
return true
|
||||
end
|
||||
|
||||
function dnsbl:is_in_dnsbl(ip, server)
|
||||
local request = resolver.arpa_str(ip):gsub("%.in%-addr%.arpa", ""):gsub("%.ip6%.arpa", "") .. "." .. server
|
||||
local ips, err = utils.get_ips(request, false)
|
||||
if not ips then
|
||||
return nil, server, err
|
||||
end
|
||||
for i, ip in ipairs(ips) do
|
||||
if ip:find("^127%.0%.0%.") then
|
||||
return true, server
|
||||
end
|
||||
end
|
||||
return false, server
|
||||
end
|
||||
|
||||
return dnsbl
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local template = nil
|
||||
if ngx.shared.datastore then
|
||||
template = require "resty.template"
|
||||
|
|
@ -14,52 +14,52 @@ function errors:initialize(ctx)
|
|||
self.default_errors = {
|
||||
["400"] = {
|
||||
title = "Bad Request",
|
||||
text = "The server did not understand the request."
|
||||
text = "The server did not understand the request.",
|
||||
},
|
||||
["401"] = {
|
||||
title = "Not Authorized",
|
||||
text = "Valid authentication credentials needed for the target resource."
|
||||
text = "Valid authentication credentials needed for the target resource.",
|
||||
},
|
||||
["403"] = {
|
||||
title = "Forbidden",
|
||||
text = "Access is forbidden to the requested page."
|
||||
text = "Access is forbidden to the requested page.",
|
||||
},
|
||||
["404"] = {
|
||||
title = "Not Found",
|
||||
text = "The server cannot find the requested page."
|
||||
text = "The server cannot find the requested page.",
|
||||
},
|
||||
["405"] = {
|
||||
title = "Method Not Allowed",
|
||||
text = "The method specified in the request is not allowed."
|
||||
text = "The method specified in the request is not allowed.",
|
||||
},
|
||||
["413"] = {
|
||||
title = "Request Entity Too Large",
|
||||
text = "The server will not accept the request, because the request entity is too large."
|
||||
text = "The server will not accept the request, because the request entity is too large.",
|
||||
},
|
||||
["429"] = {
|
||||
title = "Too Many Requests",
|
||||
text = "Too many requests sent in a given amount of time, try again later."
|
||||
text = "Too many requests sent in a given amount of time, try again later.",
|
||||
},
|
||||
["500"] = {
|
||||
title = "Internal Server Error",
|
||||
text = "The request was not completed. The server met an unexpected condition."
|
||||
text = "The request was not completed. The server met an unexpected condition.",
|
||||
},
|
||||
["501"] = {
|
||||
title = "Not Implemented",
|
||||
text = "The request was not completed. The server did not support the functionality required."
|
||||
text = "The request was not completed. The server did not support the functionality required.",
|
||||
},
|
||||
["502"] = {
|
||||
title = "Bad Gateway",
|
||||
text = "The request was not completed. The server received an invalid response from the upstream server."
|
||||
text = "The request was not completed. The server received an invalid response from the upstream server.",
|
||||
},
|
||||
["503"] = {
|
||||
title = "Service Unavailable",
|
||||
text = "The request was not completed. The server is temporarily overloading or down."
|
||||
text = "The request was not completed. The server is temporarily overloading or down.",
|
||||
},
|
||||
["504"] = {
|
||||
title = "Gateway Timeout",
|
||||
text = "The gateway has timed out."
|
||||
}
|
||||
text = "The gateway has timed out.",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ function errors:render_template(code)
|
|||
title = code .. " - " .. self.default_errors[code].title,
|
||||
error_title = self.default_errors[code].title,
|
||||
error_code = code,
|
||||
error_text = self.default_errors[code].text
|
||||
error_text = self.default_errors[code].text,
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
local ipmatcher = require "resty.ipmatcher"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local greylist = class("greylist", plugin)
|
||||
local greylist = class("greylist", plugin)
|
||||
|
||||
function greylist:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
|
|
@ -22,7 +22,7 @@ function greylist:initialize(ctx)
|
|||
["RDNS"] = {},
|
||||
["ASN"] = {},
|
||||
["USER_AGENT"] = {},
|
||||
["URI"] = {}
|
||||
["URI"] = {},
|
||||
}
|
||||
for kind, _ in pairs(kinds) do
|
||||
for data in self.variables["GREYLIST_" .. kind]:gmatch("%S+") do
|
||||
|
|
@ -67,7 +67,7 @@ function greylist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(greylists) do
|
||||
local f, err = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
||||
local f, _ = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(greylists[kind], line)
|
||||
|
|
@ -91,7 +91,7 @@ function greylist:access()
|
|||
end
|
||||
-- Check the caches
|
||||
local checks = {
|
||||
["IP"] = "ip" .. self.ctx.bw.remote_addr
|
||||
["IP"] = "ip" .. self.ctx.bw.remote_addr,
|
||||
}
|
||||
if self.ctx.bw.http_user_agent then
|
||||
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
||||
|
|
@ -102,7 +102,7 @@ function greylist:access()
|
|||
local already_cached = {
|
||||
["IP"] = false,
|
||||
["URI"] = false,
|
||||
["UA"] = false
|
||||
["UA"] = false,
|
||||
}
|
||||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
|
|
@ -120,12 +120,13 @@ function greylist:access()
|
|||
return self:ret(false, "lists is nil")
|
||||
end
|
||||
-- Perform checks
|
||||
for k, v in pairs(checks) do
|
||||
for k, _ in pairs(checks) do
|
||||
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)
|
||||
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)
|
||||
|
|
@ -187,12 +188,13 @@ function greylist:is_greylisted_ip()
|
|||
end
|
||||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
-- Check if rDNS is in greylist
|
||||
if rdns_list then
|
||||
for i, rdns in ipairs(rdns_list) do
|
||||
for j, suffix in ipairs(self.lists["RDNS"]) do
|
||||
if rdns:sub(- #suffix) == suffix then
|
||||
for _, rdns in ipairs(rdns_list) do
|
||||
for _, suffix in ipairs(self.lists["RDNS"]) do
|
||||
if rdns:sub(-#suffix) == suffix then
|
||||
return true, "rDNS " .. suffix
|
||||
end
|
||||
end
|
||||
|
|
@ -208,7 +210,7 @@ function greylist:is_greylisted_ip()
|
|||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
end
|
||||
|
|
@ -222,7 +224,7 @@ end
|
|||
|
||||
function greylist:is_greylisted_uri()
|
||||
-- Check if URI is in greylist
|
||||
for i, uri in ipairs(self.lists["URI"]) do
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
|
|
@ -233,7 +235,7 @@ end
|
|||
|
||||
function greylist:is_greylisted_ua()
|
||||
-- Check if UA is in greylist
|
||||
for i, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,99 +1,110 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local headers = class("headers", plugin)
|
||||
|
||||
function headers:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "headers", ctx)
|
||||
self.all_headers = {
|
||||
["STRICT_TRANSPORT_SECURITY"] = "Strict-Transport-Security",
|
||||
["CONTENT_SECURITY_POLICY"] = "Content-Security-Policy",
|
||||
["REFERRER_POLICY"] = "Referrer-Policy",
|
||||
["PERMISSIONS_POLICY"] = "Permissions-Policy",
|
||||
["FEATURE_POLICY"] = "Feature-Policy",
|
||||
["X_FRAME_OPTIONS"] = "X-Frame-Options",
|
||||
["X_CONTENT_TYPE_OPTIONS"] = "X-Content-Type-Options",
|
||||
["X_XSS_PROTECTION"] = "X-XSS-Protection"
|
||||
}
|
||||
-- Load data from datastore if needed
|
||||
if ngx.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)
|
||||
return
|
||||
end
|
||||
self.custom_headers = {}
|
||||
-- Extract global headers
|
||||
if custom_headers.global then
|
||||
for k, v in pairs(custom_headers.global) do
|
||||
self.custom_headers[k] = v
|
||||
end
|
||||
end
|
||||
-- Extract and overwrite if needed server headers
|
||||
if custom_headers[self.ctx.bw.server_name] then
|
||||
for k, v in pairs(custom_headers[self.ctx.bw.server_name]) do
|
||||
self.custom_headers[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "headers", ctx)
|
||||
self.all_headers = {
|
||||
["STRICT_TRANSPORT_SECURITY"] = "Strict-Transport-Security",
|
||||
["CONTENT_SECURITY_POLICY"] = "Content-Security-Policy",
|
||||
["REFERRER_POLICY"] = "Referrer-Policy",
|
||||
["PERMISSIONS_POLICY"] = "Permissions-Policy",
|
||||
["FEATURE_POLICY"] = "Feature-Policy",
|
||||
["X_FRAME_OPTIONS"] = "X-Frame-Options",
|
||||
["X_CONTENT_TYPE_OPTIONS"] = "X-Content-Type-Options",
|
||||
["X_XSS_PROTECTION"] = "X-XSS-Protection",
|
||||
}
|
||||
-- Load data from datastore if needed
|
||||
if ngx.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)
|
||||
return
|
||||
end
|
||||
self.custom_headers = {}
|
||||
-- Extract global headers
|
||||
if custom_headers.global then
|
||||
for k, v in pairs(custom_headers.global) do
|
||||
self.custom_headers[k] = v
|
||||
end
|
||||
end
|
||||
-- Extract and overwrite if needed server headers
|
||||
if custom_headers[self.ctx.bw.server_name] then
|
||||
for k, v in pairs(custom_headers[self.ctx.bw.server_name]) do
|
||||
self.custom_headers[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function headers:init()
|
||||
-- Get variables
|
||||
local variables, err = utils.get_multiple_variables({ "CUSTOM_HEADER" })
|
||||
if variables == nil then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
-- Store custom headers name and value
|
||||
local data = {}
|
||||
local i = 0
|
||||
for srv, vars in pairs(variables) do
|
||||
for var, value in pairs(vars) do
|
||||
if data[srv] == nil then
|
||||
data[srv] = {}
|
||||
end
|
||||
local m = utils.regex_match(value, "([\\w-]+): ([^,]+)")
|
||||
if m then
|
||||
data[srv][m[1]] = m[2]
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
local ok, err = self.datastore:set("plugin_headers_custom_headers", data, nil, true)
|
||||
if not ok then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
return self:ret(true, "successfully loaded " .. tostring(i) .. " custom headers")
|
||||
-- Get variables
|
||||
local variables, err = utils.get_multiple_variables({ "CUSTOM_HEADER" })
|
||||
if variables == nil then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
-- Store custom headers name and value
|
||||
local data = {}
|
||||
local i = 0
|
||||
for srv, vars in pairs(variables) do
|
||||
for _, value in pairs(vars) do
|
||||
if data[srv] == nil then
|
||||
data[srv] = {}
|
||||
end
|
||||
local m = utils.regex_match(value, "([\\w-]+): ([^,]+)")
|
||||
if m then
|
||||
data[srv][m[1]] = m[2]
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
local ok
|
||||
ok, err = self.datastore:set("plugin_headers_custom_headers", data, nil, true)
|
||||
if not ok then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
return self:ret(true, "successfully loaded " .. tostring(i) .. " custom headers")
|
||||
end
|
||||
|
||||
function headers:header()
|
||||
-- Override upstream headers if needed
|
||||
local ssl = self.ctx.bw.scheme == "https"
|
||||
for variable, header in pairs(self.all_headers) do
|
||||
if 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) then
|
||||
if (header ~= "Strict-Transport-Security" or ssl) then
|
||||
if header == "Content-Security-Policy" and self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes" then
|
||||
ngx.header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
||||
else
|
||||
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
|
||||
end
|
||||
-- Remove headers
|
||||
if self.variables["REMOVE_HEADERS"] ~= "" then
|
||||
for header in self.variables["REMOVE_HEADERS"]:gmatch("%S+") do
|
||||
ngx.header[header] = nil
|
||||
end
|
||||
end
|
||||
return self:ret(true, "edited headers for request")
|
||||
-- Override upstream headers if needed
|
||||
local ssl = self.ctx.bw.scheme == "https"
|
||||
for variable, header in pairs(self.all_headers) do
|
||||
if
|
||||
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
|
||||
)
|
||||
then
|
||||
if header ~= "Strict-Transport-Security" or ssl then
|
||||
if
|
||||
header == "Content-Security-Policy"
|
||||
and self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes"
|
||||
then
|
||||
ngx.header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
||||
else
|
||||
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
|
||||
end
|
||||
-- Remove headers
|
||||
if self.variables["REMOVE_HEADERS"] ~= "" then
|
||||
for header in self.variables["REMOVE_HEADERS"]:gmatch("%S+") do
|
||||
ngx.header[header] = nil
|
||||
end
|
||||
end
|
||||
return self:ret(true, "edited headers for request")
|
||||
end
|
||||
|
||||
return headers
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local cjson = require "cjson"
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
|
||||
local letsencrypt = class("letsencrypt", plugin)
|
||||
|
||||
|
|
@ -17,9 +17,12 @@ function letsencrypt:access()
|
|||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function letsencrypt:api(ctx)
|
||||
if not string.match(ctx.bw.uri, "^/lets%-encrypt/challenge$") or
|
||||
(ctx.bw.request_method ~= "POST" and ctx.bw.request_method ~= "DELETE") then
|
||||
if
|
||||
not string.match(ctx.bw.uri, "^/lets%-encrypt/challenge$")
|
||||
or (ctx.bw.request_method ~= "POST" and ctx.bw.request_method ~= "DELETE")
|
||||
then
|
||||
return false, nil, nil
|
||||
end
|
||||
local acme_folder = "/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/"
|
||||
|
|
@ -32,7 +35,9 @@ function letsencrypt:api(ctx)
|
|||
if ctx.bw.request_method == "POST" then
|
||||
local file, err = io.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 true,
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
{ status = "error", msg = "can't write validation token : " .. err }
|
||||
end
|
||||
file:write(data.validation)
|
||||
file:close()
|
||||
|
|
@ -40,7 +45,9 @@ function letsencrypt:api(ctx)
|
|||
elseif ctx.bw.request_method == "DELETE" then
|
||||
local ok, err = os.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 true,
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
{ status = "error", msg = "can't remove validation token : " .. err }
|
||||
end
|
||||
return true, ngx.HTTP_OK, { status = "success", msg = "validation token removed" }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +1,40 @@
|
|||
local class = require "middleclass"
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local limit = class("limit", plugin)
|
||||
local limit = class("limit", plugin)
|
||||
|
||||
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 delay = 0
|
||||
if rate_time == "s" then
|
||||
delay = 1
|
||||
elseif rate_time == "m" then
|
||||
delay = 60
|
||||
elseif rate_time == "h" then
|
||||
delay = 3600
|
||||
elseif rate_time == "d" then
|
||||
delay = 86400
|
||||
end
|
||||
-- Keep only timestamp within the delay
|
||||
for _, timestamp in ipairs(timestamps) do
|
||||
if current_timestamp - timestamp <= delay then
|
||||
table.insert(new_timestamps, timestamp)
|
||||
else
|
||||
updated = true
|
||||
end
|
||||
end
|
||||
-- Only insert the new timestamp if client is not limited already to avoid infinite insert
|
||||
if #new_timestamps <= rate_max then
|
||||
table.insert(new_timestamps, current_timestamp)
|
||||
updated = true
|
||||
end
|
||||
return updated, new_timestamps, delay
|
||||
end
|
||||
|
||||
function limit:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
|
|
@ -11,7 +42,6 @@ function limit:initialize(ctx)
|
|||
-- Load rules if needed
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
-- Get all rules from datastore
|
||||
local limited = false
|
||||
local all_rules, err = self.datastore:get("plugin_limit_rules", true)
|
||||
if not all_rules then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
|
|
@ -93,19 +123,16 @@ function limit:access()
|
|||
return self:ret(true, "limit request not enabled")
|
||||
end
|
||||
-- Check if URI is limited
|
||||
local rate = nil
|
||||
local uri = nil
|
||||
local rate
|
||||
for k, v in pairs(self.rules) do
|
||||
if k ~= "/" and utils.regex_match(self.ctx.bw.uri, k) then
|
||||
rate = v
|
||||
uri = k
|
||||
break
|
||||
end
|
||||
end
|
||||
if not rate then
|
||||
if self.rules["/"] then
|
||||
rate = self.rules["/"]
|
||||
uri = "/"
|
||||
else
|
||||
return self:ret(true, "no rule for " .. self.ctx.bw.uri)
|
||||
end
|
||||
|
|
@ -118,19 +145,37 @@ function limit:access()
|
|||
end
|
||||
-- Limit reached
|
||||
if limited then
|
||||
return self:ret(true,
|
||||
"client IP " ..
|
||||
self.ctx.bw.remote_addr ..
|
||||
" is limited for URL " ..
|
||||
self.ctx.bw.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")",
|
||||
ngx.HTTP_TOO_MANY_REQUESTS)
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP "
|
||||
.. self.ctx.bw.remote_addr
|
||||
.. " is limited for URL "
|
||||
.. self.ctx.bw.uri
|
||||
.. " (current rate = "
|
||||
.. current_rate
|
||||
.. "r/"
|
||||
.. rate_time
|
||||
.. " and max rate = "
|
||||
.. rate
|
||||
.. ")",
|
||||
ngx.HTTP_TOO_MANY_REQUESTS
|
||||
)
|
||||
end
|
||||
-- Limit not reached
|
||||
return self:ret(true,
|
||||
"client IP " ..
|
||||
self.ctx.bw.remote_addr ..
|
||||
" is not limited for URL " ..
|
||||
self.ctx.bw.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")")
|
||||
return self:ret(
|
||||
true,
|
||||
"client IP "
|
||||
.. self.ctx.bw.remote_addr
|
||||
.. " is not limited for URL "
|
||||
.. self.ctx.bw.uri
|
||||
.. " (current rate = "
|
||||
.. current_rate
|
||||
.. "r/"
|
||||
.. rate_time
|
||||
.. " and max rate = "
|
||||
.. rate
|
||||
.. ")"
|
||||
)
|
||||
end
|
||||
|
||||
function limit:limit_req(rate_max, rate_time)
|
||||
|
|
@ -143,9 +188,12 @@ function limit:limit_req(rate_max, rate_time)
|
|||
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), delay)
|
||||
cjson.encode(timestamps),
|
||||
delay
|
||||
)
|
||||
if not ok then
|
||||
return nil, "can't update timestamps : " .. err
|
||||
end
|
||||
|
|
@ -167,8 +215,8 @@ end
|
|||
|
||||
function limit:limit_req_local(rate_max, rate_time)
|
||||
-- Get timestamps
|
||||
local timestamps, err = self.datastore:get("plugin_limit_" ..
|
||||
self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri)
|
||||
local timestamps, err =
|
||||
self.datastore:get("plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri)
|
||||
if not timestamps and err ~= "not found" then
|
||||
return nil, err
|
||||
elseif err == "not found" then
|
||||
|
|
@ -176,12 +224,15 @@ function limit:limit_req_local(rate_max, rate_time)
|
|||
end
|
||||
timestamps = cjson.decode(timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated, new_timestamps, delay = self:limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
local updated, new_timestamps, delay = limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
-- Save new timestamps if needed
|
||||
if updated then
|
||||
-- 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), delay)
|
||||
cjson.encode(new_timestamps),
|
||||
delay
|
||||
)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -245,9 +296,15 @@ function limit:limit_req_redis(rate_max, rate_time)
|
|||
return nil, err
|
||||
end
|
||||
-- Execute script
|
||||
local timestamps, err = self.clusterstore:call("eval", redis_script, 1,
|
||||
"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")))
|
||||
local timestamps, err = self.clusterstore:call(
|
||||
"eval",
|
||||
redis_script,
|
||||
1,
|
||||
"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"))
|
||||
)
|
||||
if not timestamps then
|
||||
self.clusterstore:close()
|
||||
return nil, err
|
||||
|
|
@ -257,35 +314,4 @@ function limit:limit_req_redis(rate_max, rate_time)
|
|||
return timestamps, "success"
|
||||
end
|
||||
|
||||
function limit:limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated = false
|
||||
local new_timestamps = {}
|
||||
local current_timestamp = os.time(os.date("!*t"))
|
||||
local delay = 0
|
||||
if rate_time == "s" then
|
||||
delay = 1
|
||||
elseif rate_time == "m" then
|
||||
delay = 60
|
||||
elseif rate_time == "h" then
|
||||
delay = 3600
|
||||
elseif rate_time == "d" then
|
||||
delay = 86400
|
||||
end
|
||||
-- Keep only timestamp within the delay
|
||||
for i, timestamp in ipairs(timestamps) do
|
||||
if current_timestamp - timestamp <= delay then
|
||||
table.insert(new_timestamps, timestamp)
|
||||
else
|
||||
updated = true
|
||||
end
|
||||
end
|
||||
-- Only insert the new timestamp if client is not limited already to avoid infinite insert
|
||||
if #new_timestamps <= rate_max then
|
||||
table.insert(new_timestamps, current_timestamp)
|
||||
updated = true
|
||||
end
|
||||
return updated, new_timestamps, delay
|
||||
end
|
||||
|
||||
return limit
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
local class = require "middleclass"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local misc = class("misc", plugin)
|
||||
local misc = class("misc", plugin)
|
||||
|
||||
function misc:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "misc", ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "misc", ctx)
|
||||
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)
|
||||
end
|
||||
-- Check if method is allowed
|
||||
for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do
|
||||
if method == allowed_method then
|
||||
return self:ret(true, "method " .. method .. " is allowed")
|
||||
end
|
||||
end
|
||||
return self:ret(true, "method " .. method .. " is not allowed", ngx.HTTP_NOT_ALLOWED)
|
||||
-- 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)
|
||||
end
|
||||
-- Check if method is allowed
|
||||
for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do
|
||||
if method == allowed_method then
|
||||
return self:ret(true, "method " .. method .. " is allowed")
|
||||
end
|
||||
end
|
||||
return self:ret(true, "method " .. method .. " is not allowed", ngx.HTTP_NOT_ALLOWED)
|
||||
end
|
||||
|
||||
return misc
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
local class = require "middleclass"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
|
||||
local redis = class("redis", plugin)
|
||||
local redis = class("redis", plugin)
|
||||
|
||||
function redis:initialize()
|
||||
function redis:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "redis", ctx)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,153 +1,155 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local reversescan = class("reversescan", plugin)
|
||||
|
||||
function reversescan:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "reversescan", ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "reversescan", ctx)
|
||||
end
|
||||
|
||||
function reversescan:access()
|
||||
-- Check if access is needed
|
||||
if self.variables["USE_REVERSE_SCAN"] ~= "yes" then
|
||||
return self:ret(true, "reverse scan not activated")
|
||||
end
|
||||
-- Loop on ports
|
||||
local threads = {}
|
||||
local ret_threads = nil
|
||||
local ret_err = nil
|
||||
for port in self.variables["REVERSE_SCAN_PORTS"]:gmatch("%S+") do
|
||||
-- Check if the scan is already cached
|
||||
local ok, cached = self:is_in_cache(self.ctx.bw.remote_addr .. ":" .. port)
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
ret_err = "error getting info from cachestore : " .. cached
|
||||
break
|
||||
-- Deny access if port opened
|
||||
elseif cached == "open" then
|
||||
ret_threads = true
|
||||
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
||||
break
|
||||
-- Perform scan in a thread
|
||||
elseif not cached then
|
||||
local thread = ngx.thread.spawn(self.scan, self.ctx.bw.remote_addr, tonumber(port),
|
||||
tonumber(self.variables["REVERSE_SCAN_TIMEOUT"]))
|
||||
threads[port] = thread
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for port, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.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))
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- Check results of threads
|
||||
ret_threads = nil
|
||||
ret_err = nil
|
||||
local results = {}
|
||||
while true do
|
||||
-- Compute threads to wait
|
||||
local wait_threads = {}
|
||||
for port, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
-- No port opened
|
||||
if #wait_threads == 0 then
|
||||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
local ok, open, port = ngx.thread.wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
ret_err = "error while waiting thread : " .. open
|
||||
break
|
||||
end
|
||||
port = tostring(port)
|
||||
-- Remove thread from list
|
||||
threads[port] = nil
|
||||
-- Add result to cache
|
||||
local result = "close"
|
||||
if open then
|
||||
result = "open"
|
||||
end
|
||||
results[port] = result
|
||||
-- Port is opened
|
||||
if open then
|
||||
ret_threads = true
|
||||
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Kill running threads
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for port, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Cache results
|
||||
for port, result in pairs(results) do
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr .. ":" .. port, result)
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding element to cache : " .. err)
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- No port opened
|
||||
return self:ret(true, "no port open for IP " .. self.ctx.bw.remote_addr)
|
||||
-- Check if access is needed
|
||||
if self.variables["USE_REVERSE_SCAN"] ~= "yes" then
|
||||
return self:ret(true, "reverse scan not activated")
|
||||
end
|
||||
-- Loop on ports
|
||||
local threads = {}
|
||||
local ret_threads = nil
|
||||
local ret_err = nil
|
||||
for port in self.variables["REVERSE_SCAN_PORTS"]:gmatch("%S+") do
|
||||
-- Check if the scan is already cached
|
||||
local ok, cached = self:is_in_cache(self.ctx.bw.remote_addr .. ":" .. port)
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
ret_err = "error getting info from cachestore : " .. cached
|
||||
break
|
||||
-- Deny access if port opened
|
||||
elseif cached == "open" then
|
||||
ret_threads = true
|
||||
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
||||
break
|
||||
-- Perform scan in a thread
|
||||
elseif not cached then
|
||||
local thread = ngx.thread.spawn(
|
||||
self.scan,
|
||||
self.ctx.bw.remote_addr,
|
||||
tonumber(port),
|
||||
tonumber(self.variables["REVERSE_SCAN_TIMEOUT"])
|
||||
)
|
||||
threads[port] = thread
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.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))
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- Check results of threads
|
||||
ret_threads = nil
|
||||
ret_err = nil
|
||||
local results = {}
|
||||
while true do
|
||||
-- Compute threads to wait
|
||||
local wait_threads = {}
|
||||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
-- No port opened
|
||||
if #wait_threads == 0 then
|
||||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
local ok, open, port = ngx.thread.wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
ret_err = "error while waiting thread : " .. open
|
||||
break
|
||||
end
|
||||
port = tostring(port)
|
||||
-- Remove thread from list
|
||||
threads[port] = nil
|
||||
-- Add result to cache
|
||||
local result = "close"
|
||||
if open then
|
||||
result = "open"
|
||||
end
|
||||
results[port] = result
|
||||
-- Port is opened
|
||||
if open then
|
||||
ret_threads = true
|
||||
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Kill running threads
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Cache results
|
||||
for port, result in pairs(results) do
|
||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr .. ":" .. port, result)
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding element to cache : " .. err)
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- No port opened
|
||||
return self:ret(true, "no port open for IP " .. self.ctx.bw.remote_addr)
|
||||
end
|
||||
|
||||
function reversescan:preread()
|
||||
return self:access()
|
||||
return self:access()
|
||||
end
|
||||
|
||||
function reversescan.scan(ip, port, timeout)
|
||||
local tcpsock = ngx.socket.tcp()
|
||||
tcpsock:settimeout(timeout)
|
||||
local ok, err = tcpsock:connect(ip, port)
|
||||
tcpsock:close()
|
||||
if not ok then
|
||||
return false, port
|
||||
end
|
||||
return true, port
|
||||
local tcpsock = ngx.socket.tcp()
|
||||
tcpsock:settimeout(timeout)
|
||||
local ok, _ = tcpsock:connect(ip, port)
|
||||
tcpsock:close()
|
||||
if not ok then
|
||||
return false, port
|
||||
end
|
||||
return true, port
|
||||
end
|
||||
|
||||
function reversescan:is_in_cache(ip_port)
|
||||
local ok, data = self.cachestore:get("plugin_reverse_scan_" .. ip_port)
|
||||
if not ok then
|
||||
return false, data
|
||||
end
|
||||
return true, data
|
||||
local ok, data = self.cachestore:get("plugin_reverse_scan_" .. ip_port)
|
||||
if not ok then
|
||||
return false, data
|
||||
end
|
||||
return true, data
|
||||
end
|
||||
|
||||
function reversescan:add_to_cache(ip_port, value)
|
||||
local ok, err = self.cachestore:set("plugin_reverse_scan_" .. ip_port, value, 86400)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
return true
|
||||
local ok, err = self.cachestore:set("plugin_reverse_scan_" .. ip_port, value, 86400)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return reversescan
|
||||
|
|
|
|||
|
|
@ -1,118 +1,118 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local session = require "resty.session"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local session = require "resty.session"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local sessions = class("sessions", plugin)
|
||||
|
||||
function sessions:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "sessions", ctx)
|
||||
-- Check if random cookie name and secrets are already generated
|
||||
local is_random = {
|
||||
"SESSIONS_SECRET",
|
||||
"SESSIONS_NAME"
|
||||
}
|
||||
self.randoms = {}
|
||||
for i, var in ipairs(is_random) do
|
||||
if self.variables[var] == "random" then
|
||||
local data, err = self.datastore:get("storage_sessions_" .. var)
|
||||
if data then
|
||||
self.randoms[var] = data
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "sessions", ctx)
|
||||
-- Check if random cookie name and secrets are already generated
|
||||
local is_random = {
|
||||
"SESSIONS_SECRET",
|
||||
"SESSIONS_NAME",
|
||||
}
|
||||
self.randoms = {}
|
||||
for _, var in ipairs(is_random) do
|
||||
if self.variables[var] == "random" then
|
||||
local data, _ = self.datastore:get("storage_sessions_" .. var)
|
||||
if data then
|
||||
self.randoms[var] = data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sessions:set()
|
||||
if self.is_loading or self.kind ~= "http" then
|
||||
return self:ret(true, "set not needed")
|
||||
end
|
||||
local checks = {
|
||||
["IP"] = self.ctx.bw.remote_addr,
|
||||
["USER_AGENT"] = self.ctx.bw.http_user_agent or ""
|
||||
}
|
||||
self.ctx.bw.sessions_checks = {}
|
||||
for check, value in pairs(checks) do
|
||||
if self.variables["SESSIONS_CHECK_" .. check] == "yes" then
|
||||
table.insert(self.ctx.bw.sessions_checks, { check, value })
|
||||
end
|
||||
end
|
||||
return self:ret(true, "success")
|
||||
if self.is_loading or self.kind ~= "http" then
|
||||
return self:ret(true, "set not needed")
|
||||
end
|
||||
local checks = {
|
||||
["IP"] = self.ctx.bw.remote_addr,
|
||||
["USER_AGENT"] = self.ctx.bw.http_user_agent or "",
|
||||
}
|
||||
self.ctx.bw.sessions_checks = {}
|
||||
for check, value in pairs(checks) do
|
||||
if self.variables["SESSIONS_CHECK_" .. check] == "yes" then
|
||||
table.insert(self.ctx.bw.sessions_checks, { check, value })
|
||||
end
|
||||
end
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
function sessions:init()
|
||||
if self.is_loading or self.kind ~= "http" then
|
||||
return self:ret(true, "init not needed")
|
||||
end
|
||||
-- Get redis vars
|
||||
local redis_vars = {
|
||||
["USE_REDIS"] = "",
|
||||
["REDIS_HOST"] = "",
|
||||
["REDIS_PORT"] = "",
|
||||
["REDIS_DATABASE"] = "",
|
||||
["REDIS_SSL"] = "",
|
||||
["REDIS_TIMEOUT"] = "",
|
||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = ""
|
||||
}
|
||||
for k, v in pairs(redis_vars) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
if value == nil then
|
||||
return self:ret(false, "can't get " .. k .. " variable : " .. err)
|
||||
end
|
||||
redis_vars[k] = value
|
||||
end
|
||||
-- Init configuration
|
||||
local config = {
|
||||
secret = self.variables["SESSIONS_SECRET"],
|
||||
cookie_name = self.variables["SESSIONS_NAME"],
|
||||
idling_timeout = tonumber(self.variables["SESSIONS_IDLING_TIMEOUT"]),
|
||||
rolling_timeout = tonumber(self.variables["SESSIONS_ROLLING_TIMEOUT"]),
|
||||
absolute_timeout = tonumber(self.variables["SESSIONS_ABSOLUTE_TIMEOUT"])
|
||||
}
|
||||
if self.variables["SESSIONS_SECRET"] == "random" then
|
||||
if self.randoms["SESSIONS_SECRET"] then
|
||||
config.secret = self.randoms["SESSIONS_SECRET"]
|
||||
else
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.variables["SESSIONS_NAME"] == "random" then
|
||||
if self.randoms["SESSIONS_NAME"] then
|
||||
config.cookie_name = self.randoms["SESSIONS_NAME"]
|
||||
else
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
if redis_vars["USE_REDIS"] ~= "yes" then
|
||||
config.storage = "cookie"
|
||||
else
|
||||
config.storage = "redis"
|
||||
config.redis = {
|
||||
prefix = "sessions_",
|
||||
connect_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
send_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
read_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
keepalive_timeout = tonumber(redis_vars["REDIS_KEEPALIVE_IDLE"]),
|
||||
pool = "bw-redis",
|
||||
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
|
||||
ssl = redis_vars["REDIS_SSL"] == "yes",
|
||||
host = redis_vars["REDIS_HOST"],
|
||||
port = tonumber(redis_vars["REDIS_PORT"]),
|
||||
database = tonumber(redis_vars["REDIS_DATABASE"])
|
||||
}
|
||||
end
|
||||
session.init(config)
|
||||
return self:ret(true, "sessions init successful")
|
||||
if self.is_loading or self.kind ~= "http" then
|
||||
return self:ret(true, "init not needed")
|
||||
end
|
||||
-- Get redis vars
|
||||
local redis_vars = {
|
||||
["USE_REDIS"] = "",
|
||||
["REDIS_HOST"] = "",
|
||||
["REDIS_PORT"] = "",
|
||||
["REDIS_DATABASE"] = "",
|
||||
["REDIS_SSL"] = "",
|
||||
["REDIS_TIMEOUT"] = "",
|
||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = "",
|
||||
}
|
||||
for k, _ in pairs(redis_vars) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
if value == nil then
|
||||
return self:ret(false, "can't get " .. k .. " variable : " .. err)
|
||||
end
|
||||
redis_vars[k] = value
|
||||
end
|
||||
-- Init configuration
|
||||
local config = {
|
||||
secret = self.variables["SESSIONS_SECRET"],
|
||||
cookie_name = self.variables["SESSIONS_NAME"],
|
||||
idling_timeout = tonumber(self.variables["SESSIONS_IDLING_TIMEOUT"]),
|
||||
rolling_timeout = tonumber(self.variables["SESSIONS_ROLLING_TIMEOUT"]),
|
||||
absolute_timeout = tonumber(self.variables["SESSIONS_ABSOLUTE_TIMEOUT"]),
|
||||
}
|
||||
if self.variables["SESSIONS_SECRET"] == "random" then
|
||||
if self.randoms["SESSIONS_SECRET"] then
|
||||
config.secret = self.randoms["SESSIONS_SECRET"]
|
||||
else
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.variables["SESSIONS_NAME"] == "random" then
|
||||
if self.randoms["SESSIONS_NAME"] then
|
||||
config.cookie_name = self.randoms["SESSIONS_NAME"]
|
||||
else
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
if redis_vars["USE_REDIS"] ~= "yes" then
|
||||
config.storage = "cookie"
|
||||
else
|
||||
config.storage = "redis"
|
||||
config.redis = {
|
||||
prefix = "sessions_",
|
||||
connect_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
send_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
read_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
keepalive_timeout = tonumber(redis_vars["REDIS_KEEPALIVE_IDLE"]),
|
||||
pool = "bw-redis",
|
||||
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
|
||||
ssl = redis_vars["REDIS_SSL"] == "yes",
|
||||
host = redis_vars["REDIS_HOST"],
|
||||
port = tonumber(redis_vars["REDIS_PORT"]),
|
||||
database = tonumber(redis_vars["REDIS_DATABASE"]),
|
||||
}
|
||||
end
|
||||
session.init(config)
|
||||
return self:ret(true, "sessions init successful")
|
||||
end
|
||||
|
||||
return sessions
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
local env = require "resty.env"
|
||||
local ipmatcher = require "resty.ipmatcher"
|
||||
local env = require "resty.env"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local whitelist = class("whitelist", plugin)
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ function whitelist:initialize(ctx)
|
|||
["RDNS"] = {},
|
||||
["ASN"] = {},
|
||||
["USER_AGENT"] = {},
|
||||
["URI"] = {}
|
||||
["URI"] = {},
|
||||
}
|
||||
for kind, _ in pairs(kinds) do
|
||||
for data in self.variables["WHITELIST_" .. kind]:gmatch("%S+") do
|
||||
|
|
@ -64,11 +64,11 @@ function whitelist:init()
|
|||
["RDNS"] = {},
|
||||
["ASN"] = {},
|
||||
["USER_AGENT"] = {},
|
||||
["URI"] = {}
|
||||
["URI"] = {},
|
||||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(whitelists) do
|
||||
local f, err = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
||||
local f, _ = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(whitelists[kind], line)
|
||||
|
|
@ -123,13 +123,14 @@ function whitelist:access()
|
|||
return self:ret(true, err, ngx.OK)
|
||||
end
|
||||
-- Perform checks
|
||||
for k, v in pairs(already_cached) do
|
||||
local ok
|
||||
for k, _ in pairs(already_cached) do
|
||||
if not already_cached[k] then
|
||||
local ok, whitelisted = self:is_whitelisted(k)
|
||||
ok, whitelisted = self:is_whitelisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is whitelisted : " .. whitelisted)
|
||||
else
|
||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), whitelisted)
|
||||
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)
|
||||
end
|
||||
|
|
@ -163,7 +164,7 @@ end
|
|||
function whitelist:check_cache()
|
||||
-- Check the caches
|
||||
local checks = {
|
||||
["IP"] = "ip" .. self.ctx.bw.remote_addr
|
||||
["IP"] = "ip" .. self.ctx.bw.remote_addr,
|
||||
}
|
||||
if self.ctx.bw.http_user_agent then
|
||||
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
||||
|
|
@ -172,7 +173,7 @@ function whitelist:check_cache()
|
|||
checks["URI"] = "uri" .. self.ctx.bw.uri
|
||||
end
|
||||
local already_cached = {}
|
||||
for k, v in pairs(checks) do
|
||||
for k, _ in pairs(checks) do
|
||||
already_cached[k] = false
|
||||
end
|
||||
for k, v in pairs(checks) do
|
||||
|
|
@ -242,14 +243,15 @@ function whitelist:is_whitelisted_ip()
|
|||
end
|
||||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
-- Check if rDNS is in whitelist
|
||||
if rdns_list then
|
||||
local forward_check = nil
|
||||
local rdns_suffix = nil
|
||||
for i, rdns in ipairs(rdns_list) do
|
||||
for j, suffix in ipairs(self.lists["RDNS"]) do
|
||||
if rdns:sub(- #suffix) == suffix then
|
||||
for _, rdns in ipairs(rdns_list) do
|
||||
for _, suffix in ipairs(self.lists["RDNS"]) do
|
||||
if rdns:sub(-#suffix) == suffix then
|
||||
forward_check = rdns
|
||||
rdns_suffix = suffix
|
||||
break
|
||||
|
|
@ -262,12 +264,15 @@ function whitelist:is_whitelisted_ip()
|
|||
if forward_check then
|
||||
local ip_list, err = utils.get_ips(forward_check)
|
||||
if ip_list then
|
||||
for i, ip in ipairs(ip_list) do
|
||||
for _, ip in ipairs(ip_list) do
|
||||
if ip == self.ctx.bw.remote_addr then
|
||||
return true, "rDNS " .. rdns_suffix
|
||||
end
|
||||
end
|
||||
self.logger:log(ngx.WARN, "IP " .. self.ctx.bw.remote_addr .. " may spoof reverse DNS " .. forward_check)
|
||||
self.logger:log(
|
||||
ngx.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)
|
||||
end
|
||||
|
|
@ -283,7 +288,7 @@ function whitelist:is_whitelisted_ip()
|
|||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
end
|
||||
|
|
@ -297,7 +302,7 @@ end
|
|||
|
||||
function whitelist:is_whitelisted_uri()
|
||||
-- Check if URI is in whitelist
|
||||
for i, uri in ipairs(self.lists["URI"]) do
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
|
|
@ -308,7 +313,7 @@ end
|
|||
|
||||
function whitelist:is_whitelisted_ua()
|
||||
-- Check if UA is in whitelist
|
||||
for i, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ greenlet==3.0.0 \
|
|||
--hash=sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9 \
|
||||
--hash=sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d \
|
||||
--hash=sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14 \
|
||||
--hash=sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383 \
|
||||
--hash=sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b \
|
||||
--hash=sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99 \
|
||||
--hash=sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7 \
|
||||
|
|
@ -103,7 +104,6 @@ greenlet==3.0.0 \
|
|||
--hash=sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c \
|
||||
--hash=sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4 \
|
||||
--hash=sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362 \
|
||||
--hash=sha256:553d6fb2324e7f4f0899e5ad2c427a4579ed4873f42124beba763f16032959af \
|
||||
--hash=sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692 \
|
||||
--hash=sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365 \
|
||||
--hash=sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9 \
|
||||
|
|
@ -134,7 +134,6 @@ greenlet==3.0.0 \
|
|||
--hash=sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705 \
|
||||
--hash=sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c \
|
||||
--hash=sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f \
|
||||
--hash=sha256:c3692ecf3fe754c8c0f2c95ff19626584459eab110eaab66413b1e7425cd84e9 \
|
||||
--hash=sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c \
|
||||
--hash=sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870 \
|
||||
--hash=sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353 \
|
||||
|
|
@ -233,6 +232,7 @@ sqlalchemy==2.0.21 \
|
|||
--hash=sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258 \
|
||||
--hash=sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce \
|
||||
--hash=sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede \
|
||||
--hash=sha256:2a1f7ffac934bc0ea717fa1596f938483fb8c402233f9b26679b4f7b38d6ab6e \
|
||||
--hash=sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce \
|
||||
--hash=sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4 \
|
||||
--hash=sha256:3eb7c03fe1cd3255811cd4e74db1ab8dca22074d50cd8937edf4ef62d758cdf4 \
|
||||
|
|
@ -242,6 +242,9 @@ sqlalchemy==2.0.21 \
|
|||
--hash=sha256:4615623a490e46be85fbaa6335f35cf80e61df0783240afe7d4f544778c315a9 \
|
||||
--hash=sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67 \
|
||||
--hash=sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9 \
|
||||
--hash=sha256:526b869a0f4f000d8d8ee3409d0becca30ae73f494cbb48801da0129601f72c6 \
|
||||
--hash=sha256:56628ca27aa17b5890391ded4e385bf0480209726f198799b7e980c6bd473bd7 \
|
||||
--hash=sha256:632784f7a6f12cfa0e84bf2a5003b07660addccf5563c132cd23b7cc1d7371a9 \
|
||||
--hash=sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9 \
|
||||
--hash=sha256:73c079e21d10ff2be54a4699f55865d4b275fd6c8bd5d90c5b1ef78ae0197301 \
|
||||
--hash=sha256:7614f1eab4336df7dd6bee05bc974f2b02c38d3d0c78060c5faa4cd1ca2af3b8 \
|
||||
|
|
@ -258,14 +261,18 @@ sqlalchemy==2.0.21 \
|
|||
--hash=sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09 \
|
||||
--hash=sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a \
|
||||
--hash=sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063 \
|
||||
--hash=sha256:bfece2f7cec502ec5f759bbc09ce711445372deeac3628f6fa1c16b7fb45b682 \
|
||||
--hash=sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef \
|
||||
--hash=sha256:c2d494b6a2a2d05fb99f01b84cc9af9f5f93bf3e1e5dbdafe4bed0c2823584c1 \
|
||||
--hash=sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66 \
|
||||
--hash=sha256:cca720d05389ab1a5877ff05af96551e58ba65e8dc65582d849ac83ddde3e231 \
|
||||
--hash=sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e \
|
||||
--hash=sha256:d59cb9e20d79686aa473e0302e4a82882d7118744d30bb1dfb62d3c47141b3ec \
|
||||
--hash=sha256:db726be58837fe5ac39859e0fa40baafe54c6d54c02aba1d47d25536170b690f \
|
||||
--hash=sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430 \
|
||||
--hash=sha256:e7421c1bfdbb7214313919472307be650bd45c4dc2fcb317d64d078993de045b \
|
||||
--hash=sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce \
|
||||
--hash=sha256:f6f7276cf26145a888f2182a98f204541b519d9ea358a65d82095d9c9e22f917 \
|
||||
--hash=sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9 \
|
||||
--hash=sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa \
|
||||
--hash=sha256:fc6b15465fabccc94bf7e38777d665b6a4f95efd1725049d6184b3a39fd54880
|
||||
|
|
|
|||
|
|
@ -3,4 +3,3 @@ jinja2==3.1.2
|
|||
kubernetes==28.1.0
|
||||
python-dotenv==1.0.0
|
||||
redis==5.0.1
|
||||
requests==2.31.0
|
||||
|
|
|
|||
|
|
@ -114,9 +114,9 @@ docker==6.1.3 \
|
|||
--hash=sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20 \
|
||||
--hash=sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9
|
||||
# via -r requirements.in
|
||||
google-auth==2.23.2 \
|
||||
--hash=sha256:5a9af4be520ba33651471a0264eead312521566f44631cbb621164bc30c8fd40 \
|
||||
--hash=sha256:c2e253347579d483004f17c3bd0bf92e611ef6c7ba24d41c5c59f2e7aeeaf088
|
||||
google-auth==2.23.3 \
|
||||
--hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
|
||||
--hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
|
||||
# via kubernetes
|
||||
idna==3.4 \
|
||||
--hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
|
||||
|
|
@ -280,7 +280,6 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# docker
|
||||
# kubernetes
|
||||
# requests-oauthlib
|
||||
|
|
@ -305,9 +304,9 @@ urllib3==1.26.17 \
|
|||
# docker
|
||||
# kubernetes
|
||||
# requests
|
||||
websocket-client==1.6.3 \
|
||||
--hash=sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f \
|
||||
--hash=sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03
|
||||
websocket-client==1.6.4 \
|
||||
--hash=sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24 \
|
||||
--hash=sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df
|
||||
# via
|
||||
# docker
|
||||
# kubernetes
|
||||
|
|
|
|||
|
|
@ -40,14 +40,10 @@ pip install --no-cache-dir --require-hashes -r requirements-deps.txt
|
|||
|
||||
echo "Updating python requirements files"
|
||||
|
||||
files=("requirements.in" "../../docs/requirements.in" "../common/db/requirements.in" "../common/gen/requirements.in" "../scheduler/requirements.in" "../ui/requirements.in")
|
||||
files=("requirements.in" "../scheduler/requirements.in" "../ui/requirements.in")
|
||||
|
||||
shopt -s globstar
|
||||
for file in ../../misc/**/requirements*.in
|
||||
do
|
||||
files+=("$file")
|
||||
done
|
||||
for file in ../../tests/**/requirements*.in
|
||||
for file in ../{common,../{docs,misc,tests}}/**/requirements*.in
|
||||
do
|
||||
files+=("$file")
|
||||
done
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
certbot==2.7.0
|
||||
certbot==2.7.1
|
||||
cryptography==41.0.4
|
||||
maxminddb==2.4.0
|
||||
python-magic==0.4.27
|
||||
schedule==1.2.1
|
||||
urllib3==1.26.17
|
||||
configobj==5.0.8
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
acme==2.7.0 \
|
||||
--hash=sha256:7aeaf0886322ca5569bc532828086ff2a7fc5b66a29a0a5d3270363d448af228 \
|
||||
--hash=sha256:fabdcb97801dfd3bc23ac7ca7175f2d98e4192b62297a6efd6e6718423653ac3
|
||||
acme==2.7.1 \
|
||||
--hash=sha256:47aea91999434cb01a3530c7c76866f387fdb818097808704d6cfa98dbe4e966 \
|
||||
--hash=sha256:669167c5d855a9d60dbe9b98681ddb376ba1c2a17a35be3291489b8b0eaef4ff
|
||||
# via certbot
|
||||
certbot==2.7.0 \
|
||||
--hash=sha256:274329918656db82fb314e2ecb12a9b276a1df820e28e36e8ecca8c3a0563c1c \
|
||||
--hash=sha256:892d25d1016aa6da4f947b40f46a02efa9ba77f8811e0b22e9db046aae05e518
|
||||
certbot==2.7.1 \
|
||||
--hash=sha256:113a4aabbe946c3677cb2af867279d19a3f07aaff0ebd4683cc9768be14cc077 \
|
||||
--hash=sha256:22604d6ab8e5b665ea1aa201d65840298f5e4f98a100d399f52cf30c6f5c7408
|
||||
# via -r requirements.in
|
||||
certifi==2023.7.22 \
|
||||
--hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
|
||||
|
|
@ -169,7 +169,9 @@ configargparse==1.7 \
|
|||
configobj==5.0.8 \
|
||||
--hash=sha256:6f704434a07dc4f4dc7c9a745172c1cad449feb548febd9f7fe362629c627a97 \
|
||||
--hash=sha256:a7a8c6ab7daade85c3f329931a807c8aee750a2494363934f8ea84d8a54c87ea
|
||||
# via certbot
|
||||
# via
|
||||
# -r requirements.in
|
||||
# certbot
|
||||
cryptography==41.0.4 \
|
||||
--hash=sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67 \
|
||||
--hash=sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311 \
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ beautifulsoup4==4.12.2 \
|
|||
--hash=sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da \
|
||||
--hash=sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a
|
||||
# via -r requirements.in
|
||||
blinker==1.6.2 \
|
||||
--hash=sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213 \
|
||||
--hash=sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0
|
||||
blinker==1.6.3 \
|
||||
--hash=sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d \
|
||||
--hash=sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa
|
||||
# via flask
|
||||
click==8.1.7 \
|
||||
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
|
||||
|
|
@ -250,9 +250,9 @@ werkzeug==3.0.0 \
|
|||
# via
|
||||
# flask
|
||||
# flask-login
|
||||
wtforms==3.0.1 \
|
||||
--hash=sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc \
|
||||
--hash=sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b
|
||||
wtforms==3.1.0 \
|
||||
--hash=sha256:4edd15771630289a5fa343d58822f72749822ca5a39dd33f92ee917cf72b954b \
|
||||
--hash=sha256:addd7899004fdf9318eb711d33aae9c1973fe80378257b7383e06de2eff7c559
|
||||
# via flask-wtf
|
||||
zipp==3.17.0 \
|
||||
--hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \
|
||||
|
|
|
|||
4
stylua.toml
Normal file
4
stylua.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
call_parentheses = "Input"
|
||||
|
||||
[sort_requires]
|
||||
enabled = true
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
requests==2.31.0
|
||||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
urllib3[socks]==2.0.6
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
requests==2.31.0
|
||||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
urllib3[socks]==2.0.6
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ urllib3==2.0.6 \
|
|||
# via
|
||||
# docker
|
||||
# requests
|
||||
websocket-client==1.6.3 \
|
||||
--hash=sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f \
|
||||
--hash=sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03
|
||||
websocket-client==1.6.4 \
|
||||
--hash=sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24 \
|
||||
--hash=sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df
|
||||
# via docker
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
annotated-types==0.5.0 \
|
||||
--hash=sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802 \
|
||||
--hash=sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd
|
||||
annotated-types==0.6.0 \
|
||||
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||
# via pydantic
|
||||
anyio==3.7.1 \
|
||||
--hash=sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780 \
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
annotated-types==0.5.0 \
|
||||
--hash=sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802 \
|
||||
--hash=sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd
|
||||
annotated-types==0.6.0 \
|
||||
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||
# via pydantic
|
||||
anyio==3.7.1 \
|
||||
--hash=sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780 \
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ urllib3==2.0.6 \
|
|||
# via
|
||||
# docker
|
||||
# requests
|
||||
websocket-client==1.6.3 \
|
||||
--hash=sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f \
|
||||
--hash=sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03
|
||||
websocket-client==1.6.4 \
|
||||
--hash=sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24 \
|
||||
--hash=sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df
|
||||
# via docker
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
requests==2.31.0
|
||||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
urllib3[socks]==2.0.6
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ greenlet==3.0.0 \
|
|||
--hash=sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9 \
|
||||
--hash=sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d \
|
||||
--hash=sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14 \
|
||||
--hash=sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383 \
|
||||
--hash=sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b \
|
||||
--hash=sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99 \
|
||||
--hash=sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7 \
|
||||
|
|
@ -103,7 +104,6 @@ greenlet==3.0.0 \
|
|||
--hash=sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c \
|
||||
--hash=sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4 \
|
||||
--hash=sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362 \
|
||||
--hash=sha256:553d6fb2324e7f4f0899e5ad2c427a4579ed4873f42124beba763f16032959af \
|
||||
--hash=sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692 \
|
||||
--hash=sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365 \
|
||||
--hash=sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9 \
|
||||
|
|
@ -134,7 +134,6 @@ greenlet==3.0.0 \
|
|||
--hash=sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705 \
|
||||
--hash=sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c \
|
||||
--hash=sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f \
|
||||
--hash=sha256:c3692ecf3fe754c8c0f2c95ff19626584459eab110eaab66413b1e7425cd84e9 \
|
||||
--hash=sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c \
|
||||
--hash=sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870 \
|
||||
--hash=sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353 \
|
||||
|
|
@ -233,6 +232,7 @@ sqlalchemy==2.0.21 \
|
|||
--hash=sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258 \
|
||||
--hash=sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce \
|
||||
--hash=sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede \
|
||||
--hash=sha256:2a1f7ffac934bc0ea717fa1596f938483fb8c402233f9b26679b4f7b38d6ab6e \
|
||||
--hash=sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce \
|
||||
--hash=sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4 \
|
||||
--hash=sha256:3eb7c03fe1cd3255811cd4e74db1ab8dca22074d50cd8937edf4ef62d758cdf4 \
|
||||
|
|
@ -242,6 +242,9 @@ sqlalchemy==2.0.21 \
|
|||
--hash=sha256:4615623a490e46be85fbaa6335f35cf80e61df0783240afe7d4f544778c315a9 \
|
||||
--hash=sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67 \
|
||||
--hash=sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9 \
|
||||
--hash=sha256:526b869a0f4f000d8d8ee3409d0becca30ae73f494cbb48801da0129601f72c6 \
|
||||
--hash=sha256:56628ca27aa17b5890391ded4e385bf0480209726f198799b7e980c6bd473bd7 \
|
||||
--hash=sha256:632784f7a6f12cfa0e84bf2a5003b07660addccf5563c132cd23b7cc1d7371a9 \
|
||||
--hash=sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9 \
|
||||
--hash=sha256:73c079e21d10ff2be54a4699f55865d4b275fd6c8bd5d90c5b1ef78ae0197301 \
|
||||
--hash=sha256:7614f1eab4336df7dd6bee05bc974f2b02c38d3d0c78060c5faa4cd1ca2af3b8 \
|
||||
|
|
@ -258,14 +261,18 @@ sqlalchemy==2.0.21 \
|
|||
--hash=sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09 \
|
||||
--hash=sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a \
|
||||
--hash=sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063 \
|
||||
--hash=sha256:bfece2f7cec502ec5f759bbc09ce711445372deeac3628f6fa1c16b7fb45b682 \
|
||||
--hash=sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef \
|
||||
--hash=sha256:c2d494b6a2a2d05fb99f01b84cc9af9f5f93bf3e1e5dbdafe4bed0c2823584c1 \
|
||||
--hash=sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66 \
|
||||
--hash=sha256:cca720d05389ab1a5877ff05af96551e58ba65e8dc65582d849ac83ddde3e231 \
|
||||
--hash=sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e \
|
||||
--hash=sha256:d59cb9e20d79686aa473e0302e4a82882d7118744d30bb1dfb62d3c47141b3ec \
|
||||
--hash=sha256:db726be58837fe5ac39859e0fa40baafe54c6d54c02aba1d47d25536170b690f \
|
||||
--hash=sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430 \
|
||||
--hash=sha256:e7421c1bfdbb7214313919472307be650bd45c4dc2fcb317d64d078993de045b \
|
||||
--hash=sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce \
|
||||
--hash=sha256:f6f7276cf26145a888f2182a98f204541b519d9ea358a65d82095d9c9e22f917 \
|
||||
--hash=sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9 \
|
||||
--hash=sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa \
|
||||
--hash=sha256:fc6b15465fabccc94bf7e38777d665b6a4f95efd1725049d6184b3a39fd54880
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ pysocks==1.7.1 \
|
|||
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
||||
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||
# via urllib3
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
requests==2.31.0
|
||||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
urllib3[socks]==2.0.6
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
annotated-types==0.5.0 \
|
||||
--hash=sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802 \
|
||||
--hash=sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd
|
||||
annotated-types==0.6.0 \
|
||||
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||
# via pydantic
|
||||
anyio==3.7.1 \
|
||||
--hash=sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780 \
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
requests==2.31.0
|
||||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
urllib3[socks]==2.0.6
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ requests==2.31.0
|
|||
redis==5.0.1
|
||||
fastapi==0.103.2
|
||||
uvicorn[standard]==0.23.2
|
||||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
annotated-types==0.5.0 \
|
||||
--hash=sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802 \
|
||||
--hash=sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd
|
||||
annotated-types==0.6.0 \
|
||||
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||
# via pydantic
|
||||
anyio==3.7.1 \
|
||||
--hash=sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780 \
|
||||
|
|
@ -373,9 +373,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
annotated-types==0.5.0 \
|
||||
--hash=sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802 \
|
||||
--hash=sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd
|
||||
annotated-types==0.6.0 \
|
||||
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||
# via pydantic
|
||||
anyio==3.7.1 \
|
||||
--hash=sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780 \
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
requests==2.31.0
|
||||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
urllib3[socks]==2.0.6
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#
|
||||
# pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in
|
||||
#
|
||||
annotated-types==0.5.0 \
|
||||
--hash=sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802 \
|
||||
--hash=sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd
|
||||
annotated-types==0.6.0 \
|
||||
--hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \
|
||||
--hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d
|
||||
# via pydantic
|
||||
anyio==3.7.1 \
|
||||
--hash=sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780 \
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
selenium==4.13.0
|
||||
selenium==4.14.0
|
||||
requests==2.31.0
|
||||
urllib3[socks]==2.0.6
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ requests==2.31.0 \
|
|||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
# via -r requirements.in
|
||||
selenium==4.13.0 \
|
||||
--hash=sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f \
|
||||
--hash=sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece
|
||||
selenium==4.14.0 \
|
||||
--hash=sha256:0d14b0d9842366f38fb5f8f842cf7c042bcfa062affc6a0a86e4d634bdd0fe54 \
|
||||
--hash=sha256:be9824a9354a7fe288e3fad9ceb6a9c65ddc7c44545d23ad0ebf4ce202b19893
|
||||
# via -r requirements.in
|
||||
sniffio==1.3.0 \
|
||||
--hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
|
||||
|
|
|
|||
Loading…
Reference in a new issue