diff --git a/confs2/site/error.conf b/confs2/site/error.conf index 7d7a70e..1636d1a 100644 --- a/confs2/site/error.conf +++ b/confs2/site/error.conf @@ -1,7 +1,13 @@ -error_page {{ CODE }} {{ PAGE }}; +{% if ERRORS != "" %} + {% for element in ERRORS.split(" ") %} + {% set code = element.split("=")[0] %} + {% set page = element.split("=")[1] %} +error_page {{ code }} {{ page }}; -location = {{ PAGE }} { +location = {{ page }} { root {{ ROOT_FOLDER }}; modsecurity off; internal; } + {% endfor %} +{% endif %} diff --git a/confs2/site/main-lua.conf b/confs2/site/main-lua.conf index cb98819..a6310d9 100644 --- a/confs2/site/main-lua.conf +++ b/confs2/site/main-lua.conf @@ -1,4 +1,8 @@ +{% if ANTIBOT_SESSION_SECRET == "random" %} +set $session_secret {{ random(32) }} ; +{% else %} set $session_secret {{ ANTIBOT_SESSION_SECRET }}; +{% endif %} set $session_check_addr on; access_by_lua_block { diff --git a/confs2/site/modsecurity-rules.conf b/confs2/site/modsecurity-rules.conf index d0dd42f..37f0a7d 100644 --- a/confs2/site/modsecurity-rules.conf +++ b/confs2/site/modsecurity-rules.conf @@ -58,13 +58,26 @@ SecAuditLog /var/log/nginx/modsec_audit.log include {{ NGINX_PREFIX }}modsecurity-clamav.conf {% endif %} -# include OWASP CRS rules +# include OWASP CRS configuration {% if USE_MODSECURITY_CRS == "yes" %} include /opt/owasp/crs.conf -# TODO : include without errors ? -#{{ MODSECURITY_INCLUDE_CUSTOM_CRS }} + +# custom CRS configurations before loading rules (exclusions) +{% if is_custom_conf("/modsec-crs-confs") %} +include /modsec-crs-confs/*.conf +{% endif %} +{% if MULTISITE == "yes" and is_custom_conf("/modsec-crs-confs/" + FIRST_SERVER) %} +include /modsec-crs-confs/{{ FIRST_SERVER }}/*.conf +{% endif %} + +# include OWASP CRS rules include /opt/owasp/crs/*.conf {% endif %} -# TODO : include custom rules -# {{ MODSECURITY_INCLUDE_CUSTOM_RULES }} +# custom rules after loading the CRS +{% if is_custom_conf("/modsec-confs") %} +include /modsec-confs/*.conf +{% endif %} +{% if MULTISITE == "yes" and is_custom_conf("/modsec-confs/" + FIRST_SERVER) %} +include /modsec-confs/{{ FIRST_SERVER }}/*.conf +{% endif %} diff --git a/confs2/site/reverse-proxy.conf b/confs2/site/reverse-proxy.conf index bef8844..284df6e 100644 --- a/confs2/site/reverse-proxy.conf +++ b/confs2/site/reverse-proxy.conf @@ -1,7 +1,25 @@ -location {{ REVERSE_PROXY_URL }} { +{% if USE_REVERSE_PROXY == "yes" %} + {% for k, v in all.items() %} + {% if k.startswith("REVERSE_PROXY_URL") %} + {% set url = v %} + {% set host = all[k.replace("URL", "HOST")] if k.replace("URL", "HOST") in all else "" %} + {% set ws = all[k.replace("URL", "WS")] if k.replace("URL", "WS") in all else "" %} + {% set headers = all[k.replace("URL", "HEADERS")] if k.replace("URL", "HEADERS") in all else "" %} +location {{ url }} {% raw %}{{% endraw %} etag off; - proxy_pass {{ REVERSE_PROXY_HOST }}; - {{ REVERSE_PROXY_HEADERS }} - {{ REVERSE_PROXY_WS }} - {{ REVERSE_PROXY_CUSTOM_HEADERS }} -} + proxy_pass {{ host }}; + include {{ NGINX_PREFIX }}reverse-proxy-headers.conf; + {% if ws == "yes" %} + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + {% endif %} + {% if headers != "" %} + {% for header in headers.split(";") %} + proxy_set_header {{ header }}; + {% endfor %} + {% endif %} +{% raw %}}{% endraw %} + {% endif %} + {% endfor %} +{% endif %} diff --git a/confs2/site/server.conf b/confs2/site/server.conf index 256e28c..99ba927 100644 --- a/confs2/site/server.conf +++ b/confs2/site/server.conf @@ -130,7 +130,8 @@ server { include {{ NGINX_PREFIX }}cookie-flags.conf; {% endif %} - # TODO : ERRORS + # custom errors + include {{ NGINX_PREFIX }}error.conf; # client caching {% if USE_CLIENT_CACHE == "yes" %} @@ -165,7 +166,7 @@ server { # reverse proxy {% if USE_REVERSE_PROXY == "yes" %} - include {{ NGINX_PREFIX }}reverse-proxy-*.conf; + include {{ NGINX_PREFIX }}reverse-proxy.conf; {% endif %} # remote PHP diff --git a/gen/Configurator.py b/gen/Configurator.py index 03b8fa4..5182fd8 100644 --- a/gen/Configurator.py +++ b/gen/Configurator.py @@ -40,4 +40,9 @@ class Configurator : real_var = var elif var[len(var.split("_")[0])+1:] in self.__settings : real_var = var[len(var.split("_")[0])+1:] + elif re.search("\\_\d+$", var) and ("_".join(var.split("_")[:-1]) in self.__settings or "_".join(var.split("_")[:-1][1:]) in self.__settings) : + if "_".join(var.split("_")[:-1]) in self.__settings : + real_var = "_".join(var.split("_")[:-1]) + else : + real_var = "_".join(var.split("_")[:-1][1:]) return real_var != "" and re.search(self.__settings[real_var]["regex"], value) and (not multisite_only or self.__settings[real_var]["context"] == "multisite") diff --git a/gen/Templator.py b/gen/Templator.py index 08d8f4b..97e4b57 100644 --- a/gen/Templator.py +++ b/gen/Templator.py @@ -1,4 +1,4 @@ -import jinja2, glob, os, pathlib, copy, crypt +import jinja2, glob, os, pathlib, copy, crypt, random, string class Templator : @@ -21,7 +21,7 @@ class Templator : first_server = self.__config["SERVER_NAME"].split(" ")[0] return self.__render("site", server_name, first_server) - def __render(self, type, server_name=None, first_server=None) : + def __prepare_config(self, type, server_name=None, first_server=None) : real_config = copy.deepcopy(self.__config) if not server_name is None : real_config["SERVER_NAME"] = server_name @@ -30,23 +30,48 @@ class Templator : real_config["NGINX_PREFIX"] = self.__target_path if real_config["MULTISITE"] == "yes" and type == "site" : real_config["NGINX_PREFIX"] += first_server + "/" + real_config["ROOT_FOLDER"] += "/" + first_server for variable, value in self.__config.items() : if variable.startswith(first_server + "_") : real_config[variable.replace(first_server + "_", "", 1)] = value + if real_config["ROOT_SITE_SUBFOLDER"] != "" : + real_config["ROOT_FOLDER"] += "/" + real_config["ROOT_SITE_SUBFOLDER"] + return real_config + + def __save_config(self, type, config) : + data = "" + for variable, value in config.items() : + data += variable + "=" + value + "\n" + file = self.__output_path + if type == "global" : + file += "/global.env" + elif config["MULTISITE"] == "yes" : + file += "/" + config["FIRST_SERVER"] + "/site.env" + else : + file += "/site.env" + with open(file, "w") as f : + f.write(data) + + def __render(self, type, server_name=None, first_server=None) : + real_config = self.__prepare_config(type, server_name, first_server) for filename in glob.iglob(self.__input_path + "/" + type + "**/**", recursive=True) : - if os.path.isfile(filename) : - relative_filename = filename.replace(self.__input_path, "").replace(type + "/", "") - template = self.__template_env.get_template(type + "/" + relative_filename) - template.globals["has_value"] = Templator.has_value - template.globals["sha512_crypt"] = Templator.sha512_crypt - output = template.render(real_config, all=real_config) - if real_config["MULTISITE"] == "yes" and type == "site" : - relative_filename = first_server + "/" + relative_filename - if "/" in relative_filename : - directory = relative_filename.replace(relative_filename.split("/")[-1], "") - pathlib.Path(self.__output_path + "/" + directory).mkdir(parents=True, exist_ok=True) - with open(self.__output_path + "/" + relative_filename, "w") as f : - f.write(output) + if not os.path.isfile(filename) : + continue + relative_filename = filename.replace(self.__input_path, "").replace(type + "/", "") + template = self.__template_env.get_template(type + "/" + relative_filename) + template.globals["has_value"] = Templator.has_value + template.globals["sha512_crypt"] = Templator.sha512_crypt + template.globals["is_custom_conf"] = Templator.is_custom_conf + template.globals["random"] = Templator.random + output = template.render(real_config, all=real_config) + if real_config["MULTISITE"] == "yes" and type == "site" : + relative_filename = real_config["FIRST_SERVER"] + "/" + relative_filename + if "/" in relative_filename : + directory = relative_filename.replace(relative_filename.split("/")[-1], "") + pathlib.Path(self.__output_path + "/" + directory).mkdir(parents=True, exist_ok=True) + with open(self.__output_path + "/" + relative_filename, "w") as f : + f.write(output) + self.__save_config(type, real_config) @jinja2.contextfunction def has_value(context, name, value) : @@ -57,3 +82,11 @@ class Templator : def sha512_crypt(password) : return crypt.crypt(password, crypt.mksalt(crypt.METHOD_SHA512)) + + def is_custom_conf(folder) : + for filename in glob.iglob(folder + "/*.conf") : + return True + return False + + def random(number) : + return "".join(random.choices(string.ascii_uppercase + string.ascii_lowercase + string.digits, k=number)) diff --git a/settings.json b/settings.json index 2992aa9..31d3a77 100644 --- a/settings.json +++ b/settings.json @@ -749,6 +749,15 @@ "label": "Disable default server", "regex": "^(yes|no)$", "type": "checkbox" + }, + { + "context": "multisite", + "default": "", + "env": "ERRORS", + "id": "errors", + "label": "Custom error pages (code1=/page1 code2=/page2 ...)", + "regex": ".*", + "type": "text" } ] },