2022-10-19 15:37:13 +00:00
|
|
|
from os import getenv
|
2022-06-03 15:24:14 +00:00
|
|
|
from docker import DockerClient
|
2022-07-01 14:32:52 +00:00
|
|
|
from re import search
|
2022-10-19 15:37:13 +00:00
|
|
|
from traceback import format_exc
|
2022-06-03 15:24:14 +00:00
|
|
|
|
|
|
|
|
from Controller import Controller
|
2022-06-27 07:43:36 +00:00
|
|
|
from ConfigCaller import ConfigCaller
|
2022-10-19 15:37:13 +00:00
|
|
|
from logger import setup_logger
|
2022-06-03 15:24:14 +00:00
|
|
|
|
|
|
|
|
|
2022-10-19 15:37:13 +00:00
|
|
|
class DockerController(Controller, ConfigCaller):
|
|
|
|
|
def __init__(self, docker_host):
|
2022-06-03 15:24:14 +00:00
|
|
|
super().__init__("docker")
|
2022-06-27 08:10:42 +00:00
|
|
|
ConfigCaller.__init__(self)
|
2022-06-03 15:24:14 +00:00
|
|
|
self.__client = DockerClient(base_url=docker_host)
|
2022-10-19 15:37:13 +00:00
|
|
|
self.__logger = setup_logger("docker-controller", getenv("LOG_LEVEL", "INFO"))
|
|
|
|
|
|
|
|
|
|
def _get_controller_instances(self):
|
|
|
|
|
return self.__client.containers.list(filters={"label": "bunkerweb.INSTANCE"})
|
2022-06-03 15:24:14 +00:00
|
|
|
|
2022-10-19 15:37:13 +00:00
|
|
|
def _to_instances(self, controller_instance):
|
2022-06-03 15:24:14 +00:00
|
|
|
instance = {}
|
|
|
|
|
instance["name"] = controller_instance.name
|
|
|
|
|
instance["hostname"] = controller_instance.name
|
2022-10-19 15:37:13 +00:00
|
|
|
instance["health"] = (
|
|
|
|
|
controller_instance.status == "running"
|
|
|
|
|
and controller_instance.attrs["State"]["Health"]["Status"] == "healthy"
|
|
|
|
|
)
|
2022-06-03 15:24:14 +00:00
|
|
|
instance["env"] = {}
|
2022-10-19 15:37:13 +00:00
|
|
|
for env in controller_instance.attrs["Config"]["Env"]:
|
2022-06-03 15:24:14 +00:00
|
|
|
variable = env.split("=")[0]
|
2022-10-19 15:37:13 +00:00
|
|
|
value = env.replace(f"{variable}=", "", 1)
|
|
|
|
|
if self._is_setting(variable):
|
2022-06-27 06:50:14 +00:00
|
|
|
instance["env"][variable] = value
|
2022-06-03 15:24:14 +00:00
|
|
|
return [instance]
|
|
|
|
|
|
2022-10-19 15:37:13 +00:00
|
|
|
def _get_controller_services(self):
|
|
|
|
|
return self.__client.containers.list(filters={"label": "bunkerweb.SERVER_NAME"})
|
|
|
|
|
|
|
|
|
|
def _to_services(self, controller_service):
|
2022-06-03 15:24:14 +00:00
|
|
|
service = {}
|
2022-10-19 15:37:13 +00:00
|
|
|
for variable, value in controller_service.labels.items():
|
|
|
|
|
if not variable.startswith("bunkerweb."):
|
2022-06-03 15:24:14 +00:00
|
|
|
continue
|
2022-06-27 06:50:14 +00:00
|
|
|
real_variable = variable.replace("bunkerweb.", "", 1)
|
2022-10-19 15:37:13 +00:00
|
|
|
if not self._is_multisite_setting(real_variable):
|
2022-06-27 06:50:14 +00:00
|
|
|
continue
|
|
|
|
|
service[real_variable] = value
|
2022-06-03 15:24:14 +00:00
|
|
|
return [service]
|
|
|
|
|
|
2022-10-19 15:37:13 +00:00
|
|
|
def _get_static_services(self):
|
2022-06-27 07:43:36 +00:00
|
|
|
services = []
|
|
|
|
|
variables = {}
|
2022-10-19 15:37:13 +00:00
|
|
|
for instance in self.__client.containers.list(
|
|
|
|
|
filters={"label": "bunkerweb.INSTANCE"}
|
|
|
|
|
):
|
|
|
|
|
for env in instance.attrs["Config"]["Env"]:
|
2022-06-27 07:43:36 +00:00
|
|
|
variable = env.split("=")[0]
|
2022-10-19 15:37:13 +00:00
|
|
|
value = env.replace(f"{variable}=", "", 1)
|
2022-06-27 07:43:36 +00:00
|
|
|
variables[variable] = value
|
|
|
|
|
server_names = []
|
2022-10-19 15:37:13 +00:00
|
|
|
if "SERVER_NAME" in variables and variables["SERVER_NAME"] != "":
|
2022-06-27 07:43:36 +00:00
|
|
|
server_names = variables["SERVER_NAME"].split(" ")
|
2022-10-19 15:37:13 +00:00
|
|
|
for server_name in server_names:
|
2022-06-27 07:43:36 +00:00
|
|
|
service = {}
|
|
|
|
|
service["SERVER_NAME"] = server_name
|
2022-10-19 15:37:13 +00:00
|
|
|
for variable, value in variables.items():
|
2022-06-27 07:43:36 +00:00
|
|
|
prefix = variable.split("_")[0]
|
2022-10-19 15:37:13 +00:00
|
|
|
real_variable = variable.replace(f"{prefix}_", "", 1)
|
|
|
|
|
if prefix == server_name and self._is_multisite_setting(real_variable):
|
2022-06-27 07:43:36 +00:00
|
|
|
service[real_variable] = value
|
|
|
|
|
services.append(service)
|
|
|
|
|
return services
|
|
|
|
|
|
2022-10-19 15:37:13 +00:00
|
|
|
def get_configs(self):
|
2022-07-01 14:32:52 +00:00
|
|
|
configs = {}
|
2022-10-19 15:37:13 +00:00
|
|
|
for config_type in self._supported_config_types:
|
2022-07-01 14:32:52 +00:00
|
|
|
configs[config_type] = {}
|
|
|
|
|
# get site configs from labels
|
2022-10-19 15:37:13 +00:00
|
|
|
for container in self.__client.containers.list(
|
|
|
|
|
filters={"label": "bunkerweb.SERVER_NAME"}
|
|
|
|
|
):
|
2022-07-01 14:32:52 +00:00
|
|
|
# extract server_name
|
|
|
|
|
server_name = ""
|
2022-10-19 15:37:13 +00:00
|
|
|
for variable, value in container.labels.items():
|
|
|
|
|
if not variable.startswith("bunkerweb."):
|
2022-07-01 14:32:52 +00:00
|
|
|
continue
|
|
|
|
|
real_variable = variable.replace("bunkerweb.", "", 1)
|
2022-10-19 15:37:13 +00:00
|
|
|
if real_variable == "SERVER_NAME":
|
2022-07-01 14:32:52 +00:00
|
|
|
server_name = value.split(" ")[0]
|
|
|
|
|
break
|
|
|
|
|
# extract configs
|
2022-10-19 15:37:13 +00:00
|
|
|
if server_name == "":
|
2022-07-01 14:32:52 +00:00
|
|
|
continue
|
2022-10-19 15:37:13 +00:00
|
|
|
for variable, value in container.labels.items():
|
|
|
|
|
if not variable.startswith("bunkerweb."):
|
2022-07-01 14:32:52 +00:00
|
|
|
continue
|
|
|
|
|
real_variable = variable.replace("bunkerweb.", "", 1)
|
2022-10-19 15:37:13 +00:00
|
|
|
result = search(
|
|
|
|
|
r"^CUSTOM_CONF_(SERVER_HTTP|MODSEC|MODSEC_CRS)_(.+)$", real_variable
|
|
|
|
|
)
|
|
|
|
|
if result is None:
|
2022-07-01 14:32:52 +00:00
|
|
|
continue
|
2022-07-21 15:04:27 +00:00
|
|
|
cfg_type = result.group(1).lower().replace("_", "-")
|
|
|
|
|
cfg_name = result.group(2)
|
2022-10-19 15:37:13 +00:00
|
|
|
configs[cfg_type][f"{server_name}/{cfg_name}"] = value
|
2022-07-01 14:32:52 +00:00
|
|
|
return configs
|
2022-06-03 15:24:14 +00:00
|
|
|
|
2022-10-19 15:37:13 +00:00
|
|
|
def apply_config(self):
|
2022-07-21 15:04:27 +00:00
|
|
|
ret = self._config.apply(self._instances, self._services, configs=self._configs)
|
2022-06-27 10:08:35 +00:00
|
|
|
return ret
|
2022-06-03 15:24:14 +00:00
|
|
|
|
2022-10-19 15:37:13 +00:00
|
|
|
def process_events(self):
|
|
|
|
|
for event in self.__client.events(decode=True, filters={"type": "container"}):
|
2022-06-03 15:24:14 +00:00
|
|
|
self._instances = self.get_instances()
|
|
|
|
|
self._services = self.get_services()
|
2022-07-01 14:32:52 +00:00
|
|
|
self._configs = self.get_configs()
|
2022-10-19 15:37:13 +00:00
|
|
|
if not self._config.update_needed(
|
|
|
|
|
self._instances, self._services, configs=self._configs
|
|
|
|
|
):
|
2022-06-03 15:24:14 +00:00
|
|
|
continue
|
2022-10-19 15:37:13 +00:00
|
|
|
self.__logger.info(
|
|
|
|
|
"Catched docker event, deploying new configuration ...",
|
|
|
|
|
)
|
|
|
|
|
try:
|
2022-06-03 15:24:14 +00:00
|
|
|
ret = self.apply_config()
|
2022-10-19 15:37:13 +00:00
|
|
|
if not ret:
|
|
|
|
|
self.__logger.error(
|
|
|
|
|
"Error while deploying new configuration",
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
self.__logger.info(
|
|
|
|
|
"Successfully deployed new configuration 🚀",
|
|
|
|
|
)
|
2022-11-04 17:14:44 +00:00
|
|
|
|
|
|
|
|
if not self._config._db.is_autoconf_loaded():
|
|
|
|
|
ret = self._config._db.set_autoconf_load(True)
|
|
|
|
|
if ret:
|
|
|
|
|
self.__logger.error(
|
|
|
|
|
f"Can't set autoconf loaded metadata to true in database: {ret}",
|
|
|
|
|
)
|
2022-10-19 15:37:13 +00:00
|
|
|
except:
|
|
|
|
|
self.__logger.error(
|
|
|
|
|
f"Exception while deploying new configuration :\n{format_exc()}",
|
|
|
|
|
)
|