Merge pull request #366 from TheophileDiot/dev

Add a few fixes for the 1.5
This commit is contained in:
Théophile Diot 2022-11-20 18:51:06 +01:00 committed by GitHub
commit 2889b2638f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 162 additions and 137 deletions

View file

@ -9,4 +9,5 @@ CHANGELOG.md
CONTRIBUTING.md
LICENSE.md
README.md
SECURITY.md
SECURITY.md
ui

View file

@ -111,6 +111,6 @@ try:
except:
status = 2
logger.error(f"Exception while running certbot-new.py :\n{format_exc()}")
logger.error(f"Exception while running self-signed.py :\n{format_exc()}")
sys_exit(status)

View file

@ -330,18 +330,17 @@ class Database:
global_values = []
db_services = (
session.query(Services)
.with_entities(Services.id)
.filter_by(method=method)
.with_entities(Services.id, Services.method)
.all()
)
db_ids = [service.id for service in db_services]
services = config.get("SERVER_NAME", "").split(" ")
if db_services:
db_services = [service.id for service in db_services]
missing_ids = [
service
service.id
for service in db_services
if service not in services
if (service.method == method) and service.id not in services
]
if missing_ids:
@ -373,9 +372,9 @@ class Database:
except StopIteration:
continue
if server_name not in db_services:
if server_name not in db_ids:
to_put.append(Services(id=server_name, method=method))
db_services.append(server_name)
db_ids.append(server_name)
key = key.replace(f"{server_name}_", "")
setting = (
@ -471,7 +470,16 @@ class Database:
}
)
else:
if "SERVER_NAME" in config and config["SERVER_NAME"] != "":
if (
"SERVER_NAME" in config
and config["SERVER_NAME"] != ""
and not (
session.query(Services)
.with_entities(Services.id)
.filter_by(id=config["SERVER_NAME"].split(" ")[0])
.first()
)
):
to_put.append(
Services(
id=config["SERVER_NAME"].split(" ")[0], method=method
@ -640,96 +648,80 @@ class Database:
)
.all()
):
suffix = 0
while True:
global_value = (
session.query(Global_values)
.with_entities(Global_values.value, Global_values.method)
.filter_by(setting_id=setting.id, suffix=suffix)
.first()
default = setting.default or ""
config[setting.id] = (
default
if methods is False
else {"value": default, "method": "default"}
)
global_values = (
session.query(Global_values)
.with_entities(
Global_values.value, Global_values.suffix, Global_values.method
)
.filter_by(setting_id=setting.id)
.all()
)
for global_value in global_values:
config[
setting.id
+ (
f"_{global_value.suffix}"
if setting.multiple and global_value.suffix > 0
else ""
)
] = (
global_value.value
if methods is False
else {
"value": global_value.value,
"method": global_value.method,
}
)
if global_value is None:
if suffix == 0:
default = setting.default or ""
config[setting.id] = (
default
if methods is False
else {"value": default, "method": "default"}
if setting.context != "multisite":
continue
for service in db_services:
config[f"{service.id}_{setting.id}"] = (
config[setting.id]
if methods is False
else {
"value": config[setting.id]["value"],
"method": "default",
}
)
service_settings = (
session.query(Services_settings)
.with_entities(
Services_settings.value,
Services_settings.suffix,
Services_settings.method,
)
.filter_by(service_id=service.id, setting_id=setting.id)
.all()
)
for service_setting in service_settings:
config[
f"{service.id}_{setting.id}"
+ (
f"_{service_setting.suffix}"
if service_setting.suffix > 0
else ""
)
elif setting.context != "multisite":
break
else:
config[setting.id + (f"_{suffix}" if suffix > 0 else "")] = (
global_value.value
] = (
service_setting.value
if methods is False
else {
"value": global_value.value,
"method": global_value.method,
"value": service_setting.value,
"method": service_setting.method,
}
)
if setting.context == "multisite":
changed = False
for service in db_services:
if suffix == 0:
config[f"{service.id}_{setting.id}"] = (
config[setting.id]
if methods is False
else {
"value": config[setting.id]["value"],
"method": "default",
}
)
changed = True
elif f"{setting.id}_{suffix}" in config:
config[f"{service.id}_{setting.id}_{suffix}"] = (
config[f"{setting.id}_{suffix}"]
if methods is False
else {
"value": config[f"{setting.id}_{suffix}"][
"value"
],
"method": "default",
}
)
changed = True
service_setting = (
session.query(Services_settings)
.with_entities(
Services_settings.value, Services_settings.method
)
.filter_by(
service_id=service.id,
setting_id=setting.id,
suffix=suffix,
)
.first()
)
if service_setting is not None:
config[
f"{service.id}_{setting.id}"
+ (f"_{suffix}" if suffix > 0 else "")
] = (
service_setting.value
if methods is False
else {
"value": service_setting.value,
"method": service_setting.method,
}
)
changed = True
if global_value is None and changed is False:
break
if not setting.multiple:
break
suffix += 1
return config
def get_custom_configs(self) -> List[Dict[str, Any]]:

View file

@ -4,8 +4,8 @@ from argparse import ArgumentParser
from glob import glob
from itertools import chain
from json import loads
from os import R_OK, W_OK, X_OK, access, environ, getenv, path
from os.path import exists
from os import R_OK, X_OK, access, environ, getenv, listdir, path, walk
from os.path import exists, join
from re import compile as re_compile
from sys import exit as sys_exit, path as sys_path
from traceback import format_exc
@ -108,6 +108,12 @@ if __name__ == "__main__":
action="store_true",
help="Only initialize the database",
)
parser.add_argument(
"--method",
default="scheduler",
type=str,
help="The method that is used to save the config",
)
args = parser.parse_args()
logger.info("Save config started ...")
@ -127,9 +133,12 @@ if __name__ == "__main__":
with open("/usr/share/bunkerweb/INTEGRATION", "r") as f:
integration = f.read().strip()
logger.info(f"Detected {integration} integration")
if args.init:
logger.info(f"Detected {integration} integration")
config_files = None
db = None
apis = []
# Check existences and permissions
logger.info("Checking arguments ...")
@ -194,10 +203,31 @@ if __name__ == "__main__":
for k, v in environ.items()
if custom_confs_rx.match(k)
]
root_dirs = listdir("/etc/bunkerweb/configs")
for (root, dirs, files) in walk("/etc/bunkerweb/configs", topdown=True):
if (
root != "configs"
and (dirs and not root.split("/")[-1] in root_dirs)
or files
):
path_exploded = root.split("/")
for file in files:
with open(join(root, file), "r") as f:
custom_confs.append(
{
"value": f.read(),
"exploded": (
f"{path_exploded.pop()}"
if path_exploded[-1] not in root_dirs
else "",
path_exploded[-1],
file.replace(".conf", ""),
),
}
)
elif integration == "Kubernetes":
corev1 = kube_client.CoreV1Api()
tmp_config = {}
apis = []
for pod in corev1.list_pod_for_all_namespaces(watch=False).items:
if (
@ -280,7 +310,6 @@ if __name__ == "__main__":
tmp_config = {}
custom_confs = []
apis = []
for instance in (
docker_client.containers.list(filters={"label": "bunkerweb.INSTANCE"})
@ -354,10 +383,10 @@ if __name__ == "__main__":
if args.init:
sys_exit(0)
err = db.save_config(config_files, "scheduler")
err = db.save_config(config_files, args.method)
if not err:
err1 = db.save_custom_configs(custom_confs, "scheduler")
err1 = db.save_custom_configs(custom_confs, args.method)
else:
err = None
err1 = None

View file

@ -68,6 +68,9 @@ getLogger("sqlalchemy.orm.strategies.LazyLoader").setLevel(
getLogger("sqlalchemy.pool.impl.QueuePool").setLevel(
default_level if default_level != INFO else WARNING
)
getLogger("sqlalchemy.pool.impl.NullPool").setLevel(
default_level if default_level != INFO else WARNING
)
getLogger("sqlalchemy.engine.Engine").setLevel(
default_level if default_level != INFO else WARNING
)

View file

@ -197,10 +197,14 @@ class JobScheduler(ApiCaller):
path = job["path"]
name = job["name"]
file = job["file"]
thread = Thread(
target=self.__job_wrapper, args=(path, plugin, name, file)
)
threads.append(thread)
if job["name"].startswith("bunkernet"):
self.__job_wrapper(path, plugin, name, file)
else:
thread = Thread(
target=self.__job_wrapper, args=(path, plugin, name, file)
)
threads.append(thread)
for thread in threads:
thread.start()

View file

@ -27,10 +27,6 @@ if ! grep -q "Docker" /usr/share/bunkerweb/INTEGRATION ; then
fi
fi
if [ -f /var/lib/bunkerweb/db.sqlite3 ] ; then
chown scheduler:scheduler /var/lib/bunkerweb/db.sqlite3
fi
# execute jobs
log "ENTRYPOINT" " " "Executing scheduler ..."
/usr/share/bunkerweb/scheduler/main.py

View file

@ -3,7 +3,6 @@
from argparse import ArgumentParser
from copy import deepcopy
from glob import glob
from json import load
from os import (
_exit,
chmod,
@ -17,10 +16,9 @@ from os import (
walk,
)
from os.path import dirname, exists, isdir, isfile, islink, join
from re import compile as re_compile
from shutil import chown, copy, rmtree
from signal import SIGINT, SIGTERM, signal
from subprocess import PIPE, run as subprocess_run, DEVNULL, STDOUT
from subprocess import run as subprocess_run, DEVNULL, STDOUT
from sys import path as sys_path
from time import sleep
from traceback import format_exc
@ -290,6 +288,9 @@ if __name__ == "__main__":
"Looks like BunkerWeb configuration is already generated, will not generate it again ..."
)
if exists("/var/lib/bunkerweb/db.sqlite3"):
chmod("/var/lib/bunkerweb/db.sqlite3", 0o760)
while True:
# Instantiate scheduler
scheduler = JobScheduler(

View file

@ -20,7 +20,6 @@ RUN apk add --no-cache --virtual .build-deps g++ gcc musl-dev jpeg-dev zlib-dev
# Copy files
# can't exclude specific files/dir from . so we are copying everything by hand
COPY src/common/api /usr/share/bunkerweb/api
COPY src/common/confs /usr/share/bunkerweb/confs
COPY src/common/db /usr/share/bunkerweb/db
COPY src/common/core /usr/share/bunkerweb/core
COPY src/common/gen /usr/share/bunkerweb/gen
@ -46,7 +45,7 @@ RUN apk add --no-cache bash file && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type f -exec chmod 0740 {} \; ; done && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type d -exec chmod 0750 {} \; ; done && \
chmod 770 /var/cache/bunkerweb /var/lib/bunkerweb /var/tmp/bunkerweb && \
chmod 750 /usr/share/bunkerweb/gen/main.py /usr/share/bunkerweb/deps/python/bin/* && \
chmod 750 /usr/share/bunkerweb/gen/*.py /usr/share/bunkerweb/deps/python/bin/* && \
chmod 660 /usr/share/bunkerweb/INTEGRATION
# Fix CVEs

View file

@ -1,5 +1,5 @@
from copy import deepcopy
from os import listdir
from os import listdir, remove
from time import sleep
from flask import flash
from os.path import exists, isfile
@ -147,13 +147,8 @@ class Config:
self.__dict_to_env(env_file, conf)
proc = run(
[
"/usr/share/bunkerweb/gen/main.py",
"--settings",
"/usr/share/bunkerweb/settings.json",
"--templates",
"/usr/share/bunkerweb/confs",
"--output",
"/etc/nginx",
"python3",
"/usr/share/bunkerweb/gen/save_config.py",
"--variables",
env_file,
"--method",
@ -166,11 +161,7 @@ class Config:
if proc.returncode != 0:
raise Exception(f"Error from generator (return code = {proc.returncode})")
err = self.__db.save_config(conf, "ui")
if err:
self.__logger.error(
f"Can't save config in database: {err}",
)
remove(env_file)
def get_plugins_settings(self) -> dict:
return self.__plugins_settings
@ -181,7 +172,7 @@ class Config:
def get_settings(self) -> dict:
return self.__settings
def get_config(self) -> dict:
def get_config(self, methods: bool = True) -> dict:
"""Get the nginx variables env file and returns it as a dict
Returns
@ -191,13 +182,13 @@ class Config:
"""
if exists("/usr/sbin/nginx"):
return {
k: {"value": v, "method": "ui"}
k: ({"value": v, "method": "ui"} if methods is True else v)
for k, v in self.__env_to_dict("/etc/nginx/variables.env").items()
}
return self.__db.get_config(methods=True)
return self.__db.get_config(methods=methods)
def get_services(self) -> list[dict]:
def get_services(self, methods: bool = True) -> list[dict]:
"""Get nginx's services
Returns
@ -208,15 +199,20 @@ class Config:
if exists("/usr/sbin/nginx"):
services = []
for filename in iglob("/etc/nginx/**/variables.env"):
service = filename.split("/")[3]
env = {
k: {"value": v, "method": "ui"}
k.replace(f"{service}_", ""): (
{"value": v, "method": "ui"} if methods is True else v
)
for k, v in self.__env_to_dict(filename).items()
if k.startswith(f"{service}_")
or k in self.__plugins_settings.keys()
}
services.append(env)
return services
return self.__db.get_services_settings(methods=True)
return self.__db.get_services_settings(methods=methods)
def check_variables(self, variables: dict, _global: bool = False) -> int:
"""Testify that the variables passed are valid
@ -265,7 +261,9 @@ class Config:
return error
def reload_config(self) -> None:
self.__gen_conf(self.get_config(), self.get_services())
self.__gen_conf(
self.get_config(methods=False), self.get_services(methods=False)
)
def new_service(self, variables: dict) -> Tuple[str, int]:
"""Creates a new service from the given variables
@ -285,7 +283,7 @@ class Config:
Exception
raise this if the service already exists
"""
services = self.get_services()
services = self.get_services(methods=False)
for service in services:
if service["SERVER_NAME"] == variables["SERVER_NAME"] or service[
"SERVER_NAME"
@ -293,7 +291,7 @@ class Config:
return f"Service {service['SERVER_NAME']} already exists.", 1
services.append(variables)
self.__gen_conf(self.get_config(), services)
self.__gen_conf(self.get_config(methods=False), services)
return f"Configuration for {variables['SERVER_NAME']} has been generated.", 0
def edit_service(self, old_server_name: str, variables: dict) -> Tuple[str, int]:
@ -332,7 +330,9 @@ class Config:
str
the confirmation message
"""
self.__gen_conf(self.get_config() | variables, self.get_services())
self.__gen_conf(
self.get_config(methods=False) | variables, self.get_services(methods=False)
)
return f"The global configuration has been edited."
def delete_service(self, service_name: str) -> Tuple[str, int]:
@ -353,8 +353,8 @@ class Config:
Exception
raises this if the service_name given isn't found
"""
full_env = self.get_config()
services = self.get_services()
full_env = self.get_config(methods=False)
services = self.get_services(methods=False)
new_services = []
found = False