diff --git a/src/autoconf/Dockerfile b/src/autoconf/Dockerfile index cf871c8dc..72fecc709 100644 --- a/src/autoconf/Dockerfile +++ b/src/autoconf/Dockerfile @@ -43,12 +43,11 @@ RUN apk add --no-cache bash && \ for dir in $(echo "configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \ chown -R root:nginx /data && \ chmod -R 770 /data && \ - chown -R root:nginx /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb && \ + chown -R root:nginx /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \ find /usr/share/bunkerweb -type f -exec chmod 0740 {} \; && \ find /usr/share/bunkerweb -type d -exec chmod 0750 {} \; && \ chmod -R 770 /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb && \ chmod 750 /usr/share/bunkerweb/cli/main.py /usr/share/bunkerweb/helpers/*.sh /usr/bin/bwcli /usr/share/bunkerweb/autoconf/main.py /usr/share/bunkerweb/deps/python/bin/* && \ - chown root:nginx /usr/bin/bwcli && \ mkdir /var/log/letsencrypt /var/lib/letsencrypt && \ chown root:nginx /var/log/letsencrypt /var/lib/letsencrypt && \ chmod 770 /var/log/letsencrypt /var/lib/letsencrypt diff --git a/src/bw/Dockerfile b/src/bw/Dockerfile index ea9da4308..30f8d71f5 100644 --- a/src/bw/Dockerfile +++ b/src/bw/Dockerfile @@ -36,13 +36,12 @@ COPY src/common/api /usr/share/bunkerweb/api COPY src/common/cli /usr/share/bunkerweb/cli COPY src/common/confs /usr/share/bunkerweb/confs COPY src/common/core /usr/share/bunkerweb/core -COPY src/common/db /usr/share/bunkerweb/db COPY src/common/gen /usr/share/bunkerweb/gen COPY src/common/helpers /usr/share/bunkerweb/helpers COPY src/common/settings.json /usr/share/bunkerweb/settings.json COPY src/common/utils /usr/share/bunkerweb/utils COPY src/VERSION /usr/share/bunkerweb/VERSION -COPY misc/*.ascii /usr/share/bunkerweb/ +COPY misc/*.ascii /usr/share/bunkerweb/misc/ # Install runtime dependencies, pypi packages, move bwcli, create data folders and set permissions RUN apk add --no-cache pcre bash python3 && \ diff --git a/src/bw/entrypoint.sh b/src/bw/entrypoint.sh index 7010c27f3..1331a3482 100644 --- a/src/bw/entrypoint.sh +++ b/src/bw/entrypoint.sh @@ -2,7 +2,7 @@ . /usr/share/bunkerweb/helpers/utils.sh -ascii_array=($(ls /usr/share/bunkerweb/*.ascii)) +ascii_array=($(ls /usr/share/bunkerweb/misc/*.ascii)) cat ${ascii_array[$(($RANDOM % ${#ascii_array[@]}))]} log "ENTRYPOINT" "ℹ️" "Starting BunkerWeb v$(cat /usr/share/bunkerweb/VERSION) ..." diff --git a/src/common/cli/CLI.py b/src/common/cli/CLI.py index ecb3f2415..ccbd6bb61 100644 --- a/src/common/cli/CLI.py +++ b/src/common/cli/CLI.py @@ -1,3 +1,4 @@ +from os import getenv from dotenv import dotenv_values from pathlib import Path from redis import StrictRedis @@ -35,8 +36,22 @@ def format_remaining_time(seconds): class CLI(ApiCaller): def __init__(self): - self.__variables = dotenv_values("/etc/nginx/variables.env") - self.__logger = setup_logger("CLI", self.__variables.get("LOG_LEVEL", "INFO")) + self.__logger = setup_logger("CLI", getenv("LOG_LEVEL", "INFO")) + + if not Path("/usr/share/bunkerweb/db").is_dir(): + self.__variables = dotenv_values("/etc/nginx/variables.env") + else: + if "/usr/share/bunkerweb/db" not in sys_path: + sys_path.append("/usr/share/bunkerweb/db") + + from Database import Database + + db = Database( + self.__logger, + sqlalchemy_string=getenv("DATABASE_URI", None), + ) + self.__variables = db.get_config() + self.__integration = self.__detect_integration() self.__use_redis = self.__variables.get("USE_REDIS", "no") == "yes" self.__redis = None @@ -95,7 +110,11 @@ class CLI(ApiCaller): ) self.__use_redis = False - if self.__integration in ("docker", "linux"): + if not Path("/usr/share/bunkerweb/db").is_dir() or self.__integration not in ( + "kubernetes", + "swarm", + "autoconf", + ): # Docker & Linux case super().__init__( apis=[ @@ -154,14 +173,21 @@ class CLI(ApiCaller): return False, "error" def bans(self) -> Tuple[bool, str]: - bans = {} + servers = {} + + ret, resp = self._send_to_apis("GET", "/bans", response=True) + if not ret: + return False, "error" + + for k, v in resp.items(): + servers[k] = v.get("data", []) if self.__redis: - bans["redis"] = [] + servers["redis"] = [] for key in self.__redis.scan_iter("bans_ip_*"): ip = key.decode("utf-8").replace("bans_ip_", "") exp = self.__redis.ttl(key) - bans["redis"].append( + servers["redis"].append( { "ip": ip, "exp": exp, @@ -169,18 +195,15 @@ class CLI(ApiCaller): } ) - ret, resp = self._send_to_apis("GET", "/bans", response=True) # TODO: fix this - if not ret: - return False, "error" + cli_str = "" + for server, bans in servers.items(): + cli_str += f"List of bans for {server}:\n" + if not bans: + cli_str += "No ban found\n" - print(resp, flush=True) # TODO: handle response + for ban in bans: + cli_str += f"- {ban['ip']} for {format_remaining_time(ban['exp'])} : {ban.get('reason', 'no reason given')}\n" + else: + cli_str += "\n" - # bans.extend(resp.get("data", [])) - - # if len(bans) == 0: - # return True, "No ban found" - - # cli_str = "List of bans :\n" - # for ban in bans: - # cli_str += f"- {ban['ip']} for {format_remaining_time(ban['exp'])} : {ban.get('reason', 'no reason given')}\n" - # return True, cli_str + return True, cli_str diff --git a/src/common/utils/ApiCaller.py b/src/common/utils/ApiCaller.py index ad3e191c4..69d20ef04 100644 --- a/src/common/utils/ApiCaller.py +++ b/src/common/utils/ApiCaller.py @@ -2,7 +2,7 @@ from io import BytesIO from os import getenv from sys import path as sys_path from tarfile import open as taropen -from typing import Optional +from typing import Any, Dict, List, Literal, Optional, Tuple, Union if "/usr/share/bunkerweb/utils" not in sys_path: sys_path.append("/usr/share/bunkerweb/utils") @@ -18,8 +18,8 @@ from docker import DockerClient class ApiCaller: - def __init__(self, apis=[]): - self.__apis = apis + def __init__(self, apis: List[API] = None): + self.__apis = apis or [] self.__logger = setup_logger("Api", getenv("LOG_LEVEL", "INFO")) def auto_setup(self, bw_integration: Optional[str] = None): @@ -101,14 +101,22 @@ class ApiCaller: ) ) - def _set_apis(self, apis): + def _set_apis(self, apis: List[API]): self.__apis = apis def _get_apis(self): return self.__apis - def _send_to_apis(self, method, url, files=None, data=None, response=False): + def _send_to_apis( + self, + method: Union[Literal["POST"], Literal["GET"]], + url: str, + files: Optional[Dict[str, BytesIO]] = None, + data: Optional[Dict[str, Any]] = None, + response: bool = False, + ) -> Tuple[bool, Tuple[bool, Optional[Dict[str, Any]]]]: ret = True + responses = {} for api in self.__apis: if files is not None: for buffer in files.values(): @@ -130,13 +138,18 @@ class ApiCaller: f"Successfully sent API request to {api.get_endpoint()}{url}", ) + if response: + instance = api.get_endpoint().replace("http://", "").split(":")[0] + if isinstance(resp, dict): + responses[instance] = resp + else: + responses[instance] = resp.json() + if response: - if isinstance(resp, dict): - return ret, resp - return ret, resp.json() + return ret, responses return ret - def _send_files(self, path, url): + def _send_files(self, path: str, url: str) -> bool: ret = True with BytesIO() as tgz: with taropen( diff --git a/src/scheduler/Dockerfile b/src/scheduler/Dockerfile index 1b4802b68..007aa11aa 100644 --- a/src/scheduler/Dockerfile +++ b/src/scheduler/Dockerfile @@ -21,6 +21,7 @@ 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/cli /usr/share/bunkerweb/cli COPY src/common/confs /usr/share/bunkerweb/confs COPY src/common/db /usr/share/bunkerweb/db COPY src/common/core /usr/share/bunkerweb/core @@ -31,11 +32,12 @@ COPY src/common/utils /usr/share/bunkerweb/utils COPY src/scheduler /usr/share/bunkerweb/scheduler COPY src/VERSION /usr/share/bunkerweb/VERSION -# Add scheduler user, install runtime dependencies, create data folders and set permissions +# Add scheduler user, drop bwcli, install runtime dependencies, create data folders and set permissions RUN apk add --no-cache bash libgcc libstdc++ openssl && \ ln -s /usr/local/bin/python3 /usr/bin/python3 && \ addgroup -g 101 scheduler && \ adduser -h /var/cache/nginx -g scheduler -s /bin/sh -G scheduler -D -H -u 101 scheduler && \ + cp /usr/share/bunkerweb/helpers/bwcli /usr/bin/ && \ echo "Docker" > /usr/share/bunkerweb/INTEGRATION && \ mkdir -p /var/tmp/bunkerweb && \ mkdir -p /var/www && \ @@ -48,12 +50,12 @@ RUN apk add --no-cache bash libgcc libstdc++ openssl && \ for dir in $(echo "configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \ chown -R root:scheduler /data && \ chmod -R 770 /data && \ - chown -R root:scheduler /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb && \ + chown -R root:scheduler /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /usr/bin/bwcli && \ find /usr/share/bunkerweb -type f -exec chmod 0740 {} \; && \ find /usr/share/bunkerweb -type d -exec chmod 0750 {} \; && \ chmod -R 770 /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb && \ find /usr/share/bunkerweb/core/*/jobs/* -type f -exec chmod 750 {} \; && \ - chmod 750 /usr/share/bunkerweb/gen/*.py /usr/share/bunkerweb/scheduler/main.py /usr/share/bunkerweb/scheduler/entrypoint.sh /usr/share/bunkerweb/helpers/*.sh /usr/share/bunkerweb/deps/python/bin/* && \ + chmod 750 /usr/share/bunkerweb/cli/main.py /usr/share/bunkerweb/gen/*.py /usr/share/bunkerweb/scheduler/main.py /usr/share/bunkerweb/scheduler/entrypoint.sh /usr/share/bunkerweb/helpers/*.sh /usr/share/bunkerweb/deps/python/bin/* /usr/bin/bwcli && \ mkdir -p /etc/nginx && \ chown -R scheduler:scheduler /etc/nginx && \ chmod -R 770 /etc/nginx && \