diff --git a/CHANGELOG.md b/CHANGELOG.md index cc1782b..0b07210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## v1.2.8 - 2021/07/22 + +- Fix broken links in README +- Fix regex for EMAIL_LETS_ENCRYPT +- Fix regex for REMOTE_PHP and REMOTE_PHP_PATH +- Fix regex for SELF_SIGNED_* +- Fix various bugs related to web UI +- Fix bug in autoconf (missing instances parameter to reload function) +- Remove old .env files when generating a new configuration +- + ## v1.2.7 - 2021/06/14 - Add custom robots.txt and sitemap to RTD diff --git a/README.md b/README.md index d08f2e5..e66fa38 100644 --- a/README.md +++ b/README.md @@ -432,8 +432,8 @@ bunkerized-nginx comes with a set of predefined security settings that you can ( # License -This project is licensed under the terms of the [GNU Affero General Public License (AGPL) version 3](https://github.com/bunkerity/bunkerized-nginx/LICENSE.md). +This project is licensed under the terms of the [GNU Affero General Public License (AGPL) version 3](https://github.com/bunkerity/bunkerized-nginx/blob/master/LICENSE.md). # Contributing -If you would like to contribute to the project you can read the [contributing guidelines](https://github.com/bunkerity/bunkerized-nginx/CONTRIBUTING.md) to get started. +If you would like to contribute to the project you can read the [contributing guidelines](https://github.com/bunkerity/bunkerized-nginx/blob/master/CONTRIBUTING.md) to get started. diff --git a/autoconf/AutoConf.py b/autoconf/AutoConf.py index cea1b83..5651e6b 100644 --- a/autoconf/AutoConf.py +++ b/autoconf/AutoConf.py @@ -173,7 +173,7 @@ class AutoConf : self.__servers[id].reload() utils.log("[*] Deactivating config for " + vars["SERVER_NAME"]) self.__gen_env() - if self.__config.reload() : + if self.__config.reload(self.__instances) : utils.log("[*] Deactivated config for " + vars["SERVER_NAME"]) else : utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"]) diff --git a/gen/main.py b/gen/main.py index 6729740..0f0d728 100755 --- a/gen/main.py +++ b/gen/main.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -import argparse, os, sys, shutil +import argparse, os, sys, shutil, glob import utils from Configurator import Configurator @@ -51,14 +51,10 @@ if __name__ == "__main__" : config = configurator.get_config() #print(config) - # Remove old config - # TODO : remove unnecessary files after rendering -# for filename in os.listdir(args.output): -# file_path = os.path.join(args.output, filename) -# if os.path.isfile(file_path) or os.path.islink(file_path): -# os.unlink(file_path) -# elif os.path.isdir(file_path): -# shutil.rmtree(file_path) + # TODO : find a proper way to remove old sites + env_list = glob.glob(args.output + "/**/*.env", recursive=True) + for env in env_list : + os.remove(env) # Generate the files from templates and config templator = Templator(config, args.templates, args.output, args.target) diff --git a/settings.json b/settings.json index 2c64861..114177e 100644 --- a/settings.json +++ b/settings.json @@ -525,7 +525,7 @@ "env": "EMAIL_LETS_ENCRYPT", "id": "email-lets-encrypt", "label": "Email lets encrypt", - "regex": "^([a-z0-9\\-\\.]+@([a-z\\-0-9]+\\.?)|.{0})$", + "regex": "^([a-z0-9\\-\\.]+@[a-z\\-0-9\\.]+|.{0})$", "type": "text" }, { @@ -615,7 +615,7 @@ "env": "SELF_SIGNED_SSL_COUNTRY", "id": "self-signed-ssl-country", "label": "Country of the self-signed certificate", - "regex": "^[:print:]+$", + "regex": "^[A-Z]{2}$", "type": "text" }, { @@ -624,7 +624,7 @@ "env": "SELF_SIGNED_SSL_STATE", "id": "self-signed-ssl-state", "label": "State of the self-signed certificate", - "regex": "^[:print:]+$", + "regex": "^[A-Za-z\\- ]+$", "type": "text" }, { @@ -633,7 +633,7 @@ "env": "SELF_SIGNED_SSL_CITY", "id": "self-signed-ssl-city", "label": "City of the self-signed certificate", - "regex": "^[:print:]+$", + "regex": "^[A-Za-z\\- ]+$", "type": "text" }, { @@ -642,7 +642,7 @@ "env": "SELF_SIGNED_SSL_OU", "id": "self-signed-ssl-ou", "label": "Organizational Unit of the self-signed certificate", - "regex": "^[:print:]+$", + "regex": "^[A-Za-z\\- ]+$", "type": "text" }, { @@ -651,7 +651,7 @@ "env": "SELF_SIGNED_SSL_ORG", "id": "self-signed-ssl-org", "label": "Organization name of the self-signed certificate", - "regex": "^[:print:]+$", + "regex": "^[A-Za-z\\- ]+$", "type": "text" }, { @@ -660,7 +660,7 @@ "env": "SELF_SIGNED_SSL_CN", "id": "self-signed-ssl-cn", "label": "Common Name of the self-signed certificate", - "regex": "^[:print:]+$", + "regex": "^[A-Za-z\\-\\.0-9]+$", "type": "text" } ] @@ -947,7 +947,7 @@ "env": "REMOTE_PHP", "id": "remote-php", "label": "Remote php", - "regex": "^([a-z\\-0-9\\_]+\\.?)*$", + "regex": "^[a-z\\-0-9_\\.]*$", "type": "text" }, { @@ -956,7 +956,7 @@ "env": "REMOTE_PHP_PATH", "id": "remote-php-path", "label": "Remote php path", - "regex": "^/([A-Za-z0-9\\-]/?)*$", + "regex": "^\\/[a-zA-Z\\-0-9_\\.\\/]*$", "type": "text" } ] diff --git a/ui/Config.py b/ui/Config.py index 7d8ad88..9ef0433 100644 --- a/ui/Config.py +++ b/ui/Config.py @@ -1,113 +1,121 @@ -import json, uuid, glob, copy, re, subprocess +import json, uuid, glob, copy, re, subprocess, os class Config : - def __init__(self) : - with open("/opt/settings.json", "r") as f : - self.__settings = json.loads(f.read()) + def __init__(self) : + with open("/opt/settings.json", "r") as f : + self.__settings = json.loads(f.read()) - def __env_to_dict(self, filename) : - with open(filename, "r") as f : - env = f.read() - data = {} - for line in env.split("\n") : - var = line.split("=")[0] - val = line.replace(var + "=", "", 1) - data[var] = val - return data + def __env_to_dict(self, filename) : + if not os.path.isfile(filename) : + return {} + with open(filename, "r") as f : + env = f.read() + data = {} + for line in env.split("\n") : + var = line.split("=")[0] + val = line.replace(var + "=", "", 1) + data[var] = val + return data - def __dict_to_env(self, filename, variables) : - env = "" - for k, v in variables.items() : - env += k + "=" + v + "\n" - with open(filename, "w") as f : - f.write(env) + def __dict_to_env(self, filename, variables) : + env = "" + for k, v in variables.items() : + env += k + "=" + v + "\n" + with open(filename, "w") as f : + f.write(env) - def __gen_conf(self, global_conf, services_conf) : - conf = copy.deepcopy(global_conf) - servers = conf["SERVER_NAME"].split(" ") - if conf["SERVER_NAME"] == "" : - servers = [] - for service in services_conf : - first_server = service["SERVER_NAME"].split(" ")[0] - if not first_server in servers : - servers.append(first_server) - for k, v in service.items() : - conf[first_server + "_" + k] = v - conf["SERVER_NAME"] = " ".join(servers) - env_file = "/tmp/" + str(uuid.uuid4()) + ".env" - self.__dict_to_env(env_file, conf) - proc = subprocess.run(["/bin/su", "-c", "/opt/gen/main.py --settings /opt/settings.json --templates /opt/confs --output /etc/nginx --variables " + env_file, "nginx"], capture_output=True) - stderr = proc.stderr.decode("ascii") - #stdout = proc.stdout.decode("ascii") - if stderr != "" or proc.returncode != 0 : - raise Exception("Error from generator (return code = " + str(proc.returncode) + ") : " + stderr) + def __gen_conf(self, global_conf, services_conf) : + conf = copy.deepcopy(global_conf) + if not "SERVER_NAME" in conf : + conf["SERVER_NAME"] = "" + servers = conf["SERVER_NAME"].split(" ") + if conf["SERVER_NAME"] == "" : + servers = [] + for service in services_conf : + first_server = service["SERVER_NAME"].split(" ")[0] + if not first_server in servers : + servers.append(first_server) + for k, v in service.items() : + conf[first_server + "_" + k] = v + conf["SERVER_NAME"] = " ".join(servers) + env_file = "/tmp/" + str(uuid.uuid4()) + ".env" + self.__dict_to_env(env_file, conf) + proc = subprocess.run(["/opt/gen/main.py", "--settings", "/opt/settings.json", "--templates", "/opt/confs", "--output", "/etc/nginx", "--variables", env_file], capture_output=True) + stderr = proc.stderr.decode("ascii") + stdout = proc.stdout.decode("ascii") + if stderr != "" or proc.returncode != 0 : + raise Exception("Error from generator (return code = " + str(proc.returncode) + ") : " + stderr + "\n" + stdout) - def get_settings(self) : - return self.__settings + def get_settings(self) : + return self.__settings - def get_config(self) : - return self.__env_to_dict("/etc/nginx/global.env") + def get_config(self) : + return self.__env_to_dict("/etc/nginx/global.env") - def get_services(self) : - services = [] - for filename in glob.iglob("/etc/nginx/**/site.env") : - env = self.__env_to_dict(filename) - services.append(env) - return services + def get_services(self) : + services = [] + for filename in glob.iglob("/etc/nginx/**/site.env") : + env = self.__env_to_dict(filename) + services.append(env) + no_multisite = self.__env_to_dict("/etc/nginx/site.env") + if len(no_multisite) > 0 : + services.append(no_multisite) + return services - def check_variables(self, variables) : - for k, v in variables.items() : - check = False - for category in self.__settings : - for param in self.__settings[category]["params"] : - multiple = False - if param["type"] != "multiple" : - real_params = [param] - else : - real_params = param["params"] - multiple = True - for real_param in real_params : - if (((not multiple and k == real_param["env"]) or - (multiple and re.search("^" + real_param["env"] + "_" + "[0-9]+$", k))) and - real_param["context"] == "multisite" and - re.search(real_param["regex"], v)) : - check = True - if not check : - raise Exception("Variable " + k + " is not valid.") + def check_variables(self, variables) : + for k, v in variables.items() : + check = False + for category in self.__settings : + for param in self.__settings[category]["params"] : + multiple = False + if param["type"] != "multiple" : + real_params = [param] + else : + real_params = param["params"] + multiple = True + for real_param in real_params : + if (((not multiple and k == real_param["env"]) or + (multiple and re.search("^" + real_param["env"] + "_" + "[0-9]+$", k))) and + real_param["context"] == "multisite" and + re.search(real_param["regex"], v)) : + check = True + if not check : + raise Exception("Variable " + k + " is not valid.") - def new_service(self, variables) : - global_env = self.__env_to_dict("/etc/nginx/global.env") - services = self.get_services() - for service in services : - if service["SERVER_NAME"] == variables["SERVER_NAME"] or service["SERVER_NAME"] in variables["SERVER_NAME"].split(" ") : - raise Exception("Service " + service["SERVER_NAME"] + " already exists.") - services.append(variables) - self.__gen_conf(global_env, services) - return "Configuration for " + variables["SERVER_NAME"] + " has been generated." + def new_service(self, variables) : + global_env = self.__env_to_dict("/etc/nginx/global.env") + services = self.get_services() + for service in services : + if service["SERVER_NAME"] == variables["SERVER_NAME"] or service["SERVER_NAME"] in variables["SERVER_NAME"].split(" ") : + raise Exception("Service " + service["SERVER_NAME"] + " already exists.") + services.append(variables) + self.__gen_conf(global_env, services) + return "Configuration for " + variables["SERVER_NAME"] + " has been generated." - def edit_service(self, old_server_name, variables) : - self.delete_service(old_server_name) - self.new_service(variables) - return "Configuration for " + old_server_name + " has been edited." + def edit_service(self, old_server_name, variables) : + self.delete_service(old_server_name) + self.new_service(variables) + return "Configuration for " + old_server_name + " has been edited." - def delete_service(self, server_name) : - global_env = self.__env_to_dict("/etc/nginx/global.env") - services = self.get_services() - new_services = [] - found = False - for service in services : - if service["SERVER_NAME"].split(" ")[0] == server_name : - found = True - else : - new_services.append(service) - if not found : - raise Exception("Can't delete missing " + server_name + " configuration.") - new_servers = global_env["SERVER_NAME"].split(" ") - if server_name in new_servers : - new_servers.remove(server_name) - global_env["SERVER_NAME"] = " ".join(new_servers) - self.__gen_conf(global_env, new_services) - return "Configuration for " + server_name + " has been deleted." + def delete_service(self, server_name) : + global_env = self.__env_to_dict("/etc/nginx/global.env") + services = self.get_services() + new_services = [] + found = False + for service in services : + if service["SERVER_NAME"].split(" ")[0] == server_name : + found = True + else : + new_services.append(service) + if not found : + raise Exception("Can't delete missing " + server_name + " configuration.") + new_servers = global_env["SERVER_NAME"].split(" ") + if server_name in new_servers : + new_servers.remove(server_name) + global_env["SERVER_NAME"] = " ".join(new_servers) + self.__gen_conf(global_env, new_services) + return "Configuration for " + server_name + " has been deleted." + diff --git a/ui/Docker.py b/ui/Docker.py index 2facce6..aeafb8a 100644 --- a/ui/Docker.py +++ b/ui/Docker.py @@ -17,8 +17,10 @@ class Docker : return self.__client.containers.get(id) def reload_instance(self, id) : - self.get_instance(id).kill(signal="SIGHUP") - return "Instance " + id + " has been reloaded." + if self.get_instance(id).status == "running" : + self.get_instance(id).kill(signal="SIGHUP") + return "Instance " + id + " has been reloaded." + return "Instance " + id + " is not running, skipping reload." def start_instance(self, id) : self.get_instance(id).start() diff --git a/ui/templates/services-new.html b/ui/templates/services-new.html index c5285d7..2957809 100644 --- a/ui/templates/services-new.html +++ b/ui/templates/services-new.html @@ -25,7 +25,7 @@ {% if param["type"] != "multiple" and param["context"] == "multisite" %}
{% set default = {"value": param["default"]} %} - {% if param["env"] in config["CONFIG"].get_config() %} + {% if param["env"] in config["CONFIG"].get_config() and param["env"] != "SERVER_NAME" %} {% set x = default.update({"value": config["CONFIG"].get_config()[param["env"]]}) %} {% endif %} {{ form_service_gen("form-new-" + param["id"], param["label"], param["type"], default["value"], param["env"])|safe }} @@ -57,4 +57,4 @@
- \ No newline at end of file + diff --git a/ui/templates/services.html b/ui/templates/services.html index 7b06486..2517af4 100644 --- a/ui/templates/services.html +++ b/ui/templates/services.html @@ -58,12 +58,13 @@ - {% include "services-new.html" %} {% include "services-edit.html" %} {% include "services-delete.html" %} {% endfor %} + {% include "services-new.html" %} + {% endblock %}