From 6a714e2ece7cc1e7938bf03b416263075875a67f Mon Sep 17 00:00:00 2001 From: bunkerity Date: Sun, 14 Mar 2021 16:50:08 +0100 Subject: [PATCH] road to swarm - fix race condition on initial configuration --- autoconf/AutoConf.py | 14 +++++-- autoconf/Config.py | 47 ++++++++++++++++++++---- entrypoint/entrypoint.sh | 2 + entrypoint/global-config.sh | 42 --------------------- entrypoint/jobs.sh | 73 +++++++++++++++++++++++++++++++++++++ lua/api.lua | 4 ++ prepare.sh | 1 + 7 files changed, 130 insertions(+), 53 deletions(-) create mode 100644 entrypoint/jobs.sh diff --git a/autoconf/AutoConf.py b/autoconf/AutoConf.py index eb263c4..9d06434 100644 --- a/autoconf/AutoConf.py +++ b/autoconf/AutoConf.py @@ -1,5 +1,6 @@ from Config import Config import utils +import os class AutoConf : @@ -54,11 +55,11 @@ class AutoConf : def __process_instance(self, instance, event, id, name, labels) : if event == "create" : self.__instances[id] = instance - if self.__swarm : - if self.__config.globalconf(self.__instances) : - utils.log("[*] global config generated") + if self.__swarm and len(self.__instances) == 0 : + if self.__config.initconf(self.__instances) : + utils.log("[*] initial config succeeded") else : - utils.log("[!] can't generate global config") + utils.log("[!] initial config failed") utils.log("[*] bunkerized-nginx instance created : " + name + " / " + id) elif event == "start" : self.__instances[id].reload() @@ -68,6 +69,11 @@ class AutoConf : utils.log("[*] bunkerized-nginx instance stopped : " + name + " / " + id) elif event == "destroy" or event == "remove" : del self.__instances[id] + if self.__swarm and len(self.__instances) == 0 : + with open("/etc/crontabs/nginx", "w") as f : + f.write("") + if os.path.exists("/etc/nginx/autoconf") : + os.remove("/etc/nginx/autoconf") utils.log("[*] bunkerized-nginx instance removed : " + name + " / " + id) def __process_server(self, instance, event, id, name, labels) : diff --git a/autoconf/Config.py b/autoconf/Config.py index bd95235..a303bd1 100644 --- a/autoconf/Config.py +++ b/autoconf/Config.py @@ -9,6 +9,33 @@ class Config : self.__swarm = swarm self.__api = api + + def initconf(self, instances) : + try : + for instance_id, instance in instances.items() : + env = instance.attrs["Spec"]["TaskTemplate"]["ContainerSpec"]["Env"] + break + vars = {} + for var_value in env : + var = var_value.split("=")[0] + value = var_value.replace(var + "=", "", 1) + vars[var] = value + if self.globalconf(instances) : + i = 0 + started = False + while i < 5 : + if self.__status(instances) : + started = True + i = i + 1 + time.sleep(i) + if started : + proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/jobs.sh", "nginx"], env=vars, capture_output=True) + return proc.returncode == 0 + except Exception as e : + traceback.print_exc() + utils.log("[!] Error while initializing config : " + str(e)) + return False + def globalconf(self, instances) : try : for instance_id, instance in instances.items() : @@ -19,7 +46,7 @@ class Config : var = var_value.split("=")[0] value = var_value.replace(var + "=", "", 1) vars[var] = value - proc = subprocess.run(["/opt/entrypoint/global-config.sh"], env=vars, capture_output=True) + proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/global-config.sh", "nginx"], env=vars, capture_output=True) if proc.returncode == 0 : with open("/etc/nginx/autoconf", "w") as f : f.write("ok") @@ -46,9 +73,9 @@ class Config : vars_defaults.update(vars_instances) vars_defaults.update(vars) # Call site-config.sh to generate the config - proc = subprocess.run(["/opt/entrypoint/site-config.sh", vars["SERVER_NAME"]], env=vars_defaults, capture_output=True) + proc = subprocess.run(["/bin/su", "-s", "/bin/sh", "-c", "/opt/entrypoint/site-config.sh" + " " + vars["SERVER_NAME"], "nginx"], env=vars_defaults, capture_output=True) if proc.returncode == 0 : - proc = subprocess.run(["/opt/entrypoint/multisite-config.sh"], capture_output=True) + proc = subprocess.run(["/bin/su", "-s", "/opt/entrypoint/multisite-config.sh", "nginx"], capture_output=True) return proc.returncode == 0 except Exception as e : traceback.print_exc() @@ -65,7 +92,7 @@ class Config : # Include the server conf utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n}") - return self.reload(instances) + return self.__reload(instances) except Exception as e : utils.log("[!] Error while activating config : " + str(e)) return False @@ -80,7 +107,7 @@ class Config : # Remove the include utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n", "") - return self.reload(instances) + return self.__reload(instances) except Exception as e : utils.log("[!] Error while deactivating config : " + str(e)) @@ -100,7 +127,13 @@ class Config : utils.log("[!] Error while deactivating config : " + str(e)) return False - def reload(self, instances) : + def __reload(self, instances) : + return self.__api(instances, "/reload") + + def __status(self, instances) : + return self.__api(instances, "/status") + + def __api(self, instances, path) : ret = True for instance_id, instance in instances.items() : # Reload the instance object just in case @@ -113,7 +146,7 @@ class Config : nodeID = task["NodeID"] taskID = task["ID"] fqdn = name + "." + nodeID + "." + taskID - req = requests.post("http://" + fqdn + ":8080" + self.__api + "/reload") + req = requests.post("http://" + fqdn + ":8080" + self.__api + path) if req and req.status_code == 200 : utils.log("[*] Sent reload order to instance " + fqdn + " (service.node.task)") else : diff --git a/entrypoint/entrypoint.sh b/entrypoint/entrypoint.sh index 147b4e1..087bf3c 100644 --- a/entrypoint/entrypoint.sh +++ b/entrypoint/entrypoint.sh @@ -72,6 +72,8 @@ if [ ! -f "/opt/installed" ] ; then if [ "$SWARM_MODE" = "no" ] ; then # global config /opt/entrypoint/global-config.sh + # background jobs + /opt/entrypoint/jobs.sh # multisite configs if [ "$MULTISITE" = "yes" ] ; then for server in $SERVER_NAME ; do diff --git a/entrypoint/global-config.sh b/entrypoint/global-config.sh index 728cf50..fae987e 100644 --- a/entrypoint/global-config.sh +++ b/entrypoint/global-config.sh @@ -103,13 +103,6 @@ if [ "$BLACKLIST_COUNTRY" != "" ] || [ "$WHITELIST_COUNTRY" != "" ] ; then replace_in_file "/etc/nginx/geoip.conf" "%COUNTRY%" "$(echo $BLACKLIST_COUNTRY | sed 's/ / no;\\n/g') no;" fi echo "$GEOIP_CRON /opt/scripts/geoip.sh" >> /etc/crontabs/nginx - if [ -f "/cache/geoip.mmdb" ] ; then - echo "[*] Copying cached geoip.mmdb ..." - cp /cache/geoip.mmdb /etc/nginx/geoip.mmdb - else - echo "[*] Downloading GeoIP database (in background) ..." - /opt/scripts/geoip.sh & - fi else replace_in_file "/etc/nginx/nginx.conf" "%USE_COUNTRY%" "" fi @@ -118,13 +111,6 @@ fi if [ "$(has_value BLOCK_USER_AGENT yes)" != "" ] ; then replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_USER_AGENT%" "include /etc/nginx/map-user-agent.conf;" echo "$BLOCK_USER_AGENT_CRON /opt/scripts/user-agents.sh" >> /etc/crontabs/nginx - if [ -f "/cache/map-user-agent.conf" ] ; then - echo "[*] Copying cached map-user-agent.conf ..." - cp /cache/map-user-agent.conf /etc/nginx/map-user-agent.conf - else - echo "[*] Downloading bad user-agent list (in background) ..." - /opt/scripts/user-agents.sh & - fi else replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_USER_AGENT%" "" fi @@ -133,13 +119,6 @@ fi if [ "$(has_value BLOCK_REFERRER yes)" != "" ] ; then replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_REFERRER%" "include /etc/nginx/map-referrer.conf;" echo "$BLOCK_REFERRER_CRON /opt/scripts/referrers.sh" >> /etc/crontabs/nginx - if [ -f "/cache/map-referrer.conf" ] ; then - echo "[*] Copying cached map-referrer.conf ..." - cp /cache/map-referrer.conf /etc/nginx/map-referrer.conf - else - echo "[*] Downloading bad referrer list (in background) ..." - /opt/scripts/referrers.sh & - fi else replace_in_file "/etc/nginx/nginx.conf" "%BLOCK_REFERRER%" "" fi @@ -147,37 +126,16 @@ fi # block TOR exit nodes if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then echo "$BLOCK_TOR_EXIT_NODE_CRON /opt/scripts/exit-nodes.sh" >> /etc/crontabs/nginx - if [ -f "/cache/block-tor-exit-node.conf" ] ; then - echo "[*] Copying cached block-tor-exit-node.conf ..." - cp /cache/block-tor-exit-node.conf /etc/nginx/block-tor-exit-node.conf - else - echo "[*] Downloading tor exit nodes list (in background) ..." - /opt/scripts/exit-nodes.sh & - fi fi # block proxies if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then echo "$BLOCK_PROXIES_CRON /opt/scripts/proxies.sh" >> /etc/crontabs/nginx - if [ -f "/cache/block-proxies.conf" ] ; then - echo "[*] Copying cached block-proxies.conf ..." - cp /cache/block-proxies.conf /etc/nginx/block-proxies.conf - else - echo "[*] Downloading proxies list (in background) ..." - /opt/scripts/proxies.sh & - fi fi # block abusers if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then echo "$BLOCK_ABUSERS_CRON /opt/scripts/abusers.sh" >> /etc/crontabs/nginx - if [ -f "/cache/block-abusers.conf" ] ; then - echo "[*] Copying cached block-abusers.conf ..." - cp /cache/block-abusers.conf /etc/nginx/block-abusers.conf - else - echo "[*] Downloading abusers list (in background) ..." - /opt/scripts/abusers.sh & - fi fi # DNS resolvers diff --git a/entrypoint/jobs.sh b/entrypoint/jobs.sh new file mode 100644 index 0000000..b3a9512 --- /dev/null +++ b/entrypoint/jobs.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# load default values +. ./opt/entrypoint/defaults.sh + +# load some functions +. /opt/entrypoint/utils.sh + +# GeoIP +if [ "$BLACKLIST_COUNTRY" != "" ] || [ "$WHITELIST_COUNTRY" != "" ] ; then + if [ -f "/cache/geoip.mmdb" ] ; then + echo "[*] Copying cached geoip.mmdb ..." + cp /cache/geoip.mmdb /etc/nginx/geoip.mmdb + else + echo "[*] Downloading GeoIP database (in background) ..." + /opt/scripts/geoip.sh & + fi +fi + +# User-Agents +if [ "$(has_value BLOCK_USER_AGENT yes)" != "" ] ; then + if [ -f "/cache/map-user-agent.conf" ] ; then + echo "[*] Copying cached map-user-agent.conf ..." + cp /cache/map-user-agent.conf /etc/nginx/map-user-agent.conf + else + echo "[*] Downloading bad user-agent list (in background) ..." + /opt/scripts/user-agents.sh & + fi +fi + +# Referrers +if [ "$(has_value BLOCK_REFERRER yes)" != "" ] ; then + if [ -f "/cache/map-referrer.conf" ] ; then + echo "[*] Copying cached map-referrer.conf ..." + cp /cache/map-referrer.conf /etc/nginx/map-referrer.conf + else + echo "[*] Downloading bad referrer list (in background) ..." + /opt/scripts/referrers.sh & + fi +fi + +# exit nodes +if [ "$(has_value BLOCK_TOR_EXIT_NODE yes)" != "" ] ; then + if [ -f "/cache/block-tor-exit-node.conf" ] ; then + echo "[*] Copying cached block-tor-exit-node.conf ..." + cp /cache/block-tor-exit-node.conf /etc/nginx/block-tor-exit-node.conf + else + echo "[*] Downloading tor exit nodes list (in background) ..." + /opt/scripts/exit-nodes.sh & + fi +fi + +# proxies +if [ "$(has_value BLOCK_PROXIES yes)" != "" ] ; then + if [ -f "/cache/block-proxies.conf" ] ; then + echo "[*] Copying cached block-proxies.conf ..." + cp /cache/block-proxies.conf /etc/nginx/block-proxies.conf + else + echo "[*] Downloading proxies list (in background) ..." + /opt/scripts/proxies.sh & + fi +fi + +# abusers +if [ "$(has_value BLOCK_ABUSERS yes)" != "" ] ; then + if [ -f "/cache/block-abusers.conf" ] ; then + echo "[*] Copying cached block-abusers.conf ..." + cp /cache/block-abusers.conf /etc/nginx/block-abusers.conf + else + echo "[*] Downloading abusers list (in background) ..." + /opt/scripts/abusers.sh & + fi +fi diff --git a/lua/api.lua b/lua/api.lua index a5d256f..8e4e24b 100644 --- a/lua/api.lua +++ b/lua/api.lua @@ -1,6 +1,10 @@ local M = {} local api_list = {} +api_list["^/ping$"] = function () + return true +end + api_list["^/reload$"] = function () return os.execute("/usr/sbin/nginx -s reload") == 0 end diff --git a/prepare.sh b/prepare.sh index 883b4d2..b8a6911 100644 --- a/prepare.sh +++ b/prepare.sh @@ -7,6 +7,7 @@ apk --no-cache add certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clama mkdir /opt/entrypoint.d # prepare /www +mkdir /www chown -R root:nginx /www chmod -R 770 /www