Merge pull request #485 from bunkerity/dev
Merge branch "dev" into branch "ui"
This commit is contained in:
commit
eda275589d
3
TODO
3
TODO
|
@ -1,6 +1,5 @@
|
|||
- Ansible
|
||||
- Vagrant
|
||||
- Plugins
|
||||
- sessions helpers in utils
|
||||
- sessions security : check IP address, check UA, ...
|
||||
- Find a way to do rdns in background
|
||||
- fix db warnings (Got an error reading communication packets)
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 91 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 98 KiB |
|
@ -253,14 +253,19 @@ That kind of security is implemented but not enabled by default in BunkerWeb and
|
|||
|
||||
Here is the list of related settings :
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--------------------------------------------------------: | :----------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `USE_ANTIBOT` | `no` | Accepted values to enable Antibot feature : `cookie`, `javascript`, `captcha`, `hcaptcha` and `recaptcha`. |
|
||||
| `ANTIBOT_URI` | `/challenge` | URI that clients will be redirected to in order to solve the challenge. Be sure that it isn't used in your web application. |
|
||||
| `ANTIBOT_SESSION_SECRET` | `random` | The secret used to encrypt cookies when using Antibot. The special value `random` will generate one for you. Be sure to set it when you use a clustered integration (32 chars). |
|
||||
| `ANTIBOT_HCAPTCHA_SITEKEY` and `ANTIBOT_RECAPTCHA_SITEKEY` | | The Sitekey value to use when `USE_ANTIBOT` is set to `hcaptcha` or `recaptcha`. |
|
||||
| `ANTIBOT_HCAPTCHA_SECRET` and `ANTIBOT_RECAPTCHA_SECRET` | | The Secret value to use when `USE_ANTIBOT` is set to `hcaptcha` or `recaptcha`. |
|
||||
| `ANTIBOT_RECAPTCHA_SCORE` | `0.7` | The minimum score that clients must have when `USE_ANTIBOT` is set to `recaptcha`. |
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|---------------------------|------------|---------|--------|------------------------------------------------------------------------------------------------------------------------------|
|
||||
|`USE_ANTIBOT` |`no` |multisite|no |Activate antibot feature. |
|
||||
|`ANTIBOT_URI` |`/challenge`|multisite|no |Unused URI that clients will be redirected to to solve the challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SCORE` |`0.7` |multisite|no |Minimum score required for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SITEKEY`| |multisite|no |Sitekey for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SECRET` | |multisite|no |Secret for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_HCAPTCHA_SITEKEY` | |multisite|no |Sitekey for hCaptcha challenge. |
|
||||
|`ANTIBOT_HCAPTCHA_SECRET` | |multisite|no |Secret for hCaptcha challenge. |
|
||||
|`ANTIBOT_TIME_RESOLVE` |`60` |multisite|no |Maximum time (in seconds) clients have to resolve the challenge. Once this time has passed, a new challenge will be generated.|
|
||||
|`ANTIBOT_TIME_VALID` |`86400` |multisite|no |Maximum validity time of solved challenges. Once this time has passed, clients will need to resolve a new one. |
|
||||
|
||||
Please note that antibot feature is using a cookie to maintain a session with clients. If you are using BunkerWeb in a clustered environment, you will need to set the `SESSIONS_SECRET` and `SESSIONS_NAME` settings to another value than the default one (which is `random`). You will find more info about sessions [here](settings.md#sessions).
|
||||
|
||||
## Blacklisting, whitelisting and greylisting
|
||||
|
||||
|
|
|
@ -498,6 +498,8 @@ Management of session used by other plugins.
|
|||
|`SESSIONS_IDLING_TIMEOUT` |`1800` |global |no |Maximum time (in seconds) of inactivity before the session is invalidated. |
|
||||
|`SESSIONS_ROLLING_TIMEOUT` |`3600` |global |no |Maximum time (in seconds) before a session must be renewed. |
|
||||
|`SESSIONS_ABSOLUTE_TIMEOUT`|`86400` |global |no |Maximum time (in seconds) before a session is destroyed. |
|
||||
|`SESSIONS_CHECK_IP` |`yes` |global |no |Destroy session if IP address is different than original one. |
|
||||
|`SESSIONS_CHECK_USER_AGENT`|`yes` |global |no |Destroy session if User-Agent is different than original one. |
|
||||
|
||||
### UI
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "mysql+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -151,7 +151,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "mysql+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -213,64 +213,6 @@ spec:
|
|||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bunkerweb-redis
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: bunkerweb-redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: bunkerweb-redis
|
||||
spec:
|
||||
containers:
|
||||
- name: bunkerweb-redis
|
||||
image: redis:7-alpine
|
||||
imagePullPolicy: Always
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bunkerweb-db
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: bunkerweb-db
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: bunkerweb-db
|
||||
spec:
|
||||
containers:
|
||||
- name: bunkerweb-db
|
||||
image: mariadb:10.10
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: MYSQL_RANDOM_ROOT_PASSWORD
|
||||
value: "yes"
|
||||
- name: "MYSQL_DATABASE"
|
||||
value: "db"
|
||||
- name: "MYSQL_USER"
|
||||
value: "bunkerweb"
|
||||
- name: "MYSQL_PASSWORD"
|
||||
value: "changeme"
|
||||
volumeMounts:
|
||||
- mountPath: "/var/lib/mysql"
|
||||
name: vol-db
|
||||
volumes:
|
||||
- name: vol-db
|
||||
persistentVolumeClaim:
|
||||
claimName: pvc-bunkerweb
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bunkerweb-ui
|
||||
spec:
|
||||
|
@ -300,7 +242,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "YES"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:testor@svc-bunkerweb-db:3306/db"
|
||||
value: "mysql+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
|
@ -363,7 +305,6 @@ spec:
|
|||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
volumeName: pv-bunkerweb
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
|
|
|
@ -124,7 +124,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "mysql+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -150,7 +150,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "mysql+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -257,4 +257,3 @@ spec:
|
|||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
volumeName: pv-bunkerweb
|
||||
|
|
|
@ -124,7 +124,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "postgresql://bunkerweb:changeme@svc-bunkerweb-db:5432/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -151,7 +151,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "postgresql://bunkerweb:changeme@svc-bunkerweb-db:5432/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -201,6 +201,8 @@ spec:
|
|||
value: "bunkerweb"
|
||||
- name: "POSTGRES_PASSWORD"
|
||||
value: "changeme"
|
||||
- name: "PGDATA"
|
||||
value: "/var/lib/postgresql/data/pgdata"
|
||||
volumeMounts:
|
||||
- mountPath: "/var/lib/postgresql/data"
|
||||
name: vol-db
|
||||
|
@ -240,7 +242,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "YES"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:testor@svc-bunkerweb-db:3306/db"
|
||||
value: "postgresql://bunkerweb:changeme@svc-bunkerweb-db:5432/db"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
|
@ -303,19 +305,6 @@ spec:
|
|||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
volumeName: pv-bunkerweb
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: pvc-bunkerweb
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
volumeName: pv-bunkerweb
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
|
|
|
@ -124,7 +124,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "postgresql://bunkerweb:changeme@svc-bunkerweb-db:5432/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -150,7 +150,7 @@ spec:
|
|||
- name: KUBERNETES_MODE
|
||||
value: "yes"
|
||||
- name: "DATABASE_URI"
|
||||
value: "mariadb+pymysql://bunkerweb:changeme@svc-bunkerweb-db:3306/db"
|
||||
value: "postgresql://bunkerweb:changeme@svc-bunkerweb-db:5432/db"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -200,6 +200,8 @@ spec:
|
|||
value: "bunkerweb"
|
||||
- name: "POSTGRES_PASSWORD"
|
||||
value: "changeme"
|
||||
- name: "PGDATA"
|
||||
value: "/var/lib/postgresql/data/pgdata"
|
||||
volumeMounts:
|
||||
- mountPath: "/var/lib/postgresql/data"
|
||||
name: vol-db
|
||||
|
@ -255,4 +257,3 @@ spec:
|
|||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
volumeName: pv-bunkerweb
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
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 api = class("api")
|
||||
|
||||
|
@ -10,6 +13,32 @@ api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
|
|||
|
||||
function api:initialize()
|
||||
self.datastore = datastore:new()
|
||||
self.logger = logger:new("API")
|
||||
end
|
||||
|
||||
function api:log_cmd(cmd, status, stdout, stderr)
|
||||
local level = ngx.NOTICE
|
||||
local prefix = "success"
|
||||
if status ~= 0 then
|
||||
level = ngx.ERR
|
||||
prefix = "error"
|
||||
end
|
||||
self.logger:log(level, prefix .. " while running command " .. command)
|
||||
self.logger:log(level, "stdout = " .. stdout)
|
||||
self.logger:log(level, "stdout = " .. stderr)
|
||||
end
|
||||
|
||||
-- TODO : use this if we switch to OpenResty
|
||||
function api:cmd(cmd)
|
||||
-- Non-blocking command
|
||||
local ok, stdout, stderr, reason, status = shell.run(cmd, nil, 10000)
|
||||
self.logger:log_cmd(cmd, status, stdout, stderr)
|
||||
-- Timeout
|
||||
if ok == nil then
|
||||
return nil, reason
|
||||
end
|
||||
-- Other cases : exit 0, exit !0 and killed by signal
|
||||
return status == 0, reason, status
|
||||
end
|
||||
|
||||
function api:response(http_status, api_status, msg)
|
||||
|
@ -24,19 +53,21 @@ api.global.GET["^/ping$"] = function(self)
|
|||
end
|
||||
|
||||
api.global.POST["^/reload$"] = function(self)
|
||||
local status = os.execute("nginx -s reload")
|
||||
if status == 0 then
|
||||
return self:response(ngx.HTTP_OK, "success", "reload successful")
|
||||
-- Send HUP signal to master process
|
||||
local ok, err = rsignal.kill(process.get_master_pid(), "HUP")
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
end
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||
return self:response(ngx.HTTP_OK, "success", "reload successful")
|
||||
end
|
||||
|
||||
api.global.POST["^/stop$"] = function(self)
|
||||
local status = os.execute("nginx -s quit")
|
||||
if status == 0 then
|
||||
return self:response(ngx.HTTP_OK, "success", "stop successful")
|
||||
-- Send QUIT signal to master process
|
||||
local ok, err = rsignal.kill(process.get_master_pid(), "QUIT")
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
end
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||
return self:response(ngx.HTTP_OK, "success", "stop successful")
|
||||
end
|
||||
|
||||
api.global.POST["^/confs$"] = function(self)
|
||||
|
@ -74,13 +105,15 @@ api.global.POST["^/confs$"] = function(self)
|
|||
end
|
||||
file:flush()
|
||||
file:close()
|
||||
local status = os.execute("rm -rf " .. destination .. "/*")
|
||||
if status ~= 0 then
|
||||
return self:response(ngx.HTTP_BAD_REQUEST, "error", "can't remove old files")
|
||||
end
|
||||
status = os.execute("tar xzf " .. tmp .. " -C " .. destination)
|
||||
if status ~= 0 then
|
||||
return self:response(ngx.HTTP_BAD_REQUEST, "error", "can't extract archive")
|
||||
local cmds = {
|
||||
"rm -rf " .. destination .. "/*",
|
||||
"tar xzf " .. tmp .. " -C " .. destination
|
||||
}
|
||||
for i, 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))
|
||||
end
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", "saved data at " .. destination)
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local mlcache = require "resty.mlcache"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
|
@ -41,17 +42,24 @@ if not cache then
|
|||
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
|
||||
end
|
||||
|
||||
function cachestore:initialize(use_redis)
|
||||
function cachestore:initialize(use_redis, new_cs)
|
||||
self.cache = cache
|
||||
self.use_redis = (use_redis and utils.is_cosocket_available()) or false
|
||||
self.use_redis = use_redis or false
|
||||
self.logger = module_logger
|
||||
if new_cs then
|
||||
self.clusterstore = clusterstore:new(false)
|
||||
self.shared_cs = false
|
||||
else
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore")
|
||||
self.shared_cs = true
|
||||
end
|
||||
end
|
||||
|
||||
function cachestore:get(key)
|
||||
local callback = function(key)
|
||||
local callback = function(key, cs)
|
||||
-- Connect to redis
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local ok, err = clusterstore:connect()
|
||||
local clusterstore = cs or require "bunkerweb.clusterstore":new(false)
|
||||
local ok, err, reused = clusterstore:connect()
|
||||
if not ok then
|
||||
return nil, "can't connect to redis : " .. err, nil
|
||||
end
|
||||
|
@ -88,8 +96,12 @@ function cachestore:get(key)
|
|||
return nil, nil, -1
|
||||
end
|
||||
local value, err, hit_level
|
||||
if self.use_redis then
|
||||
value, err, hit_level = self.cache:get(key, nil, callback, key)
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local cs = nil
|
||||
if self.shared_cs then
|
||||
cs = self.clusterstore
|
||||
end
|
||||
value, err, hit_level = self.cache:get(key, nil, callback, key, cs)
|
||||
else
|
||||
value, err, hit_level = self.cache:get(key, nil, callback_no_miss)
|
||||
end
|
||||
|
@ -101,7 +113,7 @@ function cachestore:get(key)
|
|||
end
|
||||
|
||||
function cachestore:set(key, value, ex)
|
||||
if self.use_redis then
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local ok, err = self:set_redis(key, value, ex)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
|
@ -121,24 +133,23 @@ end
|
|||
|
||||
function cachestore:set_redis(key, value, ex)
|
||||
-- Connect to redis
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local ok, err = clusterstore:connect()
|
||||
local ok, err, reused = 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 = clusterstore:call("set", key, value, "EX", default_ex)
|
||||
local ok, err = self.clusterstore:call("set", key, value, "EX", default_ex)
|
||||
if err then
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return false, "SET failed : " .. err
|
||||
end
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return true
|
||||
end
|
||||
|
||||
function cachestore:delete(key, value, ex)
|
||||
if self.use_redis then
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local ok, err = self.del_redis(key)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
|
@ -153,18 +164,17 @@ end
|
|||
|
||||
function cachestore:del_redis(key)
|
||||
-- Connect to redis
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local ok, err = clusterstore:connect()
|
||||
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 = clusterstore:del(key)
|
||||
local ok, err = self.clusterstore:del(key)
|
||||
if err then
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return false, "DEL failed : " .. err
|
||||
end
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ local redis = require "resty.redis"
|
|||
|
||||
local clusterstore = class("clusterstore")
|
||||
|
||||
function clusterstore:initialize()
|
||||
function clusterstore:initialize(pool)
|
||||
-- Instantiate logger
|
||||
self.logger = logger:new("CLUSTERSTORE")
|
||||
-- Get variables
|
||||
|
@ -29,12 +29,13 @@ function clusterstore:initialize()
|
|||
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 ~= nil then
|
||||
return true, "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()
|
||||
|
@ -42,42 +43,50 @@ function clusterstore:connect()
|
|||
return false, err
|
||||
end
|
||||
-- Set timeouts
|
||||
redis_client:set_timeouts(tonumber(self.variables["REDIS_TIMEOUT"]), tonumber(self.variables["REDIS_TIMEOUT"]),
|
||||
tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||
redis_client:set_timeout(tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||
-- Connect
|
||||
local options = {
|
||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||
pool = "bw",
|
||||
pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
}
|
||||
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
|
||||
-- Save client
|
||||
self.redis_client = redis_client
|
||||
-- Select database if needed
|
||||
local times, err = redis_client:get_reused_times()
|
||||
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 = redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
||||
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"
|
||||
return true, "success", times
|
||||
end
|
||||
|
||||
function clusterstore:close()
|
||||
if self.redis_client then
|
||||
-- Equivalent to close but keep a pool of connections
|
||||
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 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"
|
||||
|
|
|
@ -146,45 +146,50 @@ helpers.call_plugin = function(plugin, method)
|
|||
end
|
||||
|
||||
helpers.fill_ctx = function()
|
||||
-- Check if ctx is already filled
|
||||
if ngx.ctx.bw then
|
||||
return true, "already filled"
|
||||
end
|
||||
-- Return errors as table
|
||||
local errors = {}
|
||||
-- Instantiate bw table
|
||||
local data = {}
|
||||
-- Common vars
|
||||
data.kind = "http"
|
||||
if ngx.shared.datastore_stream then
|
||||
data.kind = "stream"
|
||||
-- Check if ctx is already filled
|
||||
if not ngx.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.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_origin = ngx.var.http_origin
|
||||
-- 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
|
||||
ngx.ctx.bw = data
|
||||
end
|
||||
data.remote_addr = ngx.var.remote_addr
|
||||
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_origin = ngx.var.http_origin
|
||||
-- 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
|
||||
-- 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
|
||||
-- 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()
|
||||
-- Plugins
|
||||
data.plugins = {}
|
||||
-- Fill ctx
|
||||
ngx.ctx.bw = data
|
||||
ngx.ctx.bw.datastore = require "bunkerweb.datastore":new()
|
||||
ngx.ctx.bw.clusterstore = require "bunkerweb.clusterstore":new()
|
||||
ngx.ctx.bw.cachestore = require "bunkerweb.cachestore":new(use_redis == "yes")
|
||||
return true, "ctx filled", errors
|
||||
end
|
||||
|
||||
|
|
|
@ -1,17 +1,40 @@
|
|||
local class = require "middleclass"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local plugin = class("plugin")
|
||||
|
||||
function plugin:initialize(id)
|
||||
-- Store default values
|
||||
-- Store common, values
|
||||
self.id = id
|
||||
self.variables = {}
|
||||
-- Instantiate objects
|
||||
self.logger = logger:new(id)
|
||||
self.datastore = datastore:new()
|
||||
local multisite = false
|
||||
local current_phase = ngx.get_phase()
|
||||
for i, check_phase in ipairs({ "set", "access", "content", "header", "log", "preread", "log_stream", "log_default" }) do
|
||||
if current_phase == check_phase then
|
||||
multisite = true
|
||||
break
|
||||
end
|
||||
end
|
||||
self.is_request = multisite
|
||||
-- Store common objets
|
||||
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
|
||||
self.datastore = utils.get_ctx_obj("datastore") or datastore:new()
|
||||
self.cachestore = utils.get_ctx_obj("cachestore") or cachestore:new(use_redis == "yes", true)
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore") 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 encoded_metadata, err = self.datastore:get("plugin_" .. id)
|
||||
if not encoded_metadata then
|
||||
|
@ -19,16 +42,8 @@ function plugin:initialize(id)
|
|||
return
|
||||
end
|
||||
-- Store variables
|
||||
self.variables = {}
|
||||
local metadata = cjson.decode(encoded_metadata)
|
||||
local multisite = false
|
||||
local current_phase = ngx.get_phase()
|
||||
for i, check_phase in ipairs({ "set", "access", "log", "preread" }) do
|
||||
if current_phase == check_phase then
|
||||
multisite = true
|
||||
break
|
||||
end
|
||||
end
|
||||
self.is_request = multisite
|
||||
for k, v in pairs(metadata.settings) do
|
||||
local value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
||||
if value == nil then
|
||||
|
|
|
@ -12,6 +12,8 @@ local datastore = cdatastore:new()
|
|||
|
||||
local utils = {}
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
utils.get_variable = function(var, site_search)
|
||||
-- Default site search to true
|
||||
if site_search == nil then
|
||||
|
@ -363,7 +365,6 @@ utils.get_rdns = function(ip)
|
|||
for i, answer in ipairs(answers) do
|
||||
if answer.ptrdname then
|
||||
table.insert(ptrs, answer.ptrdname)
|
||||
logger:log(ngx.ERR, answer.ptrdname)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -510,22 +511,74 @@ utils.get_deny_status = function()
|
|||
return tonumber(status)
|
||||
end
|
||||
|
||||
utils.check_session = function()
|
||||
local _session, err, exists, refreshed = session.start({audience = "metadata"})
|
||||
if exists then
|
||||
for i, check in ipairs(ngx.ctx.bw.sessions_checks) do
|
||||
local key = check[1]
|
||||
local value = check[2]
|
||||
if _session:get(key) ~= value then
|
||||
local ok, err = _session:destroy()
|
||||
if not ok then
|
||||
_session:close()
|
||||
return false, "session:destroy() error : " .. err
|
||||
end
|
||||
logger:log(ngx.WARN, "session check " .. key .. " failed, destroying session")
|
||||
return utils.check_session()
|
||||
end
|
||||
end
|
||||
else
|
||||
for i, check in ipairs(ngx.ctx.bw.sessions_checks) do
|
||||
_session:set(check[1], check[2])
|
||||
end
|
||||
local ok, err = _session:save()
|
||||
if not ok then
|
||||
_session:close()
|
||||
return false, "session:save() error : " .. err
|
||||
end
|
||||
end
|
||||
ngx.ctx.bw.sessions_is_checked = true
|
||||
_session:close()
|
||||
return true, exists
|
||||
end
|
||||
|
||||
utils.get_session = function(audience)
|
||||
-- Session already in context
|
||||
if ngx.ctx.bw.session then
|
||||
ngx.ctx.bw.session:set_audience(audience)
|
||||
return ngx.ctx.bw.session
|
||||
-- Check session
|
||||
if not ngx.ctx.bw.sessions_is_checked then
|
||||
local ok, err = utils.check_session()
|
||||
if not ok then
|
||||
return false, "error while checking session, " .. err
|
||||
end
|
||||
end
|
||||
-- Open session and fill ctx
|
||||
local _session, err, exists, refreshed = session.start({ audience = audience })
|
||||
if err and err ~= "missing session cookie" and err ~= "no session" then
|
||||
logger:log(ngx.ERR, "session:start() error : " .. err)
|
||||
-- Open session with specific audience
|
||||
local _session, err, exists = session.open({audience = audience})
|
||||
if err then
|
||||
logger:log(ngx.INFO, "session:open() error : " .. err)
|
||||
end
|
||||
_session:set_audience(audience)
|
||||
ngx.ctx.bw.session = _session
|
||||
return _session
|
||||
end
|
||||
|
||||
utils.get_session_data = function(_session, site)
|
||||
local site_only = site == nil or site
|
||||
local data = _session:get_data()
|
||||
if site_only then
|
||||
return data[ngx.ctx.bw.server_name] or {}
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
utils.set_session_data = function(_session, data, site)
|
||||
local site_only = site == nil or site
|
||||
if site_only then
|
||||
local all_data = _session:get_data()
|
||||
all_data[ngx.ctx.bw.server_name] = data
|
||||
_session:set_data(all_data)
|
||||
return _session:save()
|
||||
end
|
||||
_session:set_data(data)
|
||||
return _session:save()
|
||||
end
|
||||
|
||||
utils.is_banned = function(ip)
|
||||
-- Check on local datastore
|
||||
local reason, err = datastore:get("bans_ip_" .. ip)
|
||||
|
@ -627,7 +680,7 @@ utils.new_cachestore = function()
|
|||
use_redis = use_redis == "yes"
|
||||
end
|
||||
-- Instantiate
|
||||
return require "bunkerweb.cachestore":new(use_redis)
|
||||
return require "bunkerweb.cachestore":new(use_redis, true)
|
||||
end
|
||||
|
||||
utils.regex_match = function(str, regex, options)
|
||||
|
@ -672,4 +725,20 @@ utils.is_cosocket_available = function()
|
|||
return false
|
||||
end
|
||||
|
||||
utils.kill_all_threads = function(threads)
|
||||
for i, 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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils.get_ctx_obj = function(obj)
|
||||
if ngx.ctx and ngx.ctx.bw then
|
||||
return ngx.ctx.bw[obj]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return utils
|
||||
|
|
|
@ -66,10 +66,6 @@ server {
|
|||
local order, err = datastore:get("plugins_order")
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
order = cjson.decode(order)
|
||||
|
|
|
@ -49,6 +49,10 @@ lua_shared_dict cachestore {{ CACHESTORE_MEMORY_SIZE }};
|
|||
lua_shared_dict cachestore_ipc {{ CACHESTORE_IPC_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_miss {{ CACHESTORE_MISS_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_locks {{ CACHESTORE_LOCKS_MEMORY_SIZE }};
|
||||
# only show LUA socket errors at info/debug
|
||||
{% if LOG_LEVEL != "info" and LOG_LEVEL != "debug" %}
|
||||
lua_socket_log_errors off;
|
||||
{% endif %}
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-lua.conf;
|
||||
|
|
|
@ -11,16 +11,9 @@ local logger = clogger:new("INIT")
|
|||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init phase started")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new()
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
local data_keys = {"^plugin_", "^variable_", "^plugins$", "^api_", "^misc_"}
|
||||
local data_keys = {"^plugin", "^variable_", "^api_", "^misc_"}
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
|
@ -50,6 +43,13 @@ for line in io.lines("/etc/nginx/variables.env") do
|
|||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new(false, true)
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- Set API values into the datastore
|
||||
logger:log(ngx.NOTICE, "saving API values into datastore ...")
|
||||
local value, err = datastore:get("variable_USE_API")
|
||||
|
|
|
@ -1,158 +1,157 @@
|
|||
init_by_lua_block {
|
||||
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
-- Start init phase
|
||||
local logger = clogger:new("INIT-STREAM")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init-stream phase started")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new()
|
||||
local ok, err = cachestore:purge()
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
-- Start init phase
|
||||
local logger = clogger:new("INIT")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init-stream phase started")
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
local data_keys = {"^plugin", "^variable_", "^api_", "^misc_"}
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
local data_keys = {"^plugin_", "^variable_", "^plugins$", "^api_", "^misc_"}
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "deleted old keys from datastore")
|
||||
|
||||
-- Load variables into the datastore
|
||||
logger:log(ngx.NOTICE, "saving variables into datastore ...")
|
||||
local file = io.open("/etc/nginx/variables.env")
|
||||
if not file then
|
||||
logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
|
||||
logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
file:close()
|
||||
for line in io.lines("/etc/nginx/variables.env") do
|
||||
local variable, value = line:match("^([^=]+)=(.*)$")
|
||||
local ok, err = datastore:set("variable_" .. variable, value)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save variable " .. variable .. " into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "saved variable " .. variable .. "=" .. value .. " into datastore")
|
||||
logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "deleted old keys from datastore")
|
||||
|
||||
-- Load variables into the datastore
|
||||
logger:log(ngx.NOTICE, "saving variables into datastore ...")
|
||||
local file = io.open("/etc/nginx/variables.env")
|
||||
if not file then
|
||||
logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
|
||||
return false
|
||||
end
|
||||
file:close()
|
||||
for line in io.lines("/etc/nginx/variables.env") do
|
||||
local variable, value = line:match("^([^=]+)=(.*)$")
|
||||
local ok, err = datastore:set("variable_" .. variable, value)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save variable " .. variable .. " into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Set API values into the datastore
|
||||
logger:log(ngx.NOTICE, "saving API values into datastore ...")
|
||||
local value, err = datastore:get("variable_USE_API")
|
||||
logger:log(ngx.INFO, "saved variable " .. variable .. "=" .. value .. " into datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new(false, true)
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- Set API values into the datastore
|
||||
logger:log(ngx.NOTICE, "saving API values into datastore ...")
|
||||
local value, err = datastore:get("variable_USE_API")
|
||||
if not value then
|
||||
logger:log(ngx.ERR, "can't get variable USE_API from the datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
if value == "yes" then
|
||||
local value, err = datastore:get("variable_API_WHITELIST_IP")
|
||||
if not value then
|
||||
logger:log(ngx.ERR, "can't get variable USE_API from the datastore : " .. err)
|
||||
logger:log(ngx.ERR, "can't get variable API_WHITELIST_IP from the datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
if value == "yes" then
|
||||
local value, err = datastore:get("variable_API_WHITELIST_IP")
|
||||
if not value then
|
||||
logger:log(ngx.ERR, "can't get variable API_WHITELIST_IP from the datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
local whitelists = {}
|
||||
for whitelist in value:gmatch("%S+") do
|
||||
table.insert(whitelists, whitelist)
|
||||
end
|
||||
local ok, err = datastore:set("api_whitelist_ip", cjson.encode(whitelists))
|
||||
local whitelists = {}
|
||||
for whitelist in value:gmatch("%S+") do
|
||||
table.insert(whitelists, whitelist)
|
||||
end
|
||||
local ok, err = datastore:set("api_whitelist_ip", cjson.encode(whitelists))
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save API whitelist_ip to datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "saved API whitelist_ip into datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved API values into datastore")
|
||||
|
||||
-- Load plugins into the datastore
|
||||
logger:log(ngx.NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = {"/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins"}
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
local ok, plugin = helpers.load_plugin(path .. "/plugin.json")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save API whitelist_ip to datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "saved API whitelist_ip into datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved API values into datastore")
|
||||
|
||||
-- Load plugins into the datastore
|
||||
logger:log(ngx.NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = {"/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins"}
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
local ok, plugin = helpers.load_plugin(path .. "/plugin.json")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin)
|
||||
else
|
||||
local ok, err = datastore:set("plugin_" .. plugin.id, cjson.encode(plugin))
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
else
|
||||
table.insert(plugins, plugin)
|
||||
logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local ok, err = datastore:set("plugins", cjson.encode(plugins))
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
logger:log(ngx.NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = helpers.order_plugins(plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't compute plugins order : " .. err)
|
||||
return false
|
||||
end
|
||||
for phase, id_list in pairs(order) do
|
||||
logger:log(ngx.NOTICE, "plugins order for phase " .. phase .. " : " .. cjson.encode(id_list))
|
||||
end
|
||||
local ok, err = datastore:set("plugins_order", cjson.encode(order))
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins order into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved plugins order into datastore")
|
||||
|
||||
-- Call init() method
|
||||
logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order["init"]) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.NOTICE, err)
|
||||
logger:log(ngx.ERR, plugin)
|
||||
else
|
||||
-- Check if plugin has init method
|
||||
if plugin_lua.init ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
local ok, err = datastore:set("plugin_" .. plugin.id, cjson.encode(plugin))
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
else
|
||||
logger:log(ngx.NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
table.insert(plugins, plugin)
|
||||
logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.NOTICE, "called init() methods of plugins")
|
||||
|
||||
logger:log(ngx.NOTICE, "init-stream phase ended")
|
||||
|
||||
}
|
||||
|
||||
end
|
||||
local ok, err = datastore:set("plugins", cjson.encode(plugins))
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
logger:log(ngx.NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = helpers.order_plugins(plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't compute plugins order : " .. err)
|
||||
return false
|
||||
end
|
||||
for phase, id_list in pairs(order) do
|
||||
logger:log(ngx.NOTICE, "plugins order for phase " .. phase .. " : " .. cjson.encode(id_list))
|
||||
end
|
||||
local ok, err = datastore:set("plugins_order", cjson.encode(order))
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins order into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved plugins order into datastore")
|
||||
|
||||
-- Call init() method
|
||||
logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order["init"]) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.NOTICE, err)
|
||||
else
|
||||
-- Check if plugin has init method
|
||||
if plugin_lua.init ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.NOTICE, "called init() methods of plugins")
|
||||
|
||||
logger:log(ngx.NOTICE, "init-stream phase ended")
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ local ready_work = function(premature)
|
|||
end
|
||||
|
||||
-- Instantiate lock
|
||||
local lock = require "resty.lock":new("worker_lock")
|
||||
local lock = require "resty.lock":new("worker_lock", {timeout = 10})
|
||||
if not lock then
|
||||
logger:log(ngx.ERR, "lock:new() failed : " .. err)
|
||||
return
|
||||
|
|
|
@ -46,10 +46,6 @@ end
|
|||
local order, err = datastore:get("plugins_order")
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
order = cjson.decode(order)
|
||||
|
|
|
@ -27,10 +27,6 @@ logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
|||
local order, err = datastore:get("plugins_order")
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
order = cjson.decode(order)
|
||||
|
|
|
@ -27,10 +27,6 @@ logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
|||
local order, err = datastore:get("plugins_order")
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
order = cjson.decode(order)
|
||||
|
|
|
@ -42,10 +42,6 @@ logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
|||
local order, err = datastore:get("plugins_order")
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
order = cjson.decode(order)
|
||||
|
|
|
@ -27,10 +27,6 @@ logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
|
|||
local order, err = datastore:get("plugins_order")
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
order = cjson.decode(order)
|
||||
|
@ -45,7 +41,7 @@ for i, plugin_id in ipairs(order.log_stream) do
|
|||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
else
|
||||
-- Check if plugin has log method
|
||||
-- Check if plugin has log_stream method
|
||||
if plugin_lua.log_stream ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
|
|
|
@ -77,7 +77,7 @@ for i, plugin_id in ipairs(order.preread) do
|
|||
if ret.status then
|
||||
if ret.status == utils.get_deny_status() then
|
||||
ngx.ctx.reason = plugin_id
|
||||
logger:log(ngx.WARN, "denied access from " .. plugin_id .. " : " .. ret.msg)
|
||||
logger:log(ngx.WARN, "denied preread from " .. plugin_id .. " : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. " returned status " .. tostring(ret.status) .. " : " .. ret.msg)
|
||||
end
|
||||
|
|
|
@ -33,6 +33,10 @@ lua_shared_dict cachestore_stream {{ CACHESTORE_MEMORY_SIZE }};
|
|||
lua_shared_dict cachestore_ipc_stream {{ CACHESTORE_IPC_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_miss_stream {{ CACHESTORE_MISS_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_locks_stream {{ CACHESTORE_LOCKS_MEMORY_SIZE }};
|
||||
# only show LUA socket errors at info/debug
|
||||
{% if LOG_LEVEL != "info" and LOG_LEVEL != "debug" %}
|
||||
lua_socket_log_errors off;
|
||||
{% endif %}
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-stream-lua.conf;
|
||||
|
|
|
@ -26,9 +26,15 @@ function antibot:access()
|
|||
return self:ret(true, "antibot not activated")
|
||||
end
|
||||
|
||||
-- Get session and data
|
||||
self.session = utils.get_session("antibot")
|
||||
self:get_session_data()
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot")
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session)
|
||||
-- Check if session is valid
|
||||
self:check_session()
|
||||
|
||||
-- Don't go further if client resolved the challenge
|
||||
if self.session_data.resolved then
|
||||
|
@ -50,6 +56,11 @@ function antibot:access()
|
|||
return self:ret(true, "redirecting client to the challenge uri", nil, self.variables["ANTIBOT_URI"])
|
||||
end
|
||||
|
||||
-- Cookie case : don't display challenge page
|
||||
if self.session_data.resolved then
|
||||
return self:ret(true, "client already resolved the challenge", nil, self.session_data.original_uri)
|
||||
end
|
||||
|
||||
-- Display challenge needed
|
||||
if ngx.ctx.bw.request_method == "GET" then
|
||||
ngx.ctx.bw.antibot_display_content = true
|
||||
|
@ -89,13 +100,25 @@ function antibot:content()
|
|||
if self.variables["USE_ANTIBOT"] == "no" then
|
||||
return self:ret(true, "antibot not activated")
|
||||
end
|
||||
|
||||
-- Check if display content is needed
|
||||
if not ngx.ctx.bw.antibot_display_content then
|
||||
return self:ret(true, "display content not needed", nil, "/")
|
||||
end
|
||||
-- Get session and data
|
||||
self.session = utils.get_session("antibot")
|
||||
self:get_session_data(true)
|
||||
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot")
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session)
|
||||
|
||||
-- Direct access without session
|
||||
if not self.session_data.prepared then
|
||||
return self:ret(true, "no session", nil, "/")
|
||||
end
|
||||
|
||||
-- Display content
|
||||
local ok, err = self:display_challenge()
|
||||
if not ok then
|
||||
|
@ -104,50 +127,42 @@ function antibot:content()
|
|||
return self:ret(true, "content displayed")
|
||||
end
|
||||
|
||||
function antibot:get_session_data(no_check)
|
||||
local session_data = self.session:get_data()
|
||||
if session_data[ngx.ctx.bw.server_name] then
|
||||
local data = cjson.decode(session_data[ngx.ctx.bw.server_name])
|
||||
if no_check then
|
||||
self.session_data = data
|
||||
return
|
||||
end
|
||||
if not data.time_resolve and not data.time_valid then
|
||||
self.session_data = {}
|
||||
self.session_updated = true
|
||||
return
|
||||
end
|
||||
local time = ngx.now()
|
||||
self.session_data = data
|
||||
-- Check valid time
|
||||
if data.resolved and (data.time_valid > time or time - data.time_valid > tonumber(self.variables["ANTIBOT_TIME_VALID"])) then
|
||||
self.session_data.resolved = false
|
||||
self.session_data.prepared = false
|
||||
self.session_updated = true
|
||||
return
|
||||
end
|
||||
-- Check resolve time
|
||||
if not data.resolved and (data.time_resolve > time or time - data.time_resolve > tonumber(self.variables["ANTIBOT_TIME_RESOLVE"])) then
|
||||
self.session_data.prepared = false
|
||||
self.session_updated = true
|
||||
return
|
||||
end
|
||||
-- Session is valid
|
||||
function antibot:check_session()
|
||||
-- Get values
|
||||
local time_resolve = self.session_data.time_resolve
|
||||
local time_valid = self.session_data.time_valid
|
||||
-- Not resolved and not prepared
|
||||
if not time_resolve and not time_valid then
|
||||
self.session_data = {}
|
||||
self.session_updated = true
|
||||
return
|
||||
end
|
||||
-- Check if still valid
|
||||
local time = ngx.now()
|
||||
local resolved = self.session_data.resolved
|
||||
if resolved and (time_valid > time or time - time_valid > tonumber(self.variables["ANTIBOT_TIME_VALID"])) then
|
||||
self.session_data = {}
|
||||
self.session_updated = true
|
||||
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
|
||||
self.session_data = {}
|
||||
self.session_updated = true
|
||||
return
|
||||
end
|
||||
self.session_data = {}
|
||||
self.session_updated = true
|
||||
return
|
||||
end
|
||||
|
||||
function antibot:set_session_data()
|
||||
if self.session_updated then
|
||||
local session_data = self.session:get_data()
|
||||
session_data[ngx.ctx.bw.server_name] = cjson.encode(self.session_data)
|
||||
self.session:set_data(session_data)
|
||||
return self.session:save()
|
||||
local ok, err = utils.set_session_data(self.session, self.session_data)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
self.session_updated = false
|
||||
return true, "updated"
|
||||
end
|
||||
return true, "no updates"
|
||||
return true, "no update"
|
||||
end
|
||||
|
||||
function antibot:prepare_challenge()
|
||||
|
|
|
@ -7,12 +7,6 @@ local badbehavior = class("badbehavior", plugin)
|
|||
function badbehavior:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "badbehavior")
|
||||
-- Check if redis is enabled
|
||||
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"
|
||||
end
|
||||
|
||||
function badbehavior:log()
|
||||
|
@ -146,7 +140,7 @@ end
|
|||
|
||||
function badbehavior.redis_increase(ip, count_time, ban_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_incr = redis.pcall("INCR", KEYS[1])
|
||||
|
@ -188,7 +182,7 @@ end
|
|||
|
||||
function badbehavior.redis_decrease(ip, count_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_decr = redis.pcall("DECR", KEYS[1])
|
||||
|
|
|
@ -11,12 +11,6 @@ local blacklist = class("blacklist", plugin)
|
|||
function blacklist:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "blacklist")
|
||||
-- Check if redis is enabled
|
||||
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"
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_blacklist_lists")
|
||||
|
@ -47,8 +41,6 @@ function blacklist:initialize()
|
|||
end
|
||||
end
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function blacklist:is_needed()
|
||||
|
@ -267,20 +259,21 @@ function blacklist:is_blacklisted_ip()
|
|||
if ngx.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(ngx.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
return nil, "ASN " .. err
|
||||
end
|
||||
local ignore = false
|
||||
for i, ignore_asn in ipairs(self.lists["IGNORE_ASN"]) do
|
||||
if ignore_asn == tostring(asn) then
|
||||
ignore = true
|
||||
break
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. ngx.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
local ignore = false
|
||||
for i, ignore_asn in ipairs(self.lists["IGNORE_ASN"]) do
|
||||
if ignore_asn == tostring(asn) then
|
||||
ignore = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Check if ASN is in blacklist
|
||||
if not ignore then
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
-- Check if ASN is in blacklist
|
||||
if not ignore then
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"help": "Value of the Cache-Control HTTP header.",
|
||||
"id": "client-cache-control",
|
||||
"label": "Cache-Control header",
|
||||
"regex": "^(?!(, ?| ))((, )?(((max-age|s-maxage|stale-while-revalidate|stale-if-error)=[0-9]+(?!.*6))|((?!.*public)private|(?!.*private)public)|(must|proxy)-revalidate|must-understand|immutable|no-(cache|store|transform)))+$",
|
||||
"regex": "^(?!(, ?| ))((, )?(((max-age|s-maxage|stale-while-revalidate|stale-if-error)=\\d+(?!.*\\6))|((?!.*public)private|(?!.*private)public)|(must|proxy)-revalidate|must-understand|immutable|no-(cache|store|transform))(?!.*\\4))+$",
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,6 @@ local country = class("country", plugin)
|
|||
function country:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "country")
|
||||
-- Instantiate cachestore
|
||||
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"
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function country:access()
|
||||
|
|
|
@ -10,13 +10,6 @@ local dnsbl = class("dnsbl", plugin)
|
|||
function dnsbl:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "dnsbl")
|
||||
-- Instantiate cachestore
|
||||
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"
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function dnsbl:init_worker()
|
||||
|
@ -32,9 +25,18 @@ function dnsbl:init_worker()
|
|||
return self:ret(true, "no service uses DNSBL, skipping init_worker")
|
||||
end
|
||||
-- Loop on DNSBL list
|
||||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
local result, err = self:is_in_dnsbl("127.0.0.2", server)
|
||||
if result == nil then
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, "127.0.0.2", server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
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)
|
||||
elseif result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
elseif not result then
|
||||
self.logger:log(ngx.ERR, "dnsbl check for " .. server .. " failed")
|
||||
|
@ -69,25 +71,74 @@ function dnsbl:access()
|
|||
utils.get_deny_status())
|
||||
end
|
||||
-- Loop on DNSBL list
|
||||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
local result, err = self:is_in_dnsbl(ngx.ctx.bw.remote_addr, server)
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, ngx.ctx.bw.remote_addr, server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
local ret_threads = nil
|
||||
local ret_err = nil
|
||||
local ret_server = nil
|
||||
while true do
|
||||
-- Compute threads to wait
|
||||
local wait_threads = {}
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
-- No server reported IP
|
||||
if #wait_threads == 0 then
|
||||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
local ok, result, server, err = ngx.thread.wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
ret_err = "error while waiting thread : " .. result
|
||||
break
|
||||
end
|
||||
-- Remove thread from list
|
||||
threads[server] = nil
|
||||
-- DNS error
|
||||
if result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
end
|
||||
-- IP is in DNSBL
|
||||
if result then
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr, server)
|
||||
ret_threads = true
|
||||
ret_err = "IP is blacklisted by " .. server
|
||||
ret_server = server
|
||||
break
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
-- Kill other threads
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Blacklisted by a server : add to cache and deny access
|
||||
if ret_threads then
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr, ret_server)
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding element to cache : " .. err)
|
||||
end
|
||||
return self:ret(true, "IP is blacklisted by " .. server, utils.get_deny_status())
|
||||
return self:ret(true, "IP is blacklisted by " .. ret_server, utils.get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- IP is not in DNSBL
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr, "ok")
|
||||
if not ok then
|
||||
return self:ret(false, "IP is not in DNSBL (error = " .. err .. ")")
|
||||
end
|
||||
return self:ret(true, "IP is not in DNSBL", false, nil)
|
||||
return self:ret(true, "IP is not in DNSBL")
|
||||
end
|
||||
|
||||
function dnsbl:preread()
|
||||
|
@ -114,14 +165,14 @@ 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, err
|
||||
return nil, server, err
|
||||
end
|
||||
for i, ip in ipairs(ips) do
|
||||
if ip:find("^127%.0%.0%.") then
|
||||
return true, "success"
|
||||
return true, server
|
||||
end
|
||||
end
|
||||
return false, "success"
|
||||
return false, server
|
||||
end
|
||||
|
||||
return dnsbl
|
||||
|
|
|
@ -10,12 +10,6 @@ local greylist = class("greylist", plugin)
|
|||
function greylist:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "greylist")
|
||||
-- Check if redis is enabled
|
||||
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"
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_greylist_lists")
|
||||
|
@ -41,8 +35,6 @@ function greylist:initialize()
|
|||
end
|
||||
end
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function greylist:is_needed()
|
||||
|
@ -216,11 +208,12 @@ function greylist:is_greylisted_ip()
|
|||
if ngx.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(ngx.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
return nil, "ASN " .. err
|
||||
end
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. ngx.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,13 +10,6 @@ local limit = class("limit", plugin)
|
|||
function limit:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "limit")
|
||||
-- Check if redis is enabled
|
||||
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"
|
||||
self.clusterstore = clusterstore:new()
|
||||
-- Load rules if needed
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
-- Get all rules from datastore
|
||||
|
|
|
@ -9,7 +9,6 @@ local redis = class("redis", plugin)
|
|||
function redis:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "redis")
|
||||
self.clusterstore = clusterstore:new()
|
||||
end
|
||||
|
||||
function redis:init_worker()
|
||||
|
|
|
@ -2,19 +2,13 @@ local class = require "middleclass"
|
|||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local reversescan = class("reversescan", plugin)
|
||||
|
||||
function reversescan:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "reversescan")
|
||||
-- Instantiate cachestore
|
||||
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"
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function reversescan:access()
|
||||
|
@ -23,31 +17,103 @@ function reversescan:access()
|
|||
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(ngx.ctx.bw.remote_addr .. ":" .. port)
|
||||
if not ok then
|
||||
return self:ret(false, "error getting cache from datastore : " .. cached)
|
||||
end
|
||||
if cached == "open" then
|
||||
return self:ret(true, "port " .. port .. " is opened for IP " .. ngx.ctx.bw.remote_addr,
|
||||
utils.get_deny_status())
|
||||
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 " .. ngx.ctx.bw.remote_addr
|
||||
break
|
||||
-- Perform scan in a thread
|
||||
elseif not cached then
|
||||
-- Do the scan
|
||||
local res = self:scan(ngx.ctx.bw.remote_addr, tonumber(port),
|
||||
tonumber(self.variables["REVERSE_SCAN_TIMEOUT"]))
|
||||
-- Cache the result
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr .. ":" .. port, res)
|
||||
if not ok then
|
||||
return self:ret(false, "error updating cache from datastore : " .. err)
|
||||
end
|
||||
-- Deny request if port is open
|
||||
if res == "open" then
|
||||
return self:ret(true, "port " .. port .. " is opened for IP " .. ngx.ctx.bw.remote_addr,
|
||||
utils.get_deny_status())
|
||||
end
|
||||
local thread = ngx.thread.spawn(self.scan, ngx.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())
|
||||
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 " .. ngx.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(ngx.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())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- No port opened
|
||||
return self:ret(true, "no port open for IP " .. ngx.ctx.bw.remote_addr)
|
||||
end
|
||||
|
@ -56,15 +122,15 @@ function reversescan:preread()
|
|||
return self:access()
|
||||
end
|
||||
|
||||
function reversescan:scan(ip, port, timeout)
|
||||
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 "close"
|
||||
return false, port
|
||||
end
|
||||
return "open"
|
||||
return true, port
|
||||
end
|
||||
|
||||
function reversescan:is_in_cache(ip_port)
|
||||
|
|
|
@ -46,9 +46,27 @@
|
|||
"default": "86400",
|
||||
"help": "Maximum time (in seconds) before a session is destroyed.",
|
||||
"id": "sessions-absolute-timeout",
|
||||
"label": "SessionS absolute timeout",
|
||||
"label": "Sessions absolute timeout",
|
||||
"regex": "^\\d+$",
|
||||
"type": "text"
|
||||
},
|
||||
"SESSIONS_CHECK_IP": {
|
||||
"context": "global",
|
||||
"default": "yes",
|
||||
"help": "Destroy session if IP address is different than original one.",
|
||||
"id": "sessions-check-ip",
|
||||
"label": "Sessions check IP",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"SESSIONS_CHECK_USER_AGENT": {
|
||||
"context": "global",
|
||||
"default": "yes",
|
||||
"help": "Destroy session if User-Agent is different than original one.",
|
||||
"id": "sessions-user-agent",
|
||||
"label": "Sessions check User-Agent",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,37 @@ local sessions = class("sessions", plugin)
|
|||
function sessions:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "sessions")
|
||||
-- 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
|
||||
end
|
||||
|
||||
function sessions:set()
|
||||
if self.is_loading or self.kind ~= "http" then
|
||||
return self:ret(true, "set not needed")
|
||||
end
|
||||
local checks = {
|
||||
["IP"] = ngx.ctx.bw.remote_addr,
|
||||
["USER_AGENT"] = ngx.ctx.bw.http_user_agent or ""
|
||||
}
|
||||
ngx.ctx.bw.sessions_checks = {}
|
||||
for check, value in pairs(checks) do
|
||||
if self.variables["SESSIONS_CHECK_" .. check] == "yes" then
|
||||
table.insert(ngx.ctx.bw.sessions_checks, {check, value})
|
||||
end
|
||||
end
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
function sessions:init()
|
||||
|
@ -41,10 +72,26 @@ function sessions:init()
|
|||
absolute_timeout = tonumber(self.variables["SESSIONS_ABSOLUTE_TIMEOUT"])
|
||||
}
|
||||
if self.variables["SESSIONS_SECRET"] == "random" then
|
||||
config.secret = utils.rand(16)
|
||||
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
|
||||
config.cookie_name = utils.rand(16)
|
||||
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"
|
||||
|
@ -56,7 +103,7 @@ function sessions:init()
|
|||
send_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
read_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
keepalive_timeout = tonumber(redis_vars["REDIS_KEEPALIVE_IDLE"]),
|
||||
pool = "bw",
|
||||
pool = "bw-redis",
|
||||
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
|
||||
ssl = redis_vars["REDIS_SSL"] == "yes",
|
||||
host = redis_vars["REDIS_HOST"],
|
||||
|
|
|
@ -12,12 +12,6 @@ local whitelist = class("whitelist", plugin)
|
|||
function whitelist:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "whitelist")
|
||||
-- Check if redis is enabled
|
||||
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"
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_whitelist_lists")
|
||||
|
@ -43,8 +37,6 @@ function whitelist:initialize()
|
|||
end
|
||||
end
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function whitelist:is_needed()
|
||||
|
@ -271,7 +263,6 @@ function whitelist:is_whitelisted_ip()
|
|||
end
|
||||
end
|
||||
if forward_check then
|
||||
local forward_ok = false
|
||||
local ip_list, err = utils.get_ips(forward_check)
|
||||
if ip_list then
|
||||
for i, ip in ipairs(ip_list) do
|
||||
|
@ -293,11 +284,12 @@ function whitelist:is_whitelisted_ip()
|
|||
if ngx.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(ngx.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
return nil, "ASN " .. err
|
||||
end
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. ngx.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -265,13 +265,13 @@ if [ "$dopatch" = "yes" ] ; then
|
|||
do_and_check_cmd cp deps/misc/lua-pack.Makefile deps/src/lua-pack/Makefile
|
||||
fi
|
||||
|
||||
# lua-resty-openssl v0.8.21
|
||||
# lua-resty-openssl v0.8.22
|
||||
echo "ℹ️ Downloading lua-resty-openssl"
|
||||
dopatch="no"
|
||||
if [ ! -d "deps/src/lua-resty-openssl" ] ; then
|
||||
dopatch="yes"
|
||||
fi
|
||||
git_secure_clone "https://github.com/fffonion/lua-resty-openssl.git" "15bc59b97feb5acf25fbdd9426cf73870cf7c838"
|
||||
git_secure_clone "https://github.com/fffonion/lua-resty-openssl.git" "484907935e60273d31626ac849b23a2d218173de"
|
||||
if [ "$dopatch" == "yes" ] ; then
|
||||
do_and_check_cmd rm -r deps/src/lua-resty-openssl/t
|
||||
fi
|
||||
|
@ -287,6 +287,10 @@ if [ "$dopatch" = "yes" ] ; then
|
|||
do_and_check_cmd patch deps/src/lua-ffi-zlib/lib/ffi-zlib.lua deps/misc/lua-ffi-zlib.patch
|
||||
fi
|
||||
|
||||
# lua-resty-signal v0.03
|
||||
echo "ℹ️ Downloading lua-resty-signal"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-signal.git" "d07163e8cfa673900e66048cd2a1f18523aecf16"
|
||||
|
||||
# ModSecurity v3.0.9
|
||||
echo "ℹ️ Downloading ModSecurity"
|
||||
dopatch="no"
|
||||
|
|
|
@ -154,6 +154,11 @@ do_and_check_cmd cp /tmp/bunkerweb/deps/src/lua-resty-openssl/lib/resty/openssl.
|
|||
echo "ℹ️ Installing lua-ffi-zlib"
|
||||
do_and_check_cmd cp /tmp/bunkerweb/deps/src/lua-ffi-zlib/lib/ffi-zlib.lua /usr/share/bunkerweb/deps/lib/lua
|
||||
|
||||
# Installing lua-resty-signal
|
||||
echo "ℹ️ Installing lua-resty-signal"
|
||||
CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-signal" do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps -j $NTASK
|
||||
CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-signal" do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
|
||||
|
||||
# Compile dynamic modules
|
||||
echo "ℹ️ Compiling and installing dynamic modules"
|
||||
CONFARGS="$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p')"
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
## [Unreleased]
|
||||
|
||||
|
||||
<a name="0.8.22"></a>
|
||||
## [0.8.22] - 2023-04-26
|
||||
### bug fixes
|
||||
- **crypto:** use OPENSSL_free in BoringSSL ([#107](https://github.com/fffonion/lua-resty-openssl/issues/107)) [7830212](https://github.com/fffonion/lua-resty-openssl/commit/78302123ac744f2d0b6de1156e459e9ea72b7edb)
|
||||
|
||||
|
||||
<a name="0.8.21"></a>
|
||||
## [0.8.21] - 2023-03-24
|
||||
### features
|
||||
|
@ -491,7 +497,8 @@
|
|||
- **x509:** export pubkey [ede4f81](https://github.com/fffonion/lua-resty-openssl/commit/ede4f817cb0fe092ad6f9ab5d6ecdcde864a9fd8)
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.21...HEAD
|
||||
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.22...HEAD
|
||||
[0.8.22]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.21...0.8.22
|
||||
[0.8.21]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.20...0.8.21
|
||||
[0.8.20]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.19...0.8.20
|
||||
[0.8.19]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.18...0.8.19
|
||||
|
|
|
@ -11,6 +11,7 @@ all: ;
|
|||
|
||||
install: all
|
||||
cp -rpv lib/resty/openssl/. $(DESTDIR)$(LUA_LIB_DIR)/resty/openssl
|
||||
cp -pv lib/resty/openssl.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/
|
||||
|
||||
test: all
|
||||
PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t
|
||||
|
|
|
@ -25,7 +25,7 @@ try_require_modules()
|
|||
|
||||
|
||||
local _M = {
|
||||
_VERSION = '0.8.21',
|
||||
_VERSION = '0.8.22',
|
||||
}
|
||||
|
||||
local libcrypto_name
|
||||
|
|
|
@ -3,6 +3,7 @@ local C = ffi.C
|
|||
|
||||
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
|
||||
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
|
||||
local BORINGSSL = require("resty.openssl.version").BORINGSSL
|
||||
|
||||
local OPENSSL_free
|
||||
if OPENSSL_10 then
|
||||
|
@ -10,6 +11,11 @@ if OPENSSL_10 then
|
|||
void CRYPTO_free(void *ptr);
|
||||
]]
|
||||
OPENSSL_free = C.CRYPTO_free
|
||||
elseif BORINGSSL then
|
||||
ffi.cdef [[
|
||||
void OPENSSL_free(void *ptr);
|
||||
]]
|
||||
OPENSSL_free = C.OPENSSL_free
|
||||
elseif OPENSSL_11_OR_LATER then
|
||||
ffi.cdef [[
|
||||
void CRYPTO_free(void *ptr, const char *file, int line);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package = "lua-resty-openssl"
|
||||
version = "0.8.21-1"
|
||||
version = "0.8.22-1"
|
||||
source = {
|
||||
url = "git+https://github.com/fffonion/lua-resty-openssl.git",
|
||||
tag = "0.8.21"
|
||||
tag = "0.8.22"
|
||||
}
|
||||
description = {
|
||||
detailed = "FFI-based OpenSSL binding for LuaJIT.",
|
|
@ -0,0 +1,9 @@
|
|||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
t/servroot*
|
||||
/go
|
||||
/reindex
|
||||
/a.lua
|
||||
*.o
|
||||
*.so
|
|
@ -0,0 +1,46 @@
|
|||
sudo: required
|
||||
dist: xenial
|
||||
|
||||
os: linux
|
||||
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
env:
|
||||
global:
|
||||
- JOBS=3
|
||||
- NGX_BUILD_JOBS=$JOBS
|
||||
- LUAJIT_PREFIX=/opt/luajit21
|
||||
- LUAJIT_LIB=$LUAJIT_PREFIX/lib
|
||||
- LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
|
||||
- LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
|
||||
matrix:
|
||||
- NGINX_VERSION=1.19.3
|
||||
|
||||
install:
|
||||
- sudo apt-get install -qq -y cpanminus axel
|
||||
- sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- git clone https://github.com/openresty/openresty.git ../openresty
|
||||
- git clone https://github.com/openresty/nginx-devel-utils.git
|
||||
- git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
|
||||
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
|
||||
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
|
||||
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
|
||||
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
|
||||
- git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git
|
||||
|
||||
script:
|
||||
- make
|
||||
- cd luajit2/
|
||||
- make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- cd ..
|
||||
- export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH
|
||||
- export NGX_BUILD_CC=$CC
|
||||
- ngx-build $NGINX_VERSION --with-http_realip_module --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --with-debug > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- nginx -V
|
||||
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
|
||||
- prove -r t
|
|
@ -0,0 +1,46 @@
|
|||
OPENRESTY_PREFIX=/usr/local/openresty
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
LUA_INCLUDE_DIR ?= $(PREFIX)/include
|
||||
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
|
||||
INSTALL ?= install
|
||||
|
||||
.PHONY: all test install
|
||||
|
||||
SRC := resty_signal.c
|
||||
OBJ := $(SRC:.c=.o)
|
||||
|
||||
C_SO_NAME := librestysignal.so
|
||||
|
||||
CFLAGS := -O3 -g -Wall -fpic
|
||||
|
||||
LDFLAGS := -shared
|
||||
# on Mac OS X, one should set instead:
|
||||
# LDFLAGS := -bundle -undefined dynamic_lookup
|
||||
|
||||
MY_CFLAGS := $(CFLAGS)
|
||||
MY_LDFLAGS := $(LDFLAGS) -fvisibility=hidden
|
||||
|
||||
test := t
|
||||
|
||||
.PHONY = all test clean install
|
||||
|
||||
all : $(C_SO_NAME)
|
||||
|
||||
${OBJ} : %.o : %.c
|
||||
$(CC) $(MY_CFLAGS) -c $<
|
||||
|
||||
${C_SO_NAME} : ${OBJ}
|
||||
$(CC) $(MY_LDFLAGS) $^ -o $@
|
||||
|
||||
#export TEST_NGINX_NO_CLEAN=1
|
||||
|
||||
clean:; rm -f *.o *.so a.out *.d
|
||||
|
||||
install:
|
||||
$(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/resty
|
||||
$(INSTALL) lib/resty/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty
|
||||
$(INSTALL) $(C_SO_NAME) $(DESTDIR)$(LUA_LIB_DIR)/
|
||||
|
||||
test : all
|
||||
PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r $(test)
|
|
@ -0,0 +1,132 @@
|
|||
Name
|
||||
====
|
||||
|
||||
lua-resty-signal - Lua library for killing or sending signals to Linux processes
|
||||
|
||||
Table of Contents
|
||||
=================
|
||||
|
||||
* [Name](#name)
|
||||
* [Synopsis](#synopsis)
|
||||
* [Functions](#functions)
|
||||
* [kill](#kill)
|
||||
* [signum](#signum)
|
||||
* [Author](#author)
|
||||
* [Copyright & Licenses](#copyright--licenses)
|
||||
|
||||
Synopsis
|
||||
========
|
||||
|
||||
```lua
|
||||
local resty_signal = require "resty.signal"
|
||||
local pid = 12345
|
||||
|
||||
local ok, err = resty_signal.kill(pid, "TERM")
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "failed to kill process of pid ", pid, ": ", err)
|
||||
return
|
||||
end
|
||||
|
||||
-- send the signal 0 to check the existence of a process
|
||||
local ok, err = resty_signal.kill(pid, "NONE")
|
||||
|
||||
local ok, err = resty_signal.kill(pid, "HUP")
|
||||
|
||||
local ok, err = resty_signal.kill(pid, "KILL")
|
||||
```
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
||||
kill
|
||||
----
|
||||
|
||||
**syntax:** `ok, err = resty_signal.kill(pid, signal_name_or_num)`
|
||||
|
||||
Sends a signal with its name string or number value to the process of the
|
||||
specified pid.
|
||||
|
||||
All signal names accepted by [signum](#signum) are supported, like `HUP`,
|
||||
`KILL`, and `TERM`.
|
||||
|
||||
Signal numbers are also supported when specifying nonportable system-specific
|
||||
signals is desired.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
signum
|
||||
------
|
||||
|
||||
**syntax:** `num = resty_signal.signum(sig_name)`
|
||||
|
||||
Maps the signal name specified to the system-specific signal number. Returns
|
||||
`nil` if the signal name is not known.
|
||||
|
||||
All the POSIX and BSD signal names are supported:
|
||||
|
||||
```
|
||||
HUP
|
||||
INT
|
||||
QUIT
|
||||
ILL
|
||||
TRAP
|
||||
ABRT
|
||||
BUS
|
||||
FPE
|
||||
KILL
|
||||
USR1
|
||||
SEGV
|
||||
USR2
|
||||
PIPE
|
||||
ALRM
|
||||
TERM
|
||||
CHLD
|
||||
CONT
|
||||
STOP
|
||||
TSTP
|
||||
TTIN
|
||||
TTOU
|
||||
URG
|
||||
XCPU
|
||||
XFSZ
|
||||
VTALRM
|
||||
PROF
|
||||
WINCH
|
||||
IO
|
||||
PWR
|
||||
EMT
|
||||
SYS
|
||||
INFO
|
||||
```
|
||||
|
||||
The special signal name `NONE` is also supported, which is mapped to zero (0).
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Author
|
||||
======
|
||||
|
||||
Yichun Zhang (agentzh) <yichun@openresty.com>
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Copyright & Licenses
|
||||
====================
|
||||
|
||||
This module is licensed under the BSD license.
|
||||
|
||||
Copyright (C) 2018-2019, [OpenResty Inc.](https://openresty.com)
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
|
@ -0,0 +1,156 @@
|
|||
local _M = {
|
||||
version = 0.03
|
||||
}
|
||||
|
||||
|
||||
local ffi = require "ffi"
|
||||
local base = require "resty.core.base"
|
||||
|
||||
|
||||
local C = ffi.C
|
||||
local ffi_str = ffi.string
|
||||
local tonumber = tonumber
|
||||
local assert = assert
|
||||
local errno = ffi.errno
|
||||
local type = type
|
||||
local new_tab = base.new_tab
|
||||
local error = error
|
||||
local string_format = string.format
|
||||
|
||||
|
||||
local load_shared_lib
|
||||
do
|
||||
local string_gmatch = string.gmatch
|
||||
local string_match = string.match
|
||||
local io_open = io.open
|
||||
local io_close = io.close
|
||||
|
||||
local cpath = package.cpath
|
||||
|
||||
function load_shared_lib(so_name)
|
||||
local tried_paths = new_tab(32, 0)
|
||||
local i = 1
|
||||
|
||||
for k, _ in string_gmatch(cpath, "[^;]+") do
|
||||
local fpath = string_match(k, "(.*/)")
|
||||
fpath = fpath .. so_name
|
||||
-- Don't get me wrong, the only way to know if a file exist is
|
||||
-- trying to open it.
|
||||
local f = io_open(fpath)
|
||||
if f ~= nil then
|
||||
io_close(f)
|
||||
return ffi.load(fpath)
|
||||
end
|
||||
|
||||
tried_paths[i] = fpath
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return nil, tried_paths
|
||||
end -- function
|
||||
end -- do
|
||||
|
||||
|
||||
local resty_signal, tried_paths = load_shared_lib("librestysignal.so")
|
||||
if not resty_signal then
|
||||
error("could not load librestysignal.so from the following paths:\n" ..
|
||||
table.concat(tried_paths, "\n"), 2)
|
||||
end
|
||||
|
||||
|
||||
ffi.cdef[[
|
||||
int resty_signal_signum(int num);
|
||||
]]
|
||||
|
||||
|
||||
if not pcall(function () return C.kill end) then
|
||||
ffi.cdef("int kill(int32_t pid, int sig);")
|
||||
end
|
||||
|
||||
|
||||
if not pcall(function () return C.strerror end) then
|
||||
ffi.cdef("char *strerror(int errnum);")
|
||||
end
|
||||
|
||||
|
||||
-- Below is just the ID numbers for each POSIX signal. We map these signal IDs
|
||||
-- to system-specific signal numbers on the C land (via librestysignal.so).
|
||||
local signals = {
|
||||
NONE = 0,
|
||||
HUP = 1,
|
||||
INT = 2,
|
||||
QUIT = 3,
|
||||
ILL = 4,
|
||||
TRAP = 5,
|
||||
ABRT = 6,
|
||||
BUS = 7,
|
||||
FPE = 8,
|
||||
KILL = 9,
|
||||
USR1 = 10,
|
||||
SEGV = 11,
|
||||
USR2 = 12,
|
||||
PIPE = 13,
|
||||
ALRM = 14,
|
||||
TERM = 15,
|
||||
CHLD = 17,
|
||||
CONT = 18,
|
||||
STOP = 19,
|
||||
TSTP = 20,
|
||||
TTIN = 21,
|
||||
TTOU = 22,
|
||||
URG = 23,
|
||||
XCPU = 24,
|
||||
XFSZ = 25,
|
||||
VTALRM = 26,
|
||||
PROF = 27,
|
||||
WINCH = 28,
|
||||
IO = 29,
|
||||
PWR = 30,
|
||||
EMT = 31,
|
||||
SYS = 32,
|
||||
INFO = 33
|
||||
}
|
||||
|
||||
|
||||
local function signum(name)
|
||||
local sig_num
|
||||
if type(name) == "number" then
|
||||
sig_num = name
|
||||
else
|
||||
local id = signals[name]
|
||||
if not id then
|
||||
return nil, "unknown signal name"
|
||||
end
|
||||
|
||||
sig_num = tonumber(resty_signal.resty_signal_signum(id))
|
||||
if sig_num < 0 then
|
||||
error(
|
||||
string_format("missing C def for signal %s = %d", name, id),
|
||||
2
|
||||
)
|
||||
end
|
||||
end
|
||||
return sig_num
|
||||
end
|
||||
|
||||
|
||||
function _M.kill(pid, sig)
|
||||
assert(sig)
|
||||
|
||||
local sig_num, err = signum(sig)
|
||||
if err then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local rc = tonumber(C.kill(assert(pid), sig_num))
|
||||
if rc == 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
local err = ffi_str(C.strerror(errno()))
|
||||
return nil, err
|
||||
end
|
||||
|
||||
_M.signum = signum
|
||||
|
||||
return _M
|
|
@ -0,0 +1,152 @@
|
|||
#include <signal.h>
|
||||
|
||||
|
||||
enum {
|
||||
RS_NONE = 0,
|
||||
RS_HUP = 1,
|
||||
RS_INT = 2,
|
||||
RS_QUIT = 3,
|
||||
RS_ILL = 4,
|
||||
RS_TRAP = 5,
|
||||
RS_ABRT = 6,
|
||||
RS_BUS = 7,
|
||||
RS_FPE = 8,
|
||||
RS_KILL = 9,
|
||||
RS_USR1 = 10,
|
||||
RS_SEGV = 11,
|
||||
RS_USR2 = 12,
|
||||
RS_PIPE = 13,
|
||||
RS_ALRM = 14,
|
||||
RS_TERM = 15,
|
||||
RS_CHLD = 17,
|
||||
RS_CONT = 18,
|
||||
RS_STOP = 19,
|
||||
RS_TSTP = 20,
|
||||
RS_TTIN = 21,
|
||||
RS_TTOU = 22,
|
||||
RS_URG = 23,
|
||||
RS_XCPU = 24,
|
||||
RS_XFSZ = 25,
|
||||
RS_VTALRM = 26,
|
||||
RS_PROF = 27,
|
||||
RS_WINCH = 28,
|
||||
RS_IO = 29,
|
||||
RS_PWR = 30,
|
||||
RS_EMT = 31,
|
||||
RS_SYS = 32,
|
||||
RS_INFO = 33
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
resty_signal_signum(int num)
|
||||
{
|
||||
switch (num) {
|
||||
|
||||
case RS_NONE:
|
||||
return 0;
|
||||
|
||||
case RS_HUP:
|
||||
return SIGHUP;
|
||||
|
||||
case RS_INT:
|
||||
return SIGINT;
|
||||
|
||||
case RS_QUIT:
|
||||
return SIGQUIT;
|
||||
|
||||
case RS_ILL:
|
||||
return SIGILL;
|
||||
|
||||
case RS_TRAP:
|
||||
return SIGTRAP;
|
||||
|
||||
case RS_ABRT:
|
||||
return SIGABRT;
|
||||
|
||||
case RS_BUS:
|
||||
return SIGBUS;
|
||||
|
||||
case RS_FPE:
|
||||
return SIGFPE;
|
||||
|
||||
case RS_KILL:
|
||||
return SIGKILL;
|
||||
|
||||
case RS_SEGV:
|
||||
return SIGSEGV;
|
||||
|
||||
case RS_PIPE:
|
||||
return SIGPIPE;
|
||||
|
||||
case RS_ALRM:
|
||||
return SIGALRM;
|
||||
|
||||
case RS_TERM:
|
||||
return SIGTERM;
|
||||
|
||||
case RS_CHLD:
|
||||
return SIGCHLD;
|
||||
|
||||
case RS_CONT:
|
||||
return SIGCONT;
|
||||
|
||||
case RS_STOP:
|
||||
return SIGSTOP;
|
||||
|
||||
case RS_TSTP:
|
||||
return SIGTSTP;
|
||||
|
||||
case RS_TTIN:
|
||||
return SIGTTIN;
|
||||
|
||||
case RS_TTOU:
|
||||
return SIGTTOU;
|
||||
|
||||
case RS_XCPU:
|
||||
return SIGXCPU;
|
||||
|
||||
case RS_XFSZ:
|
||||
return SIGXFSZ;
|
||||
|
||||
case RS_VTALRM:
|
||||
return SIGVTALRM;
|
||||
|
||||
case RS_PROF:
|
||||
return SIGPROF;
|
||||
|
||||
case RS_WINCH:
|
||||
return SIGWINCH;
|
||||
|
||||
case RS_IO:
|
||||
return SIGIO;
|
||||
|
||||
#ifdef __linux__
|
||||
case RS_PWR:
|
||||
return SIGPWR;
|
||||
#endif
|
||||
|
||||
case RS_USR1:
|
||||
return SIGUSR1;
|
||||
|
||||
case RS_USR2:
|
||||
return SIGUSR2;
|
||||
|
||||
case RS_URG:
|
||||
return SIGURG;
|
||||
|
||||
#ifdef __APPLE__
|
||||
case RS_EMT:
|
||||
return SIGEMT;
|
||||
|
||||
case RS_SYS:
|
||||
return SIGSYS;
|
||||
|
||||
case RS_INFO:
|
||||
return SIGINFO;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package t::TestKiller;
|
||||
|
||||
use v5.10.1;
|
||||
use Test::Nginx::Socket::Lua -Base;
|
||||
|
||||
add_block_preprocessor(sub {
|
||||
my $block = shift;
|
||||
|
||||
my $http_config = $block->http_config // '';
|
||||
my $init_by_lua_block = $block->init_by_lua_block // 'require "resty.core"';
|
||||
|
||||
$http_config .= <<_EOC_;
|
||||
|
||||
lua_package_path "./lib/?.lua;../lua-resty-core/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
|
||||
lua_package_cpath "./?.so;;";
|
||||
init_by_lua_block {
|
||||
$init_by_lua_block
|
||||
}
|
||||
_EOC_
|
||||
|
||||
$block->set_value("http_config", $http_config);
|
||||
|
||||
if (!defined $block->error_log) {
|
||||
$block->set_value("no_error_log", "[error]");
|
||||
}
|
||||
|
||||
if (!defined $block->request) {
|
||||
$block->set_value("request", "GET /t");
|
||||
}
|
||||
});
|
||||
|
||||
1;
|
|
@ -0,0 +1,207 @@
|
|||
# vi:ft=
|
||||
|
||||
use lib '.';
|
||||
use t::TestKiller;
|
||||
|
||||
plan tests => 3 * blocks();
|
||||
|
||||
no_long_string();
|
||||
#no_diff();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: returns an error if signal is unknown
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
|
||||
local ok, err = resty_signal.kill(pid, "FOO")
|
||||
if not ok then
|
||||
ngx.say("failed to send FOO signal: ", err)
|
||||
return
|
||||
end
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
failed to send FOO signal: unknown signal name
|
||||
|
||||
|
||||
|
||||
=== TEST 2: send NONE to a non-existing process
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
|
||||
local say = ngx.say
|
||||
local ngx_pipe = require "ngx.pipe"
|
||||
local proc = assert(ngx_pipe.spawn("echo ok"))
|
||||
local pid = assert(proc:pid())
|
||||
assert(proc:wait())
|
||||
|
||||
local ok, err = resty_signal.kill(pid, "NONE")
|
||||
if not ok then
|
||||
ngx.say("failed to send NONE signal: ", err)
|
||||
return
|
||||
end
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
failed to send NONE signal: No such process
|
||||
|
||||
|
||||
|
||||
=== TEST 3: send TERM to a non-existing process
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
|
||||
local say = ngx.say
|
||||
local ngx_pipe = require "ngx.pipe"
|
||||
local proc = assert(ngx_pipe.spawn("echo ok"))
|
||||
local pid = assert(proc:pid())
|
||||
assert(proc:wait())
|
||||
|
||||
local ok, err = resty_signal.kill(pid, "TERM")
|
||||
if not ok then
|
||||
ngx.say("failed to send TERM signal: ", err)
|
||||
return
|
||||
end
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
failed to send TERM signal: No such process
|
||||
|
||||
|
||||
|
||||
=== TEST 4: send NONE to an existing process
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
|
||||
local say = ngx.say
|
||||
local ngx_pipe = require "ngx.pipe"
|
||||
local proc = assert(ngx_pipe.spawn("echo ok"))
|
||||
local pid = assert(proc:pid())
|
||||
-- assert(proc:wait())
|
||||
|
||||
for i = 1, 2 do
|
||||
ngx.say("i = ", i)
|
||||
local ok, err = resty_signal.kill(pid, "NONE")
|
||||
if not ok then
|
||||
ngx.say("failed to send NONE signal: ", err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
i = 1
|
||||
i = 2
|
||||
ok
|
||||
|
||||
|
||||
|
||||
=== TEST 5: send TERM to an existing process
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
|
||||
local say = ngx.say
|
||||
local ngx_pipe = require "ngx.pipe"
|
||||
local proc = assert(ngx_pipe.spawn("echo ok"))
|
||||
local pid = assert(proc:pid())
|
||||
-- assert(proc:wait())
|
||||
|
||||
for i = 1, 2 do
|
||||
ngx.say("i = ", i)
|
||||
local ok, err = resty_signal.kill(pid, "TERM")
|
||||
if not ok then
|
||||
ngx.say("failed to send TERM signal: ", err)
|
||||
return
|
||||
end
|
||||
ngx.sleep(0.01)
|
||||
end
|
||||
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
i = 1
|
||||
i = 2
|
||||
failed to send TERM signal: No such process
|
||||
|
||||
|
||||
|
||||
=== TEST 6: send KILL to an existing process
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
|
||||
local say = ngx.say
|
||||
local ngx_pipe = require "ngx.pipe"
|
||||
local proc = assert(ngx_pipe.spawn("echo ok"))
|
||||
local pid = assert(proc:pid())
|
||||
-- assert(proc:wait())
|
||||
|
||||
for i = 1, 2 do
|
||||
ngx.say("i = ", i)
|
||||
local ok, err = resty_signal.kill(pid, "KILL")
|
||||
if not ok then
|
||||
ngx.say("failed to send KILL signal: ", err)
|
||||
return
|
||||
end
|
||||
ngx.sleep(0.01)
|
||||
end
|
||||
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
i = 1
|
||||
i = 2
|
||||
failed to send KILL signal: No such process
|
||||
|
||||
|
||||
|
||||
=== TEST 7: send TERM signal value, 15, directly to an existing process
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
|
||||
local say = ngx.say
|
||||
local ngx_pipe = require "ngx.pipe"
|
||||
local proc = assert(ngx_pipe.spawn("echo ok"))
|
||||
local pid = assert(proc:pid())
|
||||
-- assert(proc:wait())
|
||||
|
||||
for i = 1, 2 do
|
||||
ngx.say("i = ", i)
|
||||
local ok, err = resty_signal.kill(pid, 15)
|
||||
if not ok then
|
||||
ngx.say("failed to send TERM signal: ", err)
|
||||
return
|
||||
end
|
||||
ngx.sleep(0.01)
|
||||
end
|
||||
|
||||
ngx.say("ok")
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
i = 1
|
||||
i = 2
|
||||
failed to send TERM signal: No such process
|
|
@ -0,0 +1,35 @@
|
|||
# vi:ft=
|
||||
|
||||
use lib '.';
|
||||
use t::TestKiller;
|
||||
|
||||
plan tests => 3 * blocks();
|
||||
|
||||
no_long_string();
|
||||
#no_diff();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: failure to load librestysignal.so
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local cpath = package.cpath
|
||||
package.cpath = "/foo/?.so;/bar/?.so;"
|
||||
|
||||
local ok, perr = pcall(require, "resty.signal")
|
||||
if not ok then
|
||||
ngx.say(perr)
|
||||
end
|
||||
|
||||
package.cpath = cpath
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
could not load librestysignal.so from the following paths:
|
||||
/foo/librestysignal.so
|
||||
/bar/librestysignal.so
|
||||
--- no_error_log
|
||||
[error]
|
|
@ -0,0 +1,116 @@
|
|||
# vi:ft=
|
||||
|
||||
use lib '.';
|
||||
use t::TestKiller;
|
||||
|
||||
plan tests => 3 * blocks();
|
||||
|
||||
no_long_string();
|
||||
#no_diff();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: signals whose values are specified by POSIX
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_signal = require "resty.signal"
|
||||
local ffi = require "ffi"
|
||||
local say = ngx.say
|
||||
local signum = resty_signal.signum
|
||||
|
||||
for i, signame in ipairs{ "ABRT", "ALRM", "HUP", "INT", "KILL",
|
||||
"QUIT", "TERM", "TRAP", "BLAH" } do
|
||||
say(signame, ": ", tostring(signum(signame)))
|
||||
end
|
||||
|
||||
local linux_signals = {
|
||||
NONE = 0,
|
||||
HUP = 1,
|
||||
INT = 2,
|
||||
QUIT = 3,
|
||||
ILL = 4,
|
||||
TRAP = 5,
|
||||
ABRT = 6,
|
||||
BUS = 7,
|
||||
FPE = 8,
|
||||
KILL = 9,
|
||||
USR1 = 10,
|
||||
SEGV = 11,
|
||||
USR2 = 12,
|
||||
PIPE = 13,
|
||||
ALRM = 14,
|
||||
TERM = 15,
|
||||
CHLD = 17,
|
||||
CONT = 18,
|
||||
STOP = 19,
|
||||
TSTP = 20,
|
||||
TTIN = 21,
|
||||
TTOU = 22,
|
||||
URG = 23,
|
||||
XCPU = 24,
|
||||
XFSZ = 25,
|
||||
VTALRM = 26,
|
||||
PROF = 27,
|
||||
WINCH = 28,
|
||||
IO = 29,
|
||||
PWR = 30
|
||||
}
|
||||
|
||||
local macosx_signals = {
|
||||
HUP = 1,
|
||||
INT = 2,
|
||||
QUIT = 3,
|
||||
ILL = 4,
|
||||
TRAP = 5,
|
||||
ABRT = 6,
|
||||
EMT = 7,
|
||||
FPE = 8,
|
||||
KILL = 9,
|
||||
BUS = 10,
|
||||
SEGV = 11,
|
||||
SYS = 12,
|
||||
PIPE = 13,
|
||||
ALRM = 14,
|
||||
TERM = 15,
|
||||
URG = 16,
|
||||
STOP = 17,
|
||||
TSTP = 18,
|
||||
CONT = 19,
|
||||
CHLD = 20,
|
||||
TTIN = 21,
|
||||
TTOU = 22,
|
||||
IO = 23,
|
||||
XCPU = 24,
|
||||
XFSZ = 25,
|
||||
VTALRM = 26,
|
||||
PROF = 27,
|
||||
WINCH = 28,
|
||||
INFO = 29,
|
||||
USR1 = 30,
|
||||
USR2 = 31
|
||||
}
|
||||
|
||||
if ffi.os == "Linux" then
|
||||
for signame, num in pairs(linux_signals) do
|
||||
assert(num == tonumber(signum(signame)))
|
||||
end
|
||||
elseif ffi.os == "OSX" then
|
||||
for signame, num in pairs(macosx_signals) do
|
||||
assert(num == tonumber(signum(signame)))
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
ABRT: 6
|
||||
ALRM: 14
|
||||
HUP: 1
|
||||
INT: 2
|
||||
KILL: 9
|
||||
QUIT: 3
|
||||
TERM: 15
|
||||
TRAP: 5
|
||||
BLAH: nil
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Param
|
||||
epoll_ctl(event)
|
||||
fun:epoll_ctl
|
||||
fun:ngx_epoll_test_rdhup
|
||||
fun:ngx_epoll_init
|
||||
fun:ngx_event_process_init
|
||||
fun:ngx_single_process_cycle
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
fun:ngx_alloc
|
||||
fun:ngx_set_environment
|
||||
fun:ngx_single_process_cycle
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
fun:ngx_alloc
|
||||
fun:ngx_set_environment
|
||||
fun:ngx_worker_process_init
|
||||
}
|
|
@ -38,4 +38,4 @@
|
|||
ansible.builtin.pause:
|
||||
seconds: 60
|
||||
- name: Restart GH runner
|
||||
shell: systemctl restart actions.runner.*
|
||||
shell: chown -R user:user /opt/actions-runner/ && systemctl restart actions.runner.*
|
||||
|
|
|
@ -38,4 +38,4 @@
|
|||
ansible.builtin.pause:
|
||||
seconds: 60
|
||||
- name: Restart GH runner
|
||||
shell: systemctl restart actions.runner.*
|
||||
shell: chown -R user:user /opt/actions-runner/ && systemctl restart actions.runner.*
|
||||
|
|
|
@ -38,4 +38,4 @@
|
|||
ansible.builtin.pause:
|
||||
seconds: 60
|
||||
- name: Restart GH runner
|
||||
shell: systemctl restart actions.runner.*
|
||||
shell: chown -R user:user /opt/actions-runner/ && systemctl restart actions.runner.*
|
||||
|
|
|
@ -49,4 +49,4 @@
|
|||
ansible.builtin.pause:
|
||||
seconds: 60
|
||||
- name: Restart GH runner
|
||||
shell: systemctl restart actions.runner.*
|
||||
shell: chown -R user:user /opt/actions-runner/ && systemctl restart actions.runner.*
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
FROM python:3.11.3-alpine
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN MAKEFLAGS="-j $(nproc)" pip install --no-cache -r requirements.txt && \
|
||||
rm -f requirements.txt
|
||||
|
||||
WORKDIR /opt/tests
|
||||
|
||||
COPY main.py .
|
||||
|
||||
ENTRYPOINT [ "python3", "main.py" ]
|
|
@ -0,0 +1,7 @@
|
|||
version: "3.5"
|
||||
|
||||
services:
|
||||
tests:
|
||||
build: .
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
@ -0,0 +1,55 @@
|
|||
version: "3.5"
|
||||
|
||||
services:
|
||||
bw:
|
||||
image: bunkerity/bunkerweb:1.5.0-beta
|
||||
pull_policy: never
|
||||
depends_on:
|
||||
- bw-redis
|
||||
labels:
|
||||
- "bunkerweb.INSTANCE"
|
||||
environment:
|
||||
API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"
|
||||
USE_BUNKERNET: "no"
|
||||
USE_BLACKLIST: "no"
|
||||
LOG_LEVEL: "info"
|
||||
USE_REDIS: "yes"
|
||||
REDIS_HOST: "bw-redis"
|
||||
networks:
|
||||
- bw-universe
|
||||
|
||||
bw-scheduler:
|
||||
image: bunkerity/bunkerweb-scheduler:1.5.0-beta
|
||||
pull_policy: never
|
||||
depends_on:
|
||||
- bw
|
||||
- bw-docker
|
||||
environment:
|
||||
DOCKER_HOST: "tcp://bw-docker:2375"
|
||||
LOG_LEVEL: "info"
|
||||
networks:
|
||||
- bw-universe
|
||||
- bw-docker
|
||||
|
||||
bw-docker:
|
||||
image: tecnativa/docker-socket-proxy
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
CONTAINERS: "1"
|
||||
networks:
|
||||
- bw-docker
|
||||
|
||||
bw-redis:
|
||||
image: redis:7-alpine
|
||||
networks:
|
||||
- bw-universe
|
||||
|
||||
networks:
|
||||
bw-universe:
|
||||
name: bw-universe
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 10.20.30.0/24
|
||||
bw-docker:
|
|
@ -0,0 +1,111 @@
|
|||
from os import getenv
|
||||
from traceback import format_exc
|
||||
from docker import DockerClient
|
||||
from docker.models.containers import Container
|
||||
|
||||
try:
|
||||
docker_host = getenv("DOCKER_HOST", "unix:///var/run/docker.sock")
|
||||
docker_client = DockerClient(base_url=docker_host)
|
||||
|
||||
bw_instances = docker_client.containers.list(
|
||||
filters={"label": "bunkerweb.INSTANCE"}
|
||||
)
|
||||
|
||||
if not bw_instances:
|
||||
print("❌ BunkerWeb instance not found ...", flush=True)
|
||||
exit(1)
|
||||
|
||||
bw_instance: Container = bw_instances[0]
|
||||
|
||||
print(
|
||||
'ℹ️ Executing the command "bwcli ban 127.0.0.1 -exp 3600" inside the BW container ...',
|
||||
flush=True,
|
||||
)
|
||||
|
||||
result = bw_instance.exec_run("bwcli ban 127.0.0.1 -exp 3600")
|
||||
|
||||
if result.exit_code != 0:
|
||||
print(
|
||||
f'❌ Command "ban" failed, exiting ...\noutput: {result.output.decode()}\nexit_code: {result.exit_code}'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(result.output.decode(), flush=True)
|
||||
|
||||
print(
|
||||
'ℹ️ Executing the command "bwcli bans" inside the BW container and checking the result ...',
|
||||
flush=True,
|
||||
)
|
||||
|
||||
result = bw_instance.exec_run("bwcli bans")
|
||||
|
||||
if result.exit_code != 0:
|
||||
print(
|
||||
f'❌ Command "bans" failed, exiting ...\noutput: {result.output.decode()}\nexit_code: {result.exit_code}'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
if b"- 127.0.0.1" not in result.output:
|
||||
print(
|
||||
f'❌ IP 127.0.0.1 not found in the output of "bans", exiting ...\noutput: {result.output.decode()}'
|
||||
)
|
||||
exit(1)
|
||||
elif b"List of bans for redis:" not in result.output:
|
||||
print(
|
||||
f'❌ Redis ban list not found in the output of "bans", exiting ...\noutput: {result.output.decode()}'
|
||||
)
|
||||
exit(1)
|
||||
elif b"1 hour" not in result.output and b"59 minutes" not in result.output:
|
||||
print(
|
||||
f"❌ Ban duration isn't 1 hour, exiting ...\noutput: {result.output.decode()}"
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(result.output.decode(), flush=True)
|
||||
|
||||
print(
|
||||
'ℹ️ Executing the command "bwcli unban 127.0.0.1" inside the BW container ...',
|
||||
flush=True,
|
||||
)
|
||||
|
||||
result = bw_instance.exec_run("bwcli unban 127.0.0.1")
|
||||
|
||||
if result.exit_code != 0:
|
||||
print(
|
||||
f'❌ Command "unban" failed, exiting ...\noutput: {result.output.decode()}\nexit_code: {result.exit_code}'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(result.output.decode(), flush=True)
|
||||
|
||||
print(
|
||||
'ℹ️ Executing the command "bwcli bans" inside the BW container to check if the IP was unbanned ...',
|
||||
flush=True,
|
||||
)
|
||||
|
||||
result = bw_instance.exec_run("bwcli bans")
|
||||
|
||||
if result.exit_code != 0:
|
||||
print(
|
||||
f'❌ Command "bans" failed, exiting ...\noutput: {result.output.decode()}\nexit_code: {result.exit_code}'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
found = 0
|
||||
for line in result.output.splitlines():
|
||||
if b"No ban found" in line:
|
||||
found += 1
|
||||
|
||||
if found < 2:
|
||||
print(
|
||||
f"❌ IP 127.0.0.1 was not unbanned from both redis and the local ban list, exiting ...\noutput: {result.output.decode()}",
|
||||
flush=True,
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print(result.output.decode(), flush=True)
|
||||
except SystemExit:
|
||||
exit(1)
|
||||
except:
|
||||
print(f"❌ Something went wrong, exiting ...\n{format_exc()}", flush=True)
|
||||
exit(1)
|
|
@ -0,0 +1 @@
|
|||
docker==6.1.2
|
|
@ -0,0 +1,88 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "⌨️ Building bunkernet stack ..."
|
||||
|
||||
# Starting stack
|
||||
docker compose pull bw-docker
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "⌨️ Pull failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
docker compose -f docker-compose.test.yml build
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "⌨️ Build failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup_stack () {
|
||||
echo "⌨️ Cleaning up current stack ..."
|
||||
|
||||
docker compose down -v --remove-orphans 2>/dev/null
|
||||
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "⌨️ Down failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "⌨️ Cleaning up current stack done ✅"
|
||||
}
|
||||
|
||||
# Cleanup stack on exit
|
||||
trap cleanup_stack EXIT
|
||||
|
||||
echo "⌨️ Running bwcli tests ..."
|
||||
|
||||
echo "⌨️ Starting stack ..."
|
||||
docker compose up -d 2>/dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "⌨️ Up failed, retrying ... ⚠️"
|
||||
manual=1
|
||||
cleanup_stack
|
||||
manual=0
|
||||
docker compose up -d 2>/dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "⌨️ Up failed ❌"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if stack is healthy
|
||||
echo "⌨️ Waiting for stack to be healthy ..."
|
||||
i=0
|
||||
while [ $i -lt 120 ] ; do
|
||||
containers=("bwcli-bw-1" "bwcli-bw-scheduler-1")
|
||||
healthy="true"
|
||||
for container in "${containers[@]}" ; do
|
||||
check="$(docker inspect --format "{{json .State.Health }}" $container | grep "healthy")"
|
||||
if [ "$check" = "" ] ; then
|
||||
healthy="false"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$healthy" = "true" ] ; then
|
||||
echo "⌨️ Docker stack is healthy ✅"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i+1))
|
||||
done
|
||||
if [ $i -ge 120 ] ; then
|
||||
docker compose logs
|
||||
echo "⌨️ Docker stack is not healthy ❌"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start tests
|
||||
|
||||
docker compose -f docker-compose.test.yml up --abort-on-container-exit --exit-code-from tests 2>/dev/null
|
||||
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "⌨️ Test bwcli failed ❌"
|
||||
echo "🛡️ Showing BunkerWeb and BunkerWeb Scheduler logs ..."
|
||||
docker compose logs bw bw-scheduler
|
||||
exit 1
|
||||
else
|
||||
echo "⌨️ Test bwcli succeeded ✅"
|
||||
fi
|
||||
|
||||
echo "⌨️ Tests are done ! ✅"
|
|
@ -812,7 +812,7 @@ with webdriver.Firefox(
|
|||
|
||||
assert_alert_message(driver, "was successfully created")
|
||||
|
||||
sleep(15)
|
||||
sleep(30)
|
||||
|
||||
driver.execute_script("window.open('http://www.example.com/hello','_blank');")
|
||||
driver.switch_to.window(driver.window_handles[1])
|
||||
|
|
Loading…
Reference in New Issue