bunkerweb 1.4.0
This commit is contained in:
@@ -1,55 +1,135 @@
|
||||
import json, re, sys
|
||||
import sys, json, glob, re, os, traceback
|
||||
|
||||
sys.path.append("/opt/bunkerweb/utils")
|
||||
|
||||
from logger import log
|
||||
|
||||
class Configurator :
|
||||
|
||||
def __init__(self) :
|
||||
self.__settings = {}
|
||||
self.__variables = {}
|
||||
def __init__(self, settings, core, plugins, variables) :
|
||||
self.__settings = self.__load_settings(settings)
|
||||
self.__core = self.__load_plugins(core)
|
||||
self.__plugins = self.__load_plugins(plugins)
|
||||
self.__variables = self.__load_variables(variables)
|
||||
self.__multisite = False
|
||||
if "MULTISITE" in self.__variables and self.__variables["MULTISITE"] == "yes" :
|
||||
self.__multisite = True
|
||||
self.__servers = self.__map_servers()
|
||||
|
||||
def __map_servers(self) :
|
||||
if not self.__multisite or not "SERVER_NAME" in self.__variables :
|
||||
return {}
|
||||
servers = {}
|
||||
for server_name in self.__variables["SERVER_NAME"].split(" ") :
|
||||
if not re.search(self.__settings["SERVER_NAME"]["regex"], server_name) :
|
||||
log("GENERATOR", "⚠️", "Ignoring server name " + server_name + " because regex is not valid")
|
||||
continue
|
||||
names = [server_name]
|
||||
if server_name + "_SERVER_NAME" in self.__variables :
|
||||
if not re.search(self.__settings["SERVER_NAME"]["regex"], self.__variables[server_name + "_SERVER_NAME"]) :
|
||||
log("GENERATOR", "⚠️", "Ignoring " + server_name + "_SERVER_NAME because regex is not valid")
|
||||
else :
|
||||
names = self.__variables[server_name + "_SERVER_NAME"].split(" ")
|
||||
servers[server_name] = names
|
||||
return servers
|
||||
|
||||
def __load_settings(self, path) :
|
||||
with open(path) as f :
|
||||
return json.loads(f.read())
|
||||
|
||||
def __load_plugins(self, path) :
|
||||
plugins = {}
|
||||
files = glob.glob(path + "/*/plugin.json")
|
||||
for file in files :
|
||||
try :
|
||||
with open(file) as f :
|
||||
plugins.update(json.loads(f.read())["settings"])
|
||||
except :
|
||||
log("GENERATOR", "❌", "Exception while loading JSON from " + file + " :")
|
||||
print(traceback.format_exc())
|
||||
|
||||
return plugins
|
||||
|
||||
def __load_variables(self, path) :
|
||||
variables = {}
|
||||
with open(path) as f :
|
||||
lines = f.readlines()
|
||||
for line in lines :
|
||||
line = line.strip()
|
||||
if line.startswith("#") or line == "" or not "=" in line :
|
||||
continue
|
||||
var = line.split("=")[0]
|
||||
value = line[len(var)+1:]
|
||||
variables[var] = value
|
||||
return variables
|
||||
|
||||
def load_settings(self, path) :
|
||||
with open(path, "r") as f :
|
||||
data = json.loads(f.read())
|
||||
for cat in data :
|
||||
for param in data[cat]["params"] :
|
||||
if param["type"] == "multiple" :
|
||||
real_params = param["params"]
|
||||
else :
|
||||
real_params = [param]
|
||||
for real_param in real_params :
|
||||
self.__settings[real_param["env"]] = real_param
|
||||
self.__settings[real_param["env"]]["category"] = cat
|
||||
def get_config(self) :
|
||||
config = {}
|
||||
# Extract default settings
|
||||
default_settings = [self.__settings, self.__core, self.__plugins]
|
||||
for settings in default_settings :
|
||||
for setting, data in settings.items() :
|
||||
config[setting] = data["default"]
|
||||
# Override with variables
|
||||
for variable, value in self.__variables.items() :
|
||||
ret, err = self.__check_var(variable)
|
||||
if ret :
|
||||
config[variable] = value
|
||||
else :
|
||||
log("GENERATOR", "⚠️", "Ignoring variable " + variable + " : " + err)
|
||||
# Expand variables to each sites if MULTISITE=yes and if not present
|
||||
if config["MULTISITE"] == "yes" :
|
||||
for server_name in config["SERVER_NAME"].split(" ") :
|
||||
if server_name == "" :
|
||||
continue
|
||||
for settings in default_settings :
|
||||
for setting, data in settings.items() :
|
||||
if data["context"] == "global" :
|
||||
continue
|
||||
key = server_name + "_" + setting
|
||||
if setting == "SERVER_NAME" :
|
||||
if key not in config :
|
||||
config[key] = server_name
|
||||
continue
|
||||
if key not in config :
|
||||
config[key] = config[setting]
|
||||
return config
|
||||
|
||||
def load_variables(self, vars, multisite_only=False) :
|
||||
for var, value in vars.items() :
|
||||
check, reason = self.__check_var(var, value)
|
||||
if check :
|
||||
self.__variables[var] = value
|
||||
else :
|
||||
print("ignoring " + var + "=" + value + " (" + reason + ")", file=sys.stderr)
|
||||
|
||||
def get_config(self) :
|
||||
config = {}
|
||||
for setting in self.__settings :
|
||||
config[setting] = self.__settings[setting]["default"]
|
||||
for variable, value in self.__variables.items() :
|
||||
config[variable] = value
|
||||
return config
|
||||
|
||||
def __check_var(self, var, value, multisite_only=False) :
|
||||
real_var = ""
|
||||
if var in self.__settings :
|
||||
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:])
|
||||
if real_var == "" :
|
||||
return False, "doesn't exist"
|
||||
elif not re.search(self.__settings[real_var]["regex"], value) :
|
||||
return False, "doesn't match regex : " + self.__settings[real_var]["regex"]
|
||||
elif multisite_only and self.__settings[real_var]["context"] != "multisite" :
|
||||
return False, "not at multisite context"
|
||||
return True, ""
|
||||
def __check_var(self, variable) :
|
||||
value = self.__variables[variable]
|
||||
# MULTISITE=no
|
||||
if not self.__multisite :
|
||||
where, real_var = self.__find_var(variable)
|
||||
if not where :
|
||||
return False, "variable name " + variable + " doesn't exist"
|
||||
if not "regex" in where[real_var] :
|
||||
return False, "missing regex for variable " + variable
|
||||
if not re.search(where[real_var]["regex"], value) :
|
||||
return False, "value " + value + " doesn't match regex " + where[real_var]["regex"]
|
||||
return True, "ok"
|
||||
# MULTISITE=yes
|
||||
prefixed, real_var = self.__var_is_prefixed(variable)
|
||||
where, real_var = self.__find_var(real_var)
|
||||
if not where :
|
||||
return False, "variable name " + variable + " doesn't exist"
|
||||
if prefixed and where[real_var]["context"] != "multisite" :
|
||||
return False, "context of " + variable + " isn't multisite"
|
||||
if not re.search(where[real_var]["regex"], value) :
|
||||
return False, "value " + value + " doesn't match regex " + where[real_var]["regex"]
|
||||
return True, "ok"
|
||||
|
||||
def __find_var(self, variable) :
|
||||
targets = [self.__settings, self.__core, self.__plugins]
|
||||
for target in targets :
|
||||
if variable in target :
|
||||
return target, variable
|
||||
for real_var, settings in target.items() :
|
||||
if "multiple" in settings and re.search("^" + real_var + "_" + "[0-9]+$", variable) :
|
||||
return target, real_var
|
||||
return False, variable
|
||||
|
||||
def __var_is_prefixed(self, variable) :
|
||||
for server in self.__servers :
|
||||
if variable.startswith(server + "_") :
|
||||
return True, variable.replace(server + "_", "", 1)
|
||||
return False, variable
|
||||
22
gen/Dockerfile
Normal file
22
gen/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM python:3.10-alpine
|
||||
|
||||
COPY . /opt/bunkerweb
|
||||
|
||||
RUN addgroup -g 101 nginx && \
|
||||
adduser -h /opt/bunkerweb -g nginx -s /bin/sh -G nginx -D -H -u 101 nginx && \
|
||||
chown -R root:nginx /opt && \
|
||||
find /opt -type f -exec chmod 0740 {} \; && \
|
||||
find /opt -type d -exec chmod 0750 {} \; && \
|
||||
chmod 750 /opt/bunkerweb/gen/main.py && \
|
||||
pip3 install -r /opt/bunkerweb/gen/requirements.txt && \
|
||||
mkdir /etc/nginx /opt/bunkerweb/plugins && \
|
||||
chown root:nginx /etc/nginx /opt/bunkerweb/plugins && \
|
||||
chmod 770 /etc/nginx /opt/bunkerweb/plugins
|
||||
|
||||
WORKDIR /opt/bunkerweb/gen
|
||||
|
||||
USER nginx:nginx
|
||||
|
||||
VOLUME /etc/nginx /opt/bunkerweb/plugins
|
||||
|
||||
ENTRYPOINT ["python3", "/opt/bunkerweb/gen/main.py"]
|
||||
235
gen/Templator.py
235
gen/Templator.py
@@ -1,122 +1,135 @@
|
||||
import jinja2, glob, os, pathlib, copy, crypt, random, string
|
||||
import jinja2, glob, importlib, os, pathlib, copy, string, random
|
||||
|
||||
class Templator :
|
||||
|
||||
def __init__(self, config, input_path, output_path, target_path) :
|
||||
self.__config_global = copy.deepcopy(config)
|
||||
if config["MULTISITE"] == "yes" and config["SERVER_NAME"] != "" :
|
||||
self.__config_sites = {}
|
||||
for server_name in config["SERVER_NAME"].split(" ") :
|
||||
self.__config_sites[server_name] = {}
|
||||
for k, v in config.items() :
|
||||
if k.startswith(server_name + "_") :
|
||||
self.__config_sites[server_name][k.replace(server_name + "_", "", 1)] = v
|
||||
del self.__config_global[k]
|
||||
self.__input_path = input_path
|
||||
self.__output_path = output_path
|
||||
self.__target_path = target_path
|
||||
if not self.__target_path.endswith("/") :
|
||||
self.__target_path += "/"
|
||||
self.__template_env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=self.__input_path), lstrip_blocks=True, trim_blocks=True)
|
||||
def __init__(self, templates, core, plugins, output, target, config) :
|
||||
self.__templates = templates
|
||||
self.__core = core
|
||||
self.__plugins = plugins
|
||||
self.__output = output
|
||||
if not self.__output.endswith("/") :
|
||||
self.__output += "/"
|
||||
self.__target = target
|
||||
if not self.__target.endswith("/") :
|
||||
self.__target += "/"
|
||||
self.__config = config
|
||||
self.__jinja_env = self.__load_jinja_env()
|
||||
|
||||
def render_global(self) :
|
||||
return self.__render("global")
|
||||
def render(self) :
|
||||
self.__render_global()
|
||||
servers = [self.__config["SERVER_NAME"]]
|
||||
if self.__config["MULTISITE"] == "yes" :
|
||||
servers = self.__config["SERVER_NAME"].split(" ")
|
||||
for server in servers :
|
||||
self.__render_server(server)
|
||||
|
||||
def render_site(self, server_name=None, first_server=None) :
|
||||
if server_name is None :
|
||||
server_name = self.__config_global["SERVER_NAME"]
|
||||
if first_server is None :
|
||||
first_server = self.__config_global["SERVER_NAME"].split(" ")[0]
|
||||
return self.__render("site", server_name, first_server)
|
||||
def __load_jinja_env(self) :
|
||||
searchpath = [self.__templates]
|
||||
for subpath in glob.glob(self.__core + "/*") + glob.glob(self.__plugins + "/*") :
|
||||
if os.path.isdir(subpath) :
|
||||
searchpath.append(subpath + "/confs")
|
||||
return jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=searchpath), lstrip_blocks=True, trim_blocks=True)
|
||||
|
||||
def __prepare_config(self, type, server_name=None, first_server=None) :
|
||||
real_config = copy.deepcopy(self.__config_global)
|
||||
if type == "site" and self.__config_global["MULTISITE"] == "yes" :
|
||||
site_config = copy.deepcopy(self.__config_sites[first_server])
|
||||
real_config.update(site_config)
|
||||
elif type == "global" and self.__config_global["MULTISITE"] == "yes" and self.__config_global["SERVER_NAME"] != "" :
|
||||
for k, v in self.__config_sites.items() :
|
||||
for k2, v2 in v.items() :
|
||||
real_config[k + "_" + k2] = v2
|
||||
if not server_name is None :
|
||||
real_config["SERVER_NAME"] = server_name
|
||||
if not first_server is None :
|
||||
real_config["FIRST_SERVER"] = first_server
|
||||
real_config["NGINX_PREFIX"] = self.__target_path
|
||||
if self.__config_global["MULTISITE"] == "yes" and type == "site" :
|
||||
real_config["NGINX_PREFIX"] += first_server + "/"
|
||||
if not real_config["ROOT_FOLDER"].endswith("/" + first_server) :
|
||||
real_config["ROOT_FOLDER"] += "/" + first_server
|
||||
if real_config["ROOT_SITE_SUBFOLDER"] != "" :
|
||||
real_config["ROOT_FOLDER"] += "/" + real_config["ROOT_SITE_SUBFOLDER"]
|
||||
return real_config
|
||||
def __find_templates(self, contexts) :
|
||||
templates = []
|
||||
for template in self.__jinja_env.list_templates() :
|
||||
if "global" in contexts and "/" not in template :
|
||||
templates.append(template)
|
||||
continue
|
||||
for context in contexts :
|
||||
if template.startswith(context + "/") :
|
||||
templates.append(template)
|
||||
return templates
|
||||
|
||||
def __filter_var(self, variable) :
|
||||
filters = ["FIRST_SERVER", "NGINX_PREFIX"]
|
||||
for filter in filters :
|
||||
if variable == filter or variable.endswith("_" + filter) :
|
||||
return True
|
||||
return False
|
||||
def __write_config(self, subpath=None, config=None) :
|
||||
real_path = self.__output
|
||||
if subpath != None :
|
||||
real_path += subpath + "/"
|
||||
real_path += "variables.env"
|
||||
real_config = self.__config
|
||||
if config != None :
|
||||
real_config = config
|
||||
pathlib.Path(os.path.dirname(real_path)).mkdir(parents=True, exist_ok=True)
|
||||
with open(real_path, "w") as f :
|
||||
for k, v in real_config.items() :
|
||||
f.write(k + "=" + v + "\n")
|
||||
|
||||
def __save_config(self, type, config) :
|
||||
first_servers = config["SERVER_NAME"].split(" ")
|
||||
data = ""
|
||||
for variable, value in config.items() :
|
||||
if self.__filter_var(variable) :
|
||||
continue
|
||||
add = True
|
||||
if type == "global" :
|
||||
for first_server in first_servers :
|
||||
if variable.startswith(first_server + "_") :
|
||||
add = False
|
||||
break
|
||||
if add :
|
||||
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_global(self) :
|
||||
self.__write_config()
|
||||
templates = self.__find_templates(["global", "http", "stream", "default-server-http"])
|
||||
for template in templates :
|
||||
self.__render_template(template)
|
||||
|
||||
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 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)
|
||||
def __render_server(self, server) :
|
||||
templates = self.__find_templates(["modsec", "modsec-crs", "server-http", "server-stream"])
|
||||
if self.__config["MULTISITE"] == "yes" :
|
||||
config = copy.deepcopy(self.__config)
|
||||
for variable, value in self.__config.items() :
|
||||
if variable.startswith(server + "_") :
|
||||
config[variable.replace(server + "_", "", 1)] = value
|
||||
self.__write_config(subpath=server, config=config)
|
||||
for template in templates :
|
||||
subpath = None
|
||||
config = None
|
||||
name = None
|
||||
if self.__config["MULTISITE"] == "yes" :
|
||||
subpath = server
|
||||
config = copy.deepcopy(self.__config)
|
||||
for variable, value in self.__config.items() :
|
||||
if variable.startswith(server + "_") :
|
||||
config[variable.replace(server + "_", "", 1)] = value
|
||||
config["NGINX_PREFIX"] = self.__target + server + "/"
|
||||
server_key = server + "_SERVER_NAME"
|
||||
if server_key not in self.__config :
|
||||
config["SERVER_NAME"] = server
|
||||
root_confs = ["server.conf", "access-lua.conf", "init-lua.conf", "log-lua.conf"]
|
||||
for root_conf in root_confs :
|
||||
if template.endswith("/" + root_conf) :
|
||||
name = os.path.basename(template)
|
||||
break
|
||||
self.__render_template(template, subpath=subpath, config=config, name=name)
|
||||
|
||||
@jinja2.contextfunction
|
||||
def has_value(context, name, value) :
|
||||
for k, v in context.items() :
|
||||
if (k == name or k.endswith("_" + name)) and v == value :
|
||||
return True
|
||||
return False
|
||||
def __render_template(self, template, subpath=None, config=None, name=None) :
|
||||
# Get real config and output folder in case it's a server config and we are in multisite mode
|
||||
real_config = copy.deepcopy(self.__config)
|
||||
if config :
|
||||
real_config = copy.deepcopy(config)
|
||||
real_config["all"] = copy.deepcopy(real_config)
|
||||
real_config["import"] = importlib.import_module
|
||||
real_config["is_custom_conf"] = Templator.is_custom_conf
|
||||
real_config["has_variable"] = Templator.has_variable
|
||||
real_config["random"] = Templator.random
|
||||
real_config["read_lines"] = Templator.read_lines
|
||||
real_output = self.__output
|
||||
if subpath :
|
||||
real_output += "/" + subpath + "/"
|
||||
real_name = template
|
||||
if name :
|
||||
real_name = name
|
||||
jinja_template = self.__jinja_env.get_template(template)
|
||||
pathlib.Path(os.path.dirname(real_output + real_name)).mkdir(parents=True, exist_ok=True)
|
||||
with open(real_output + real_name, "w") as f :
|
||||
f.write(jinja_template.render(real_config))
|
||||
|
||||
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))
|
||||
def is_custom_conf(path) :
|
||||
return glob.glob(path + "/*.conf")
|
||||
|
||||
def has_variable(all_vars, variable, value) :
|
||||
if variable in all_vars and all_vars[variable] == value :
|
||||
return True
|
||||
if all_vars["MULTISITE"] == "yes" :
|
||||
for server_name in all_vars["SERVER_NAME"].split(" ") :
|
||||
if server_name + "_" + variable in all_vars and all_vars[server_name + "_" + variable] == value :
|
||||
return True
|
||||
return False
|
||||
|
||||
def random(nb) :
|
||||
characters = string.ascii_letters + string.digits
|
||||
return "".join(random.choice(characters) for i in range(nb))
|
||||
|
||||
def read_lines(file) :
|
||||
try :
|
||||
with open(file, "r") as f :
|
||||
return f.readlines()
|
||||
except :
|
||||
return []
|
||||
|
||||
152
gen/main.py
Executable file → Normal file
152
gen/main.py
Executable file → Normal file
@@ -1,84 +1,92 @@
|
||||
#!/usr/bin/python3
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse, os, sys, shutil, glob
|
||||
import argparse, os, sys, shutil, glob, traceback
|
||||
|
||||
import sys
|
||||
sys.path.append("/opt/bunkerweb/deps/python")
|
||||
sys.path.append("/opt/bunkerweb/gen")
|
||||
sys.path.append("/opt/bunkerweb/utils")
|
||||
|
||||
from logger import log
|
||||
|
||||
import utils
|
||||
from Configurator import Configurator
|
||||
from Templator import Templator
|
||||
|
||||
if __name__ == "__main__" :
|
||||
|
||||
# Parse arguments
|
||||
parser = argparse.ArgumentParser(description="bunkerized-nginx config generator v1.0")
|
||||
parser.add_argument("--settings", default="/opt/settings.json", type=str, help="path to the files containing the default settings")
|
||||
parser.add_argument("--templates", default="/opt/confs", type=str, help="directory containing the templates files")
|
||||
parser.add_argument("--output", default="/etc/nginx", type=str, help="where to write the rendered files")
|
||||
parser.add_argument("--target", default="/etc/nginx", type=str, help="where nginx will search for configurations files")
|
||||
parser.add_argument("--variables", default="/opt/variables.env", type=str, help="path to the file containing environment variables")
|
||||
args = parser.parse_args()
|
||||
try :
|
||||
|
||||
# Check existences and permissions
|
||||
if not os.path.exists(args.settings) :
|
||||
print("[!] Missing settings file : " + args.settings)
|
||||
sys.exit(1)
|
||||
if not os.access(args.settings, os.R_OK) :
|
||||
print("[!] Can't read settings file : " + args.settings)
|
||||
sys.exit(2)
|
||||
if not os.path.isdir(args.templates) :
|
||||
print("[!] Missing templates directory : " + args.templates)
|
||||
sys.exit(1)
|
||||
if not os.access(args.templates, os.R_OK | os.X_OK) :
|
||||
print("[!] Can't read the templates directory : " + args.templates)
|
||||
sys.exit(2)
|
||||
if not os.path.isdir(args.output) :
|
||||
print("[!] Missing output directory : " + args.output)
|
||||
sys.exit(1)
|
||||
if not os.access(args.output, os.W_OK | os.X_OK) :
|
||||
print("[!] Can't write to the templates directory : " + args.output)
|
||||
sys.exit(2)
|
||||
if not os.path.exists(args.variables) :
|
||||
print("[!] Missing variables file : " + args.variables)
|
||||
sys.exit(1)
|
||||
if not os.access(args.variables, os.R_OK) :
|
||||
print("[!] Can't read variables file : " + args.variables)
|
||||
sys.exit(2)
|
||||
# Parse arguments
|
||||
parser = argparse.ArgumentParser(description="BunkerWeb config generator")
|
||||
parser.add_argument("--settings", default="/opt/bunkerweb/settings.json", type=str, help="file containing the main settings")
|
||||
parser.add_argument("--templates", default="/opt/bunkerweb/confs", type=str, help="directory containing the main template files")
|
||||
parser.add_argument("--core", default="/opt/bunkerweb/core", type=str, help="directory containing the core plugins")
|
||||
parser.add_argument("--plugins", default="/opt/bunkerweb/plugins", type=str, help="directory containing the external plugins")
|
||||
parser.add_argument("--output", default="/etc/nginx", type=str, help="where to write the rendered files")
|
||||
parser.add_argument("--target", default="/etc/nginx", type=str, help="where nginx will search for configurations files")
|
||||
parser.add_argument("--variables", default="/opt/bunkerweb/variables.env", type=str, help="path to the file containing environment variables")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Compute the final config
|
||||
configurator = Configurator()
|
||||
configurator.load_settings(args.settings)
|
||||
variables = utils.load_variables(args.variables)
|
||||
configurator.load_variables(variables)
|
||||
config = configurator.get_config()
|
||||
log("GENERATOR", "ℹ️", "Generator started ...")
|
||||
log("GENERATOR", "ℹ️", "Settings : " + args.settings)
|
||||
log("GENERATOR", "ℹ️", "Templates : " + args.templates)
|
||||
log("GENERATOR", "ℹ️", "Core : " + args.core)
|
||||
log("GENERATOR", "ℹ️", "Plugins : " + args.plugins)
|
||||
log("GENERATOR", "ℹ️", "Output : " + args.output)
|
||||
log("GENERATOR", "ℹ️", "Target : " + args.target)
|
||||
log("GENERATOR", "ℹ️", "Variables : " + args.variables)
|
||||
|
||||
# Remove old files
|
||||
files = glob.glob(args.output + "/*")
|
||||
for file in files :
|
||||
if (file.endswith(".conf") or file.endswith(".env")) and os.path.isfile(file) and not os.path.islink(file) :
|
||||
os.remove(file)
|
||||
elif os.path.isdir(file) and not os.path.islink(file) :
|
||||
shutil.rmtree(file, ignore_errors=False)
|
||||
# Check existences and permissions
|
||||
log("GENERATOR", "ℹ️", "Checking arguments ...")
|
||||
files = [args.settings, args.variables]
|
||||
paths_rx = [args.core, args.plugins, args.templates]
|
||||
paths_rwx = [args.output]
|
||||
for file in files :
|
||||
if not os.path.exists(file) :
|
||||
log("GENERATOR", "❌", "Missing file : " + file)
|
||||
sys.exit(1)
|
||||
if not os.access(file, os.R_OK) :
|
||||
log("GENERATOR", "❌", "Can't read file : " + file)
|
||||
sys.exit(1)
|
||||
for path in paths_rx + paths_rwx :
|
||||
if not os.path.isdir(path) :
|
||||
log("GENERATOR", "❌", "Missing directory : " + path)
|
||||
sys.exit(1)
|
||||
if not os.access(path, os.R_OK | os.X_OK) :
|
||||
log("GENERATOR", "❌", "Missing RX rights on directory : " + path)
|
||||
sys.exit(1)
|
||||
for path in paths_rwx :
|
||||
if not os.access(path, os.W_OK) :
|
||||
log("GENERATOR", "❌", "Missing W rights on directory : " + path)
|
||||
sys.exit(1)
|
||||
|
||||
# Generate the files from templates and config
|
||||
templator = Templator(config, args.templates, args.output, args.target)
|
||||
templator.render_global()
|
||||
if config["MULTISITE"] == "no" :
|
||||
templator.render_site()
|
||||
elif config["SERVER_NAME"] != "" :
|
||||
# Compute a dict of first_server: [list of server_name]
|
||||
map_servers = {}
|
||||
for server_name in config["SERVER_NAME"].split(" ") :
|
||||
if server_name + "_SERVER_NAME" in config :
|
||||
map_servers[server_name] = config[server_name + "_SERVER_NAME"].split(" ")
|
||||
for server_name in config["SERVER_NAME"].split(" ") :
|
||||
if server_name in map_servers :
|
||||
continue
|
||||
for first_server, servers in map_servers.items() :
|
||||
if server_name in servers :
|
||||
continue
|
||||
map_servers[server_name] = [server_name]
|
||||
for first_server, servers in map_servers.items() :
|
||||
templator.render_site(" ".join(servers), first_server)
|
||||
# Compute the config
|
||||
log("GENERATOR", "ℹ️", "Computing config ...")
|
||||
configurator = Configurator(args.settings, args.core, args.plugins, args.variables)
|
||||
config = configurator.get_config()
|
||||
|
||||
# We're done
|
||||
print("[*] Generation done !")
|
||||
sys.exit(0)
|
||||
# Remove old files
|
||||
log("GENERATOR", "ℹ️", "Removing old files ...")
|
||||
files = glob.glob(args.output + "/*")
|
||||
for file in files :
|
||||
if os.path.islink(file) :
|
||||
os.unlink(file)
|
||||
elif os.path.isfile(file) :
|
||||
os.remove(file)
|
||||
elif os.path.isdir(file) :
|
||||
shutil.rmtree(file, ignore_errors=False)
|
||||
|
||||
# Render the templates
|
||||
log("GENERATOR", "ℹ️", "Rendering templates ...")
|
||||
templator = Templator(args.templates, args.core, args.plugins, args.output, args.target, config)
|
||||
templator.render()
|
||||
|
||||
# We're done
|
||||
log("GENERATOR", "ℹ️", "Generator successfully executed !")
|
||||
sys.exit(0)
|
||||
|
||||
except SystemExit as e :
|
||||
sys.exit(e)
|
||||
except :
|
||||
log("GENERATOR", "❌", "Exception while executing generator : " + traceback.format_exc())
|
||||
sys.exit(1)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
jinja2
|
||||
@@ -1 +0,0 @@
|
||||
./main.py --settings /opt/work/bunkerized-nginx/settings.json --templates /opt/work/bunkerized-nginx/confs --output /tmp/debug --variables /tmp/variables.env
|
||||
42
gen/utils.py
42
gen/utils.py
@@ -1,12 +1,32 @@
|
||||
def load_variables(path) :
|
||||
variables = {}
|
||||
with open(path) as f :
|
||||
lines = f.read().splitlines()
|
||||
for line in lines :
|
||||
if line.startswith("#") or line == "" or not "=" in line :
|
||||
continue
|
||||
var = line.split("=")[0]
|
||||
value = line[len(var)+1:]
|
||||
variables[var] = value
|
||||
return variables
|
||||
import os, stat
|
||||
|
||||
def has_permissions(path, need_permissions) :
|
||||
uid = os.geteuid()
|
||||
gid = os.getegid()
|
||||
statinfo = os.stat(path)
|
||||
permissions = {"R": False, "W": False, "X": False}
|
||||
if statinfo.st_uid == uid :
|
||||
if statinfo.st_mode & stat.S_IRUSR :
|
||||
permissions["R"] = True
|
||||
if statinfo.st_mode & stat.S_IWUSR :
|
||||
permissions["W"] = True
|
||||
if statinfo.st_mode & stat.S_IXUSR :
|
||||
permissions["X"] = True
|
||||
if statinfo.st_uid == gid :
|
||||
if statinfo.st_mode & stat.S_IRGRP :
|
||||
permissions["R"] = True
|
||||
if statinfo.st_mode & stat.S_IWGRP :
|
||||
permissions["W"] = True
|
||||
if statinfo.st_mode & stat.S_IXGRP :
|
||||
permissions["X"] = True
|
||||
if statinfo.st_mode & stat.S_IROTH :
|
||||
permissions["R"] = True
|
||||
if statinfo.st_mode & stat.S_IWOTH :
|
||||
permissions["W"] = True
|
||||
if statinfo.st_mode & stat.S_IXOTH :
|
||||
permissions["X"] = True
|
||||
list_permissions = [permission for permission in need_permissions]
|
||||
for need_permission in list_permissions :
|
||||
if not permissions[need_permission] :
|
||||
return False
|
||||
return True
|
||||
Reference in New Issue
Block a user