-
Notifications
You must be signed in to change notification settings - Fork 359
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds support for the security.txt file, including the necessary configuration files and templates. The security.txt file provides information about security policies and contact details for reporting vulnerabilities.
- Loading branch information
1 parent
3540903
commit 33ec069
Showing
5 changed files
with
329 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,8 @@ | |
"blacklist", | ||
"greylist", | ||
"bunkernet", | ||
"limit" | ||
"limit", | ||
"securitytxt" | ||
], | ||
"init_worker": [ | ||
"redis", | ||
|
38 changes: 38 additions & 0 deletions
38
src/common/core/securitytxt/confs/server-http/securitytxt.conf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{% if USE_SECURITYTXT == "yes" and SECURITYTXT_CONTACT != "" +%} | ||
location = {{ SECURITYTXT_URI }} { | ||
default_type 'text/plain; charset=utf-8'; | ||
root /usr/share/bunkerweb/core/securitytxt/templates; | ||
content_by_lua_block { | ||
local logger = require "bunkerweb.logger":new("SECURITYTXT") | ||
local helpers = require "bunkerweb.helpers" | ||
|
||
local ngx = ngx | ||
local ERR = ngx.ERR | ||
local INFO = ngx.INFO | ||
local fill_ctx = helpers.fill_ctx | ||
local save_ctx = helpers.save_ctx | ||
local tostring = tostring | ||
|
||
local ok, ret, errors, ctx = fill_ctx() | ||
if not ok then | ||
logger:log(ERR, "fill_ctx() failed : " .. ret) | ||
elseif errors then | ||
for i, error in ipairs(errors) do | ||
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error) | ||
end | ||
end | ||
local securitytxt = require "securitytxt.securitytxt":new(ctx) | ||
local ret = securitytxt:content() | ||
if not ret.ret then | ||
logger:log(ERR, "securitytxt:content() failed : " .. ret.msg) | ||
else | ||
logger:log(INFO, "securitytxt:content() success : " .. ret.msg) | ||
end | ||
save_ctx(ctx) | ||
} | ||
} | ||
|
||
location = /security.txt { | ||
return 301 {{ SECURITYTXT_URI }}; | ||
} | ||
{% endif %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
{ | ||
"id": "securitytxt", | ||
"name": "Security.txt", | ||
"description": "Manage the security.txt file. A proposed standard which allows websites to define security policies.", | ||
"version": "1.0", | ||
"stream": "yes", | ||
"settings": { | ||
"USE_SECURITYTXT": { | ||
"context": "multisite", | ||
"default": "no", | ||
"help": "Enable security.txt file.", | ||
"id": "use-securitytxt", | ||
"label": "Use security.txt", | ||
"regex": "^(yes|no)$", | ||
"type": "check" | ||
}, | ||
"SECURITYTXT_URI": { | ||
"context": "multisite", | ||
"default": "/.well-known/security.txt", | ||
"help": "Indicates the URI where the \"security.txt\" file will be accessible from.", | ||
"id": "securitytxt-uri", | ||
"label": "Security.txt URI", | ||
"regex": "^\\/\\S*$", | ||
"type": "text" | ||
}, | ||
"SECURITYTXT_CONTACT": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "Indicates a method that researchers should use for reporting security vulnerabilities such as an email address, a phone number, and/or a web page with contact information. (If the value is empty, the security.txt file will not be created as it is a required field)", | ||
"id": "securitytxt-contact", | ||
"label": "Security.txt contact", | ||
"regex": "^((mailto:|tel:|https:\\/\\/)\\S+)?$", | ||
"type": "text", | ||
"multiple": "securitytxt-contact" | ||
}, | ||
"SECURITYTXT_EXPIRES": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "Indicates the date and time after which the data contained in the \"security.txt\" file is considered stale and should not be used (If the value is empty, the value will always be the current date and time + 1 year).", | ||
"id": "securitytxt-expiration", | ||
"label": "Security.txt expiration", | ||
"regex": "^([0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(Z|[+-]([01][0-9]|2[0-3]):[0-5][0-9]))?$", | ||
"type": "text" | ||
}, | ||
"SECURITYTXT_ENCRYPTION": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "Indicates an encryption key that security researchers should use for encrypted communication.", | ||
"id": "securitytxt-encryption", | ||
"label": "Security.txt encryption", | ||
"regex": "^((https:\\/\\/|dns:|openpgp4fpr:)\\S+)?$", | ||
"type": "text", | ||
"multiple": "securitytxt-encryption" | ||
}, | ||
"SECURITYTXT_ACKNOWLEDGEMENTS": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "Indicates a link to a page where security researchers are recognized for their reports.", | ||
"id": "securitytxt-acknowledgement", | ||
"label": "Security.txt acknowledgement", | ||
"regex": "^(https:\\/\\/\\S+)?$", | ||
"type": "text", | ||
"multiple": "securitytxt-acknowledgement" | ||
}, | ||
"SECURITYTXT_PREFERRED_LANG": { | ||
"context": "multisite", | ||
"default": "en", | ||
"help": "Can be used to indicate a set of natural languages that are preferred when submitting security reports.", | ||
"id": "securitytxt-preferred-lang", | ||
"label": "Security.txt preferred language", | ||
"regex": "^[a-zA-Z]{2,3}(-[a-zA-Z]{2})?(, [a-zA-Z]{2,3}(-[a-zA-Z]{2})?)*$", | ||
"type": "text" | ||
}, | ||
"SECURITYTXT_CANONICAL": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "Indicates the canonical URIs where the \"security.txt\" file is located, which is usually something like \"https://example.com/.well-known/security.txt\". (If the value is empty, the default value will be automatically generated from the site URL + SECURITYTXT_URI)", | ||
"id": "securitytxt-canonical", | ||
"label": "Security.txt canonical", | ||
"regex": "^(https:\\/\\/\\S+)?$", | ||
"type": "text", | ||
"multiple": "securitytxt-canonical" | ||
}, | ||
"SECURITYTXT_POLICY": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "Indicates a link to where the vulnerability disclosure policy is located.", | ||
"id": "securitytxt-policy", | ||
"label": "Security.txt policy", | ||
"regex": "^(https:\\/\\/\\S+)?$", | ||
"type": "text", | ||
"multiple": "securitytxt-policy" | ||
}, | ||
"SECURITYTXT_HIRING": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "Used for linking to the vendor's security-related job positions.", | ||
"id": "securitytxt-hiring", | ||
"label": "Security.txt hiring", | ||
"regex": "^(https:\\/\\/\\S+)?$", | ||
"type": "text", | ||
"multiple": "securitytxt-hiring" | ||
}, | ||
"SECURITYTXT_CSAF": { | ||
"context": "multisite", | ||
"default": "", | ||
"help": "A link to the provider-metadata.json of your CSAF (Common Security Advisory Framework) provider.", | ||
"id": "securitytxt-csaf", | ||
"label": "Security.txt CSAF", | ||
"regex": "^(https:\\/\\/\\S+)?$", | ||
"type": "text", | ||
"multiple": "securitytxt-csaf" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
local class = require "middleclass" | ||
local plugin = require "bunkerweb.plugin" | ||
local utils = require "bunkerweb.utils" | ||
|
||
local ngx = ngx | ||
local ERR = ngx.ERR | ||
local get_phase = ngx.get_phase | ||
local subsystem = ngx.config.subsystem | ||
local get_multiple_variables = utils.get_multiple_variables | ||
|
||
local template | ||
local render = nil | ||
if subsystem == "http" then | ||
template = require "resty.template" | ||
render = template.render | ||
end | ||
|
||
local securitytxt = class("securitytxt", plugin) | ||
|
||
function securitytxt:initialize(ctx) | ||
-- Call parent initialize | ||
plugin.initialize(self, "securitytxt", ctx) | ||
-- Load data from datastore if needed | ||
if get_phase() ~= "init" then | ||
-- Get security policies from datastore | ||
local security_policies, err = self.datastore:get("plugin_securitytxt_security_policies", true) | ||
if not security_policies then | ||
self.logger:log(ERR, err) | ||
return | ||
end | ||
self.security_policies = { | ||
["uri"] = "", | ||
["acknowledgements"] = {}, | ||
["canonical"] = {}, | ||
["contact"] = {}, | ||
["encryption"] = {}, | ||
["expires"] = "", | ||
["hiring"] = {}, | ||
["policy"] = {}, | ||
["preferred_lang"] = "", | ||
["csaf"] = {}, | ||
} | ||
-- Extract global security policies | ||
if security_policies.global then | ||
for k, v in pairs(security_policies.global) do | ||
self.security_policies[k] = v | ||
end | ||
end | ||
-- Extract and overwrite if needed server security policies | ||
if security_policies[self.ctx.bw.server_name] then | ||
for k, v in pairs(security_policies[self.ctx.bw.server_name]) do | ||
self.security_policies[k] = v | ||
end | ||
end | ||
end | ||
end | ||
|
||
function securitytxt:init() | ||
-- Get variables | ||
local variables, err = get_multiple_variables({ | ||
"SECURITYTXT_URI", | ||
"SECURITYTXT_ACKNOWLEDGEMENTS", | ||
"SECURITYTXT_CANONICAL", | ||
"SECURITYTXT_CONTACT", | ||
"SECURITYTXT_ENCRYPTION", | ||
"SECURITYTXT_EXPIRES", | ||
"SECURITYTXT_HIRING", | ||
"SECURITYTXT_POLICY", | ||
"SECURITYTXT_PREFERRED_LANG", | ||
"SECURITYTXT_CSAF", | ||
}) | ||
if variables == nil then | ||
return self:ret(false, err) | ||
end | ||
-- Store security policies name and value | ||
local data = {} | ||
local key | ||
for srv, vars in pairs(variables) do | ||
if data[srv] == nil then | ||
data[srv] = { | ||
["uri"] = "", | ||
["acknowledgements"] = {}, | ||
["canonical"] = {}, | ||
["contact"] = {}, | ||
["encryption"] = {}, | ||
["expires"] = "", | ||
["hiring"] = {}, | ||
["policy"] = {}, | ||
["preferred_lang"] = "", | ||
["csaf"] = {}, | ||
} | ||
end | ||
for var, value in pairs(vars) do | ||
if value ~= "" then | ||
key = string.lower(string.gsub(string.gsub(var, "^SECURITYTXT_", ""), "_%d+$", "")) | ||
if key == "uri" or key == "expires" or key == "preferred_lang" then | ||
data[srv][key] = value | ||
else | ||
data[srv][key][#data[srv][key] + 1] = value | ||
end | ||
end | ||
end | ||
end | ||
local ok | ||
ok, err = self.datastore:set("plugin_securitytxt_security_policies", data, nil, true) | ||
if not ok then | ||
return self:ret(false, err) | ||
end | ||
return self:ret(true, "successfully loaded security policies") | ||
end | ||
|
||
function securitytxt:content() | ||
-- Check if content is needed | ||
if self.variables["USE_SECURITYTXT"] == "no" then | ||
return self:ret(true, "securitytxt not activated") | ||
end | ||
|
||
-- Check if display content is needed | ||
if self.ctx.bw.uri ~= self.variables["SECURITYTXT_URI"] then | ||
return self:ret(true, "securitytxt not requested") | ||
end | ||
|
||
-- Check if a contact key is set | ||
if self.security_policies["contact"] == nil or #self.security_policies["contact"] == 0 then | ||
return self:ret(false, "no contact key set") | ||
end | ||
|
||
local data = { | ||
server_name = self.ctx.bw.server_name, | ||
scheme = self.ctx.bw.scheme, | ||
} | ||
|
||
for k, v in pairs(self.security_policies) do | ||
data[k] = v | ||
end | ||
|
||
-- If expires isn't set, set it to 1 year in the future and make it a ISO.8601-1 and ISO.8601-2 date | ||
if data["expires"] == "" then | ||
data["expires"] = os.date("%Y-%m-%dT%H:%M:%S", os.time() + 31536000) | ||
end | ||
|
||
-- Render content | ||
render("security.txt", data) | ||
return self:ret(true, "content displayed") | ||
end | ||
|
||
return securitytxt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{% for _, c in ipairs(contact) do %} | ||
Contact: {*c*} | ||
{% end %} | ||
Expires: {*expires*} | ||
{% for _, e in ipairs(encryption) do %} | ||
Encryption: {*e*} | ||
{% end %} | ||
{% for _, a in ipairs(acknowledgements) do %} | ||
Acknowledgements: {*a*} | ||
{% end %} | ||
Preferred-Languages: {*preferred_lang*} | ||
{% if canonical and #canonical > 0 then %} | ||
{% for _, ca in ipairs(canonical) do %} | ||
Canonical: {*ca*} | ||
{% end %} | ||
{% else %} | ||
Canonical: {*scheme*}://{*server_name*}{*uri*} | ||
{% end %} | ||
{% for _, p in ipairs(policy) do %} | ||
Policy: {*p*} | ||
{% end %} | ||
{% for _, h in ipairs(hiring) do %} | ||
Hiring: {*h*} | ||
{% end %} | ||
{% for _, cs in ipairs(csaf) do %} | ||
CSAF: {*cs*} | ||
{% end %} |