From 6e2cf40666987e0cc97d9b798a8a9f0bef0ffc84 Mon Sep 17 00:00:00 2001 From: "Andrew \"Bob\" Brockhurst" Date: Sun, 14 Apr 2024 12:29:07 +0100 Subject: [PATCH] New feature: ability to set the content type on served ban template. --- config_example.conf | 2 ++ lib/crowdsec.lua | 5 ++--- lib/plugins/crowdsec/ban.lua | 22 ++++++++++++++++++---- lib/plugins/crowdsec/config.lua | 7 ++++--- lib/plugins/crowdsec/utils.lua | 20 ++++++++++++++------ 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/config_example.conf b/config_example.conf index e99e4b2..3a9ea39 100644 --- a/config_example.conf +++ b/config_example.conf @@ -14,6 +14,8 @@ EXCLUDE_LOCATION= #those apply for "ban" action # /!\ REDIRECT_LOCATION and RET_CODE can't be used together. REDIRECT_LOCATION take priority over RET_CODE BAN_TEMPLATE_PATH=/var/lib/crowdsec/lua/templates/ban.html +# Content type for the ban template, can be html, text, gif, jpeg, svg, png or tiff. Default: html +BAN_CONTENT_TYPE=html REDIRECT_LOCATION= RET_CODE= #those apply for "captcha" action diff --git a/lib/crowdsec.lua b/lib/crowdsec.lua index a34c890..301fb38 100644 --- a/lib/crowdsec.lua +++ b/lib/crowdsec.lua @@ -70,8 +70,7 @@ function csmod.init(configFile, userAgent) ngx.log(ngx.ERR, "Lua shared dict (crowdsec cache) is full, please increase dict size in config") end - - local err = ban.new(runtime.conf["BAN_TEMPLATE_PATH"], runtime.conf["REDIRECT_LOCATION"], runtime.conf["RET_CODE"]) + local err = ban.new(runtime.conf["BAN_TEMPLATE_PATH"], runtime.conf["REDIRECT_LOCATION"], runtime.conf["RET_CODE"], runtime.conf["BAN_CONTENT_TYPE"]) if err ~= nil then ngx.log(ngx.ERR, "error loading ban plugins: " .. err) end @@ -758,4 +757,4 @@ end function csmod.close() end -return csmod \ No newline at end of file +return csmod diff --git a/lib/plugins/crowdsec/ban.lua b/lib/plugins/crowdsec/ban.lua index 1dddf8b..e981573 100644 --- a/lib/plugins/crowdsec/ban.lua +++ b/lib/plugins/crowdsec/ban.lua @@ -5,10 +5,11 @@ local M = {_TYPE='module', _NAME='ban.funcs', _VERSION='1.0-0'} M.template_str = "" M.redirect_location = "" +M.content_type = "text/html" M.ret_code = ngx.HTTP_FORBIDDEN -function M.new(template_path, redirect_location, ret_code) +function M.new(template_path, redirect_location, ret_code, content_type) M.redirect_location = redirect_location ret_code_ok = false @@ -33,6 +34,20 @@ function M.new(template_path, redirect_location, ret_code) end end + content_type_ok = false + if content_type ~= nil and content_type ~= "" then + for k, v in pairs(utils.CONTENT_TYPE) do + if k == content_type then + M.content_type = utils.CONTENT_TYPE[content_type] + content_type_ok = true + break + end + end + if content_type_ok == false then + ngx.log(ngx.ERR, "RET_CONTENT_TYPE '" .. content_type .. "' is not supported, using default content_type " .. M.content_type) + end + end + if template_file_ok == false and (M.redirect_location == nil or M.redirect_location == "") then ngx.log(ngx.ERR, "BAN_TEMPLATE_PATH and REDIRECT_LOCATION variable are empty, will return HTTP " .. M.ret_code .. " for ban decisions") end @@ -61,17 +76,16 @@ function M.apply(...) return end if M.template_str ~= "" then - ngx.header.content_type = "text/html" + ngx.header.content_type = M.content_type ngx.header.cache_control = "no-cache" ngx.status = status ngx.say(M.template_str) ngx.exit(status) return end - ngx.exit(status) return end -return M \ No newline at end of file +return M diff --git a/lib/plugins/crowdsec/config.lua b/lib/plugins/crowdsec/config.lua index 11cb32f..716c588 100644 --- a/lib/plugins/crowdsec/config.lua +++ b/lib/plugins/crowdsec/config.lua @@ -39,7 +39,7 @@ function config.loadConfig(file) return nil, "File ".. file .." doesn't exist" end local conf = {} - local valid_params = {'ENABLED','API_URL', 'API_KEY', 'BOUNCING_ON_TYPE', 'MODE', 'SECRET_KEY', 'SITE_KEY', 'BAN_TEMPLATE_PATH' ,'CAPTCHA_TEMPLATE_PATH', 'REDIRECT_LOCATION', 'RET_CODE', 'EXCLUDE_LOCATION', 'FALLBACK_REMEDIATION', 'CAPTCHA_PROVIDER', 'APPSEC_URL', 'APPSEC_FAILURE_ACTION', 'ALWAYS_SEND_TO_APPSEC', 'SSL_VERIFY'} + local valid_params = {'ENABLED','API_URL', 'API_KEY', 'BOUNCING_ON_TYPE', 'MODE', 'SECRET_KEY', 'SITE_KEY', 'BAN_TEMPLATE_PATH', 'BAN_CONTENT_TYPE' ,'CAPTCHA_TEMPLATE_PATH', 'REDIRECT_LOCATION', 'RET_CODE', 'EXCLUDE_LOCATION', 'FALLBACK_REMEDIATION', 'CAPTCHA_PROVIDER', 'APPSEC_URL', 'APPSEC_FAILURE_ACTION', 'ALWAYS_SEND_TO_APPSEC', 'SSL_VERIFY'} local valid_int_params = {'CACHE_EXPIRATION', 'CACHE_SIZE', 'REQUEST_TIMEOUT', 'UPDATE_FREQUENCY', 'CAPTCHA_EXPIRATION', 'APPSEC_CONNECT_TIMEOUT', 'APPSEC_SEND_TIMEOUT', 'APPSEC_PROCESS_TIMEOUT', 'STREAM_REQUEST_TIMEOUT'} local valid_bouncing_on_type_values = {'ban', 'captcha', 'all'} local valid_truefalse_values = {'false', 'true'} @@ -55,6 +55,7 @@ function config.loadConfig(file) ['REDIRECT_LOCATION'] = "", ['EXCLUDE_LOCATION'] = {}, ['RET_CODE'] = 0, + ['BAN_CONTENT_TYPE'] = "html", ['CAPTCHA_PROVIDER'] = "recaptcha", ['APPSEC_URL'] = "", ['APPSEC_CONNECT_TIMEOUT'] = 100, @@ -116,7 +117,7 @@ function config.loadConfig(file) value = "ban" end end - + conf[key] = value elseif has_value(valid_int_params, key) then @@ -133,4 +134,4 @@ function config.loadConfig(file) end return conf, nil end -return config \ No newline at end of file +return config diff --git a/lib/plugins/crowdsec/utils.lua b/lib/plugins/crowdsec/utils.lua index 0665c4c..2d11af5 100644 --- a/lib/plugins/crowdsec/utils.lua +++ b/lib/plugins/crowdsec/utils.lua @@ -9,13 +9,21 @@ M.HTTP_CODE["301"] = ngx.HTTP_MOVED_PERMANENTLY M.HTTP_CODE["302"] = ngx.HTTP_MOVED_TEMPORARILY M.HTTP_CODE["400"] = ngx.HTTP_BAD_REQUEST M.HTTP_CODE["401"] = ngx.HTTP_UNAUTHORIZED -M.HTTP_CODE["401"] = ngx.HTTP_UNAUTHORIZED M.HTTP_CODE["403"] = ngx.HTTP_FORBIDDEN M.HTTP_CODE["404"] = ngx.HTTP_NOT_FOUND M.HTTP_CODE["405"] = ngx.HTTP_NOT_ALLOWED M.HTTP_CODE["406"] = ngx.HTTP_NOT_ACCEPTABLE M.HTTP_CODE["500"] = ngx.HTTP_INTERNAL_SERVER_ERROR +M.CONTENT_TYPE = {} +M.CONTENT_TYPE["gif"] = "image/gif" +M.CONTENT_TYPE["html"] = "text/html" +M.CONTENT_TYPE["jpeg"] = "image/jpeg" +M.CONTENT_TYPE["png"] = "image/png" +M.CONTENT_TYPE["svg"] = "image/svg+xml" +M.CONTENT_TYPE["text"] = "text/plain" +M.CONTENT_TYPE["tiff"] = "image/tiff" + function M.read_file(path) local file = io.open(path, "r") -- r read mode and b binary mode if not file then return nil end @@ -30,10 +38,10 @@ function M.file_exist(path) return nil end local f = io.open(path, "r") - if f ~= nil then + if f ~= nil then io.close(f) - return true - else + return true + else return false end end @@ -41,7 +49,7 @@ end function M.starts_with(str, start) return str:sub(1, #start) == start end - + function M.ends_with(str, ending) return ending == "" or str:sub(-#ending) == ending end @@ -54,4 +62,4 @@ function M.table_len(table) return count end -return M \ No newline at end of file +return M