show useful info in BW logs after startup/reload and reduce container images size

This commit is contained in:
florian 2023-05-14 15:57:55 +02:00
parent a686562f18
commit 2e1296d9ae
No known key found for this signature in database
GPG Key ID: 3D80806F12602A7C
17 changed files with 353 additions and 159 deletions

7
TODO
View File

@ -1,6 +1,9 @@
- YT video demo web UI
- README
- Ansible
- Vagrant
- Plugins
- prepare new antibot challenge when not resolved and "refresh page"
- fix doc integrations : "The Docker autoconf integration is similar to the Docker autoconf one"
- add ipv6 to doc
- edit doc integrations boilerplates : missing autoconf container in docker autoconf, fix syntax for autoconf service example and make them ready to use (copy / paste)
- edit doc + misc/integrations : network bw-services: network.external.name is deprecated. Please set network.name with external: true
- fix db warnings (Got an error reading communication packets)

View File

@ -1,4 +1,4 @@
FROM python:3.11.3-alpine
FROM python:3.11.3-alpine AS builder
# Copy python requirements
COPY src/common/gen/requirements.txt /tmp/req/requirements.txt
@ -28,10 +28,18 @@ COPY src/common/helpers /usr/share/bunkerweb/helpers
COPY src/common/settings.json /usr/share/bunkerweb/settings.json
COPY src/common/utils /usr/share/bunkerweb/utils
# Add nginx user, drop bwcli, setup data folders, permissions and logging
FROM python:3.11.3-alpine
# Set default umask to prevent huge recursive chmod increasing the final image size
RUN umask 027
# Copy dependencies
COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb
# Add autoconf user, drop bwcli, install runtime dependencies, create data folders and set permissions
RUN apk add --no-cache bash && \
addgroup -g 101 nginx && \
adduser -h /var/cache/nginx -g nginx -s /bin/sh -G nginx -D -H -u 101 nginx && \
addgroup -g 101 autoconf && \
adduser -h /var/cache/autoconf -g autoconf -s /bin/sh -G autoconf -D -H -u 101 autoconf && \
cp /usr/share/bunkerweb/helpers/bwcli /usr/bin/ && \
mkdir -p /var/tmp/bunkerweb && \
mkdir -p /var/www && \
@ -41,16 +49,11 @@ RUN apk add --no-cache bash && \
mkdir -p /data/www && ln -s /data/www /var/www/html && \
for dir in $(echo "configs plugins") ; do mkdir -p "/data/${dir}" && ln -s "/data/${dir}" "/etc/bunkerweb/${dir}" ; done && \
for dir in $(echo "configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \
chown -R root:nginx /data && \
chown -R root:autoconf /data && \
chmod -R 770 /data && \
chown -R root:nginx /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \
find /usr/share/bunkerweb -type f -exec chmod 0740 {} \; && \
find /usr/share/bunkerweb -type d -exec chmod 0750 {} \; && \
chown -R root:autoconf /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \
chmod -R 770 /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb && \
chmod 750 /usr/share/bunkerweb/cli/main.py /usr/share/bunkerweb/helpers/*.sh /usr/bin/bwcli /usr/share/bunkerweb/autoconf/main.py /usr/share/bunkerweb/deps/python/bin/* && \
mkdir /var/log/letsencrypt /var/lib/letsencrypt && \
chown root:nginx /var/log/letsencrypt /var/lib/letsencrypt && \
chmod 770 /var/log/letsencrypt /var/lib/letsencrypt
chmod 750 /usr/share/bunkerweb/cli/main.py /usr/share/bunkerweb/helpers/*.sh /usr/bin/bwcli /usr/share/bunkerweb/autoconf/main.py /usr/share/bunkerweb/deps/python/bin/*
# Fix CVEs
RUN apk add "libcrypto3>=3.0.8-r4" "libssl3>=3.0.8-r4"
@ -59,7 +62,7 @@ VOLUME /data /etc/nginx
WORKDIR /usr/share/bunkerweb/autoconf
USER nginx:nginx
USER autoconf:autoconf
HEALTHCHECK --interval=10s --timeout=10s --start-period=30s --retries=6 CMD /usr/share/bunkerweb/helpers/healthcheck-autoconf.sh

View File

@ -22,11 +22,6 @@ RUN apk add --no-cache --virtual .build-deps py3-pip && \
pip install --no-cache-dir --require-hashes --target /usr/share/bunkerweb/deps/python -r /usr/share/bunkerweb/deps/requirements.txt && \
apk del .build-deps
FROM nginx:1.24.0-alpine
# Copy dependencies
COPY --from=builder /usr/share/bunkerweb /usr/share/bunkerweb
# Copy files
# can't exclude deps from . so we are copying everything by hand
COPY src/bw/entrypoint.sh /usr/share/bunkerweb/entrypoint.sh
@ -44,6 +39,14 @@ COPY src/common/utils /usr/share/bunkerweb/utils
COPY src/VERSION /usr/share/bunkerweb/VERSION
COPY misc/*.ascii /usr/share/bunkerweb/misc/
FROM nginx:1.24.0-alpine
# Set default umask to prevent huge recursive chmod increasing the final image size
RUN umask 027
# Copy dependencies
COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb
# Install runtime dependencies, pypi packages, move bwcli, create data folders and set permissions
RUN apk add --no-cache pcre bash python3 && \
cp /usr/share/bunkerweb/helpers/bwcli /usr/bin/ && \
@ -55,21 +58,15 @@ RUN apk add --no-cache pcre bash python3 && \
for dir in $(echo "configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \
chown -R root:nginx /data && \
chmod -R 770 /data && \
chown -R root:nginx /usr/share/bunkerweb /var/cache/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type f -exec chmod 0740 {} \; ; done && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type d -exec chmod 0750 {} \; ; done && \
chown -R root:nginx /var/cache/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \
chmod 770 /var/cache/bunkerweb /var/tmp/bunkerweb && \
chmod 750 /usr/share/bunkerweb/cli/main.py /usr/share/bunkerweb/gen/main.py /usr/share/bunkerweb/helpers/*.sh /usr/share/bunkerweb/entrypoint.sh /usr/bin/bwcli /usr/share/bunkerweb/deps/python/bin/* && \
chown -R root:nginx /etc/nginx && \
chmod -R 770 /etc/nginx && \
rm -f /var/log/nginx/* && \
mkdir /var/log/letsencrypt /var/lib/letsencrypt && \
chown root:nginx /var/log/letsencrypt /var/lib/letsencrypt && \
chmod 770 /var/log/letsencrypt /var/lib/letsencrypt && \
ln -s /proc/1/fd/2 /var/log/nginx/error.log && \
ln -s /proc/1/fd/2 /var/log/nginx/modsec_audit.log && \
ln -s /proc/1/fd/1 /var/log/nginx/access.log && \
ln -s /proc/1/fd/1 /var/log/nginx/jobs.log
ln -s /proc/1/fd/1 /var/log/nginx/access.log
# Fix CVEs
RUN apk add "libcrypto3>=3.0.8-r4" "libssl3>=3.0.8-r4"

View File

@ -106,6 +106,7 @@ helpers.fill_ctx = function()
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
-- IP data : global
local ip_is_global, err = utils.ip_is_global(data.remote_addr)
if ip_is_global == nil then

View File

@ -20,8 +20,17 @@ function plugin:initialize(id)
end
-- Store 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 ngx.get_phase() ~= "init")
local value, err = utils.get_variable(k, v.context == "multisite" and multisite)
if value == nil then
self.logger:log(ngx.ERR, "can't get " .. k .. " variable : " .. err)
end

View File

@ -87,7 +87,7 @@ server {
if not ok then
logger:log(ngx.ERR, plugin_obj)
else
local ok, ret = helpers.call_plugin(plugin_obj, "log")
local ok, ret = helpers.call_plugin(plugin_obj, "log_default")
if not ok then
logger:log(ngx.ERR, ret)
else

View File

@ -1,13 +1,19 @@
lua_shared_dict ready_lock 16k;
lua_shared_dict worker_lock 16k;
init_worker_by_lua_block {
-- Our timer function
local ready_log = function(premature)
local ready_work = function(premature)
-- Libs
local helpers = require "bunkerweb.helpers"
local cjson = require "cjson"
-- Instantiate objects
local logger = require "bunkerweb.logger":new("INIT")
local logger = require "bunkerweb.logger":new("INIT-WORKER")
local datastore = require "bunkerweb.datastore":new()
-- Don't print the ready log if we are in loading state
-- Don't go further we are in loading state
local is_loading, err = require "bunkerweb.utils".get_variable("IS_LOADING", false)
if not is_loading then
logger:log(ngx.ERR, "utils.get_variable() failed : " .. err)
@ -15,34 +21,101 @@ local ready_log = function(premature)
elseif is_loading == "yes" then
return
end
-- Instantiate lock
local lock = require "resty.lock":new("ready_lock")
local lock = require "resty.lock":new("worker_lock")
if not lock then
logger:log(ngx.ERR, "lock:new() failed : " .. err)
return
end
-- Acquire lock
local elapsed, err = lock:lock("ready")
if elapsed == nil then
logger:log(ngx.ERR, "lock:lock() failed : " .. err)
else
-- Display ready log
local ok, err = datastore:get("misc_ready")
if not ok and err ~= "not found" then
logger:log(ngx.ERR, "datastore:get() failed : " .. err)
elseif not ok and err == "not found" then
logger:log(ngx.NOTICE, "BunkerWeb is ready to fool hackers ! 🚀")
local ok, err = datastore:set("misc_ready", "ok")
if not ok then
logger:log(ngx.ERR, "datastore:set() failed : " .. err)
return
end
-- Check if work is done
local ok, err = datastore:get("misc_ready")
if not ok and err ~= "not found" then
logger:log(ngx.ERR, "datastore:get() failed : " .. err)
local ok, err = lock:unlock()
if not ok then
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
end
return
end
if ok then
local ok, err = lock:unlock()
if not ok then
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
end
return
end
logger:log(ngx.INFO, "init_worker phase started")
-- Get plugins
local plugins, err = datastore:get("plugins")
if not plugins then
logger:log(ngx.ERR, "can't get plugins from datastore : " .. err)
local ok, err = lock:unlock()
if not ok then
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
end
return
end
plugins = cjson.decode(plugins)
-- Call init_worker() methods
logger:log(ngx.INFO, "calling init_worker() methods of plugins ...")
for i, plugin in ipairs(plugins) 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.INFO, err)
else
-- Check if plugin has init_worker method
if plugin_lua.init_worker ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
if not ok then
logger:log(ngx.ERR, plugin_obj)
else
local ok, ret = helpers.call_plugin(plugin_obj, "init_worker")
if not ok then
logger:log(ngx.ERR, ret)
elseif not ret.ret then
logger:log(ngx.ERR, plugin.id .. ":init_worker() call failed : " .. ret.msg)
else
logger:log(ngx.INFO, plugin.id .. ":init_worker() call successful : " .. ret.msg)
end
end
else
logger:log(ngx.INFO, "skipped execution of " .. plugin.id .. " because method init_worker() is not defined")
end
end
end
-- Release lock
lock:unlock()
logger:log(ngx.INFO, "called init_worker() methods of plugins")
-- End
local ok, err = datastore:set("misc_ready", "ok")
if not ok then
logger:log(ngx.ERR, "datastore:set() failed : " .. err)
end
local ok, err = lock:unlock()
if not ok then
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
end
logger:log(ngx.INFO, "init phase ended")
logger:log(ngx.NOTICE, "BunkerWeb is ready to fool hackers ! 🚀")
end
-- Start timer
ngx.timer.at(5, ready_log)
ngx.timer.at(5, ready_work)
}

View File

@ -18,7 +18,7 @@ function blacklist:initialize()
end
self.use_redis = use_redis == "yes"
-- Decode lists
if ngx.get_phase() ~= "init" and self.variables["USE_BLACKLIST"] == "yes" then
if ngx.get_phase() ~= "init" and self:is_needed() then
local lists, err = self.datastore:get("plugin_blacklist_lists")
if not lists then
self.logger:log(ngx.ERR, err)
@ -51,13 +51,26 @@ function blacklist:initialize()
self.cachestore = cachestore:new(self.use_redis)
end
function blacklist:init()
-- Check if init is needed
local init_needed, err = utils.has_variable("USE_BLACKLIST", "yes")
if init_needed == nil then
return self:ret(false, "can't check USE_BLACKLIST variable : " .. err)
function blacklist:is_needed()
-- Loading case
if self.is_loading then
return false
end
if not init_needed or self.is_loading then
-- Request phases (no default)
if self.is_request and (ngx.ctx.bw.server_name ~= "_") then
return self.variables["USE_BLACKLIST"] == "yes"
end
-- Other cases : at least one service uses it
local is_needed, err = utils.has_variable("USE_BLACKLIST", "yes")
if is_needed == nil then
self.logger:log(ngx.ERR, "can't check USE_BLACKLIST variable : " .. err)
end
return is_needed
end
function blacklist:init()
-- Check if init needed
if not self:is_needed() then
return self:ret(true, "init not needed")
end
@ -95,8 +108,8 @@ end
function blacklist:access()
-- Check if access is needed
if self.variables["USE_BLACKLIST"] ~= "yes" then
return self:ret(true, "blacklist not activated")
if not self:is_needed() then
return self:ret(true, "access not needed")
end
-- Check the caches
local checks = {

View File

@ -10,8 +10,8 @@ local bunkernet = class("bunkernet", plugin)
function bunkernet:initialize()
-- Call parent initialize
plugin.initialize(self, "bunkernet")
-- Get BunkerNet ID
if ngx.get_phase() ~= "init" and self.variables["USE_BUNKERNET"] == "yes" and not self.is_loading then
-- Get BunkerNet ID and save info
if ngx.get_phase() ~= "init" and self:is_needed() then
local id, err = self.datastore:get("plugin_bunkernet_id")
if id then
self.bunkernet_id = id
@ -23,19 +23,49 @@ function bunkernet:initialize()
end
end
function bunkernet:init()
-- Check if init is needed
function bunkernet:is_needed()
-- Loading case
if self.is_loading then
return self:ret(true, "bunkerweb is loading")
return false
end
local init_needed, err = utils.has_variable("USE_BUNKERNET", "yes")
if init_needed == nil then
return self:ret(false, "can't check USE_BUNKERNET variable : " .. err)
-- Request phases (no default)
if self.is_request and (ngx.ctx.bw.server_name ~= "_") then
return self.variables["USE_BUNKERNET"] == "yes"
end
if not init_needed or self.is_loading then
return self:ret(true, "no service uses bunkernet, skipping init")
-- Other cases : at least one service uses it
local is_needed, err = utils.has_variable("USE_BUNKERNET", "yes")
if is_needed == nil then
self.logger:log(ngx.ERR, "can't check USE_BUNKERNET variable : " .. err)
end
return is_needed
end
function bunkernet:init_worker()
-- Check if needed
if not self:is_needed() then
return self:ret(true, "no service uses BunkerNet, skipping init_worker")
end
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
-- Send ping request
local ok, err, status, data = self:ping()
if not ok then
return self:ret(false, "error while sending request to API : " .. err)
end
if status ~= 200 then
return self:ret(false, "received status " .. tostring(status) .. " from API using instance ID " .. self.bunkernet_id)
end
self.logger:log(ngx.NOTICE, "connectivity with API using instance ID " .. self.id .. " is successful")
return self:ret(true, "connectivity with API using instance ID " .. self.id .. " is successful")
end
function bunkernet:init()
-- Check if needed
if not self:is_needed() then
return self:ret(true, "no service uses BunkerNet, skipping init")
end
-- Check if instance ID is present
local f, err = io.open("/var/cache/bunkerweb/bunkernet/instance.id", "r")
if not f then
@ -74,23 +104,17 @@ function bunkernet:init()
if not ok then
return self:ret(false, "can't store bunkernet database into datastore : " .. err)
end
return self:ret(true,
"successfully connected to the bunkernet service " ..
self.variables["BUNKERNET_SERVER"] .. " with machine ID " .. id .. " and " .. tostring(i) .. " bad IPs in database")
return self:ret(true, "successfully loaded " .. tostring(i) .. " bad IPs using instance ID " .. id)
end
function bunkernet:access()
-- Check if not loading
if self.is_loading then
return self:ret(true, "bunkerweb is loading")
-- Check if needed
if not self:is_needed() then
return self:ret(true, "service doesn't use BunkerNet, skipping access")
end
-- Check if enabled
if self.variables["USE_BUNKERNET"] ~= "yes" then
return self:ret(true, "bunkernet not activated")
end
-- Check if BunkerNet ID is generated
-- Check id
if not self.bunkernet_id then
return self:ret(false, "bunkernet ID is not generated")
return self:ret(false, "missing instance ID")
end
-- Check if IP is global
if not ngx.ctx.bw.ip_is_global then
@ -120,20 +144,16 @@ function bunkernet:access()
return self:ret(true, "not in db")
end
function bunkernet:log(bypass_use_bunkernet)
-- Check if not loading
if self.is_loading then
return self:ret(true, "bunkerweb is loading")
end
if not bypass_use_bunkernet then
-- Check if BunkerNet is enabled
if self.variables["USE_BUNKERNET"] ~= "yes" then
return self:ret(true, "bunkernet not activated")
function bunkernet:log(bypass_checks)
if not bypass_checks then
-- Check if needed
if not self:is_needed() then
return self:ret(true, "service doesn't use BunkerNet, skipping log")
end
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
end
-- Check if BunkerNet ID is generated
if not self.bunkernet_id then
return self:ret(false, "bunkernet ID is not generated")
end
-- Check if IP has been blocked
local reason = utils.get_reason()
@ -168,25 +188,21 @@ function bunkernet:log(bypass_use_bunkernet)
end
function bunkernet:log_default()
-- Check if not loading is needed
if self.is_loading then
return self:ret(true, "bunkerweb is loading")
-- Check if needed
if not self:is_needed() then
return self:ret(true, "no service uses BunkerNet, skipping log_default")
end
-- Check if BunkerNet is activated
local check, err = utils.has_variable("USE_BUNKERNET", "yes")
if check == nil then
return false, "error while checking variable USE_BUNKERNET (" .. err .. ")"
end
if not check then
return true, "bunkernet not enabled"
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
-- Check if default server is disabled
local check, err = utils.get_variable("DISABLE_DEFAULT_SERVER", false)
if check == nil then
return false, "error while getting variable DISABLE_DEFAULT_SERVER (" .. err .. ")"
return self:ret(false, "error while getting variable DISABLE_DEFAULT_SERVER : " .. err)
end
if check ~= "yes" then
return true, "default server not disabled"
return self:ret(true, "default server is not disabled")
end
-- Call log method
return self:log(true)
@ -199,15 +215,17 @@ end
function bunkernet:request(method, url, data)
local httpc, err = http.new()
if not httpc then
return false, "can't instantiate http object : " .. err, nil, nil
return false, "can't instantiate http object : " .. err
end
local all_data = {
id = self.bunkernet_id,
version = self.version,
integration = self.integration
}
for k, v in pairs(data) do
all_data[k] = v
if data then
for k, v in pairs(data) do
all_data[k] = v
end
end
local res, err = httpc:request_uri(self.variables["BUNKERNET_SERVER"] .. url, {
method = method,
@ -219,14 +237,14 @@ function bunkernet:request(method, url, data)
})
httpc:close()
if not res then
return false, "error while sending request : " .. err, nil, nil
return false, "error while sending request : " .. err
end
if res.status ~= 200 then
return false, "status code != 200", res.status, nil
end
local ok, ret = pcall(cjson.decode, res.body)
if not ok then
return false, "error while decoding json : " .. ret, nil, nil
return false, "error while decoding json : " .. ret
end
return true, "success", res.status, ret
end

View File

@ -19,6 +19,32 @@ function dnsbl:initialize()
self.cachestore = cachestore:new(self.use_redis)
end
function dnsbl:init_worker()
-- Check if loading
if self.is_loading then
return self:ret(false, "BW is loading")
end
-- Check if at least one service uses it
local is_needed, err = utils.has_variable("USE_DNSBL", "yes")
if is_needed == nil then
return self:ret(false, "can't check USE_DNSBL variable : " .. err)
elseif not is_needed then
return self:ret(true, "no service uses DNSBL, skipping init_worker")
end
-- Loop on DNSBL list
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
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")
else
self.logger:log(ngx.NOTICE, "dnsbl check for " .. server .. " is successful")
end
end
return self:ret(true, "success")
end
function dnsbl:access()
-- Check if access is needed
if self.variables["USE_DNSBL"] ~= "yes" then

View File

@ -17,7 +17,7 @@ function greylist:initialize()
end
self.use_redis = use_redis == "yes"
-- Decode lists
if ngx.get_phase() ~= "init" and self.variables["USE_GREYLIST"] == "yes" then
if ngx.get_phase() ~= "init" and self:is_needed() then
local lists, err = self.datastore:get("plugin_greylist_lists")
if not lists then
self.logger:log(ngx.ERR, err)
@ -45,13 +45,26 @@ function greylist:initialize()
self.cachestore = cachestore:new(self.use_redis)
end
function greylist:init()
-- Check if init is needed
local init_needed, err = utils.has_variable("USE_GREYLIST", "yes")
if init_needed == nil then
return self:ret(false, "can't check USE_GREYLIST variable : " .. err)
function greylist:is_needed()
-- Loading case
if self.is_loading then
return false
end
if not init_needed or self.is_loading then
-- Request phases (no default)
if self.is_request and (ngx.ctx.bw.server_name ~= "_") then
return self.variables["USE_GREYLIST"] == "yes"
end
-- Other cases : at least one service uses it
local is_needed, err = utils.has_variable("USE_GREYLIST", "yes")
if is_needed == nil then
self.logger:log(ngx.ERR, "can't check USE_GREYLIST variable : " .. err)
end
return is_needed
end
function greylist:init()
-- Check if init needed
if not self:is_needed() then
return self:ret(true, "init not needed")
end
-- Read greylists
@ -83,8 +96,8 @@ end
function greylist:access()
-- Check if access is needed
if self.variables["USE_GREYLIST"] ~= "yes" then
return self:ret(true, "greylist not activated")
if not self:is_needed() then
return self:ret(true, "access not needed")
end
-- Check the caches
local checks = {

View File

@ -102,7 +102,7 @@
"help": "List of URI, separated with spaces, to put into the greylist.",
"id": "greylist-uri",
"label": "Greylist URI",
"regex": "^( *(/[\\w\\].~:/?#[@!$&'()*+,;=-]*)(?!.*\\2(?!.)) *)*$",
"regex": "^.*$",
"type": "text"
},
"GREYLIST_URI_URLS": {

View File

@ -18,7 +18,7 @@ function limit:initialize()
self.use_redis = use_redis == "yes"
self.clusterstore = clusterstore:new()
-- Load rules if needed
if ngx.get_phase() ~= "init" and self.variables["USE_LIMIT_REQ"] == "yes" then
if ngx.get_phase() ~= "init" and self:is_needed() then
-- Get all rules from datastore
local limited = false
local all_rules, err = self.datastore:get("plugin_limit_rules")
@ -43,14 +43,27 @@ function limit:initialize()
end
end
function limit:is_needed()
-- Loading case
if self.is_loading then
return false
end
-- Request phases (no default)
if self.is_request and (ngx.ctx.bw.server_name ~= "_") then
return self.variables["USE_LIMIT_REQ"] == "yes"
end
-- Other cases : at least one service uses it
local is_needed, err = utils.has_variable("USE_LIMIT_REQ", "yes")
if is_needed == nil then
self.logger:log(ngx.ERR, "can't check USE_LIMIT_REQ variable : " .. err)
end
return is_needed
end
function limit:init()
-- Check if init is needed
local init_needed, err = utils.has_variable("USE_LIMIT_REQ", "yes")
if init_needed == nil then
return self:ret(false, err)
end
if not init_needed or self.is_loading then
return self:ret(true, "no service uses Limit for requests, skipping init")
if not self:is_needed() then
return self:ret(true, "no service uses limit for requests, skipping init")
end
-- Get variables
local variables, err = utils.get_multiple_variables({"LIMIT_REQ_URL", "LIMIT_REQ_RATE"})
@ -86,8 +99,8 @@ function limit:access()
return self:ret(true, "client is whitelisted")
end
-- Check if access is needed
if self.variables["USE_LIMIT_REQ"] ~= "yes" then
return self:ret(true, "limit req is disabled")
if not self:is_needed() then
return self:ret(true, "limit request not enabled")
end
-- Check if URI is limited
local rate = nil

View File

@ -11,25 +11,27 @@ function redis:initialize()
plugin.initialize(self, "redis")
end
function redis:init()
-- Check if init is needed
function redis:init_worker()
-- Check if init_worker is needed
if self.variables["USE_REDIS"] ~= "yes" or self.is_loading then
return self:ret(true, "init not needed")
return self:ret(true, "init_worker not needed")
end
-- Check redis connection ()
-- Check redis connection
local ok, err = clusterstore:connect()
if not ok then
return self:ret(false, "redis connect error : " .. err)
end
-- Send ping
local ok, err = clusterstore:call("ping")
clusterstore:close()
if err then
return self:ret(false, "error while sending ping command : " .. err)
return self:ret(false, "error while sending ping command to redis server : " .. err)
end
if not ok then
return self:ret(false, "ping command failed")
return self:ret(false, "redis ping command failed")
end
return self:ret(true, "redis ping successful")
self.logger:log(ngx.NOTICE, "connectivity with redis server " .. self.variables["REDIS_HOST"] .. " is successful")
return self:ret(true, "success")
end
return redis

View File

@ -19,7 +19,7 @@ function whitelist:initialize()
end
self.use_redis = use_redis == "yes"
-- Decode lists
if ngx.get_phase() ~= "init" and self.variables["USE_WHITELIST"] == "yes" then
if ngx.get_phase() ~= "init" and self:is_needed() then
local lists, err = self.datastore:get("plugin_whitelist_lists")
if not lists then
self.logger:log(ngx.ERR, err)
@ -44,16 +44,29 @@ function whitelist:initialize()
end
end
-- Instantiate cachestore
self.cachestore = cachestore:new(self.use_redis and ngx.get_phase() == "access")
self.cachestore = cachestore:new(self.use_redis)
end
function whitelist:is_needed()
-- Loading case
if self.is_loading then
return false
end
-- Request phases (no default)
if self.is_request and (ngx.ctx.bw.server_name ~= "_") then
return self.variables["USE_WHITELIST"] == "yes"
end
-- Other cases : at least one service uses it
local is_needed, err = utils.has_variable("USE_WHITELIST", "yes")
if is_needed == nil then
self.logger:log(ngx.ERR, "can't check USE_WHITELIST variable : " .. err)
end
return is_needed
end
function whitelist:init()
-- Check if init is needed
local init_needed, err = utils.has_variable("USE_WHITELIST", "yes")
if init_needed == nil then
return self:ret(false, "can't check USE_WHITELIST variable : " .. err)
end
if not init_needed or self.is_loading then
if not self:is_needed() then
return self:ret(true, "init not needed")
end
-- Read whitelists
@ -89,7 +102,7 @@ function whitelist:set()
ngx.ctx.bw.is_whitelisted = "no"
env.set("is_whitelisted", "no")
-- Check if set is needed
if self.variables["USE_WHITELIST"] ~= "yes" then
if not self:is_needed() then
return self:ret(true, "whitelist not activated")
end
-- Check cache
@ -107,7 +120,7 @@ end
function whitelist:access()
-- Check if access is needed
if self.variables["USE_WHITELIST"] ~= "yes" then
if not self:is_needed() then
return self:ret(true, "whitelist not activated")
end
-- Check cache

View File

@ -1,4 +1,4 @@
FROM python:3.11.3-alpine
FROM python:3.11.3-alpine AS builder
# Copy python requirements
COPY src/scheduler/requirements.txt /tmp/req/requirements.txt
@ -33,6 +33,14 @@ COPY src/common/utils /usr/share/bunkerweb/utils
COPY src/scheduler /usr/share/bunkerweb/scheduler
COPY src/VERSION /usr/share/bunkerweb/VERSION
FROM python:3.11.3-alpine
# Set default umask to prevent huge recursive chmod increasing the final image size
RUN umask 027
# Copy dependencies
COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb
# Add scheduler user, drop bwcli, install runtime dependencies, create data folders and set permissions
RUN apk add --no-cache bash libgcc libstdc++ openssl && \
ln -s /usr/local/bin/python3 /usr/bin/python3 && \
@ -50,20 +58,15 @@ RUN apk add --no-cache bash libgcc libstdc++ openssl && \
for dir in $(echo "configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \
chown -R root:scheduler /data && \
chmod -R 770 /data && \
chown -R root:scheduler /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \
find /usr/share/bunkerweb -type f -exec chmod 0740 {} \; && \
find /usr/share/bunkerweb -type d -exec chmod 0750 {} \; && \
chown -R root:scheduler /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \
chmod -R 770 /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb && \
find /usr/share/bunkerweb/core/*/jobs/* -type f -exec chmod 750 {} \; && \
chmod 750 /usr/share/bunkerweb/cli/main.py /usr/share/bunkerweb/gen/*.py /usr/share/bunkerweb/scheduler/main.py /usr/share/bunkerweb/scheduler/entrypoint.sh /usr/share/bunkerweb/helpers/*.sh /usr/share/bunkerweb/deps/python/bin/* /usr/bin/bwcli && \
mkdir -p /etc/nginx && \
chown -R scheduler:scheduler /etc/nginx && \
chmod -R 770 /etc/nginx && \
mkdir -p /var/log/letsencrypt /var/lib/letsencrypt && \
chown root:scheduler /var/log/letsencrypt /var/lib/letsencrypt && \
chmod 770 /var/log/letsencrypt /var/lib/letsencrypt && \
ln -s /proc/1/fd/1 /var/log/letsencrypt/letsencrypt.log && \
chmod 660 /usr/share/bunkerweb/INTEGRATION
chmod 660 /usr/share/bunkerweb/INTEGRATION && \
chown root:scheduler /usr/share/bunkerweb/INTEGRATION
# Fix CVEs
RUN apk add "libcrypto3>=3.0.8-r4" "libssl3>=3.0.8-r4"

View File

@ -1,4 +1,4 @@
FROM python:3.11.3-alpine
FROM python:3.11.3-alpine AS builder
# Copy python requirements
COPY src/ui/requirements.txt /tmp/req/requirements.txt
@ -30,7 +30,15 @@ COPY src/common/helpers /usr/share/bunkerweb/helpers
COPY src/ui /usr/share/bunkerweb/ui
COPY src/VERSION /usr/share/bunkerweb/VERSION
# Add ui user
FROM python:3.11.3-alpine
# Set default umask to prevent huge recursive chmod increasing the final image size
RUN umask 027
# Copy dependencies
COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb
# Add ui user, drop bwcli, install runtime dependencies, create data folders and set permissions
RUN apk add --no-cache bash && \
addgroup -g 101 ui && \
adduser -h /var/cache/nginx -g ui -s /bin/sh -G ui -D -H -u 101 ui && \
@ -44,12 +52,11 @@ RUN apk add --no-cache bash && \
for dir in $(echo "configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \
chown -R root:ui /data && \
chmod -R 770 /data && \
chown -R root:ui /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /var/log/nginx && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type f -exec chmod 0740 {} \; ; done && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type d -exec chmod 0750 {} \; ; done && \
chown -R root:ui /usr/share/bunkerweb/INTEGRATION /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /var/log/nginx && \
chmod 770 /var/cache/bunkerweb /var/lib/bunkerweb /var/tmp/bunkerweb /var/log/nginx/ui.log && \
chmod 750 /usr/share/bunkerweb/gen/*.py /usr/share/bunkerweb/ui/*.py /usr/share/bunkerweb/ui/src/*.py /usr/share/bunkerweb/deps/python/bin/* /usr/share/bunkerweb/helpers/*.sh && \
chmod 660 /usr/share/bunkerweb/INTEGRATION
chmod 660 /usr/share/bunkerweb/INTEGRATION && \
chown root:ui /usr/share/bunkerweb/INTEGRATION
# Fix CVEs
RUN apk add "libcrypto3>=3.0.8-r4" "libssl3>=3.0.8-r4"