dynamic reload of nginx by sending SIGHUP

This commit is contained in:
bunkerity 2020-12-09 17:00:09 +01:00
parent 15e74e4860
commit 92569679b6
No known key found for this signature in database
GPG Key ID: 654FFF51CEF7CC47
9 changed files with 144 additions and 58 deletions

View File

@ -1,11 +1,15 @@
FROM alpine
RUN apk add py3-pip && \
pip3 install docker
RUN apk add py3-pip apache2-utils bash && \
pip3 install docker && \
mkdir /opt/entrypoint && \
mkdir -p /opt/confs/site
COPY *.py /opt/
RUN chmod +x /opt/entrypoint.py
COPY confs/site/ /opt/confs/site
COPY entrypoint/* /opt/entrypoint/
COPY autoconf/* /opt/entrypoint/
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
VOLUME /etc/nginx
ENTRYPOINT ["/opt/entrypoint.py"]
ENTRYPOINT ["/opt/entrypoint/entrypoint.py"]

View File

@ -1,13 +1,13 @@
#!/usr/bin/python3
import utils
import subprocess, shutil, os
import subprocess, shutil, os, traceback
def generate(instances, vars) :
try :
# Get env vars from bunkerized-nginx instances
vars_instances = {}
for instance_id, instance in instances :
for instance_id, instance in instances.items() :
for var_value in instance.attrs["Config"]["Env"] :
var = var_value.split("=")[0]
value = var_value.replace(var + "=", "", 1)
@ -16,10 +16,11 @@ def generate(instances, vars) :
vars_defaults.update(vars_instances)
vars_defaults.update(vars)
# Call site-config.sh to generate the config
proc = subprocess.run(["/opt/site-config.sh", vars["SERVER_NAME"]], env=vars_defaults, capture_output=True)
proc = subprocess.run(["/opt/entrypoint/site-config.sh", vars["SERVER_NAME"]], env=vars_defaults, capture_output=True)
if proc.returncode == 0 :
return True
except Exception as e :
traceback.print_exc()
utils.log("[!] Error while generating config : " + str(e))
return False
@ -34,11 +35,11 @@ def activate(instances, vars) :
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n}")
# Send SIGHUP to all running instances
for instance_id, instance in instances :
for instance_id, instance in instances.items() :
if instance.status == "running" :
try :
instance.kill("SIGHUP")
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
except docker.errors.APIError as e :
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
return True
@ -57,11 +58,11 @@ def deactivate(instances, vars) :
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n", "")
# Send SIGHUP to all running instances
for instance_id, instance in instances :
for instance_id, instance in instances.items() :
if instance.status == "running" :
try :
instance.kill("SIGHUP")
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
except docker.errors.APIError as e :
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
return True

View File

@ -32,23 +32,26 @@ def process(container, event) :
else :
utils.log("[!] Can't generate config for " + vars["SERVER_NAME"])
elif event == "start" :
containers[container.id].reload()
if config.activate(instances, vars) :
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
else :
utils.log("[!] Can't activate config for " + vars["SERVER_NAME"])
if container.id in containers :
containers[container.id].reload()
if config.activate(instances, vars) :
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
else :
utils.log("[!] Can't activate config for " + vars["SERVER_NAME"])
elif event == "die" :
containers[container.id].reload()
if config.deactivate(instances, vars) :
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
else :
utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"])
if container.id in containers :
containers[container.id].reload()
if config.deactivate(instances, vars) :
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
else :
utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"])
elif event == "destroy" :
del containers[container.id]
if config.remove(vars) :
utils.log("[*] Removed config for " + vars["SERVER_NAME"])
else :
utils.log("[!] Can't remove config for " + vars["SERVER_NAME"])
if container.id in containers :
del containers[container.id]
if config.remove(vars) :
utils.log("[*] Removed config for " + vars["SERVER_NAME"])
else :
utils.log("[!] Can't remove config for " + vars["SERVER_NAME"])
# Connect to the endpoint
endpoint = "/var/run/docker.sock"

25
confs/site/fastcgi.conf Normal file
View File

@ -0,0 +1,25 @@
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;

View File

@ -30,6 +30,9 @@ trap "trap_exit" TERM INT
# trap SIGHUP
function trap_reload() {
echo "[*] Catched reload operation"
if [ "$MULTISITE" = "yes" ] ; then
/opt/entrypoint/multisite-config.sh
fi
if [ -f /tmp/nginx.pid ] ; then
echo "[*] Reloading nginx ..."
/usr/sbin/nginx -s reload
@ -53,6 +56,7 @@ if [ ! -f "/opt/installed" ] ; then
/opt/entrypoint/site-config.sh "$server"
echo "[*] Multi site - $server configuration done"
done
/opt/entrypoint/multisite-config.sh
else
/opt/entrypoint/site-config.sh
echo "[*] Single site - $SERVER_NAME configuration done"
@ -67,13 +71,6 @@ chown -R root:nginx /etc/nginx/
chmod -R 740 /etc/nginx/
find /etc/nginx -type d -exec chmod 750 {} \;
# fix let's encrypt rights
if [ "$AUTO_LETS_ENCRYPT" = "yes" ] ; then
chown -R root:nginx /etc/letsencrypt
chmod -R 740 /etc/letsencrypt
find /etc/letsencrypt -type d -exec chmod 750 {} \;
fi
# start rsyslogd
rsyslogd

View File

@ -0,0 +1,42 @@
#!/bin/sh
# load default values
. /opt/entrypoint/defaults.sh
# load some functions
. /opt/entrypoint/utils.sh
# fix nginx configs rights (and modules through the symlink)
chown -R root:nginx /etc/nginx/
chmod -R 740 /etc/nginx/
find /etc/nginx -type d -exec chmod 750 {} \;
if [ "$MULTISITE" = "yes" ] ; then
servers=$(find /etc/nginx -name "server.conf" | cut -d '/' -f 4)
for server in $servers ; do
SERVER_PREFIX="/etc/nginx/${server}/"
if grep "/etc/letsencrypt/live" ${SERVER_PREFIX}https.conf > /dev/null && [ ! -f /etc/letsencrypt/live/${server}/fullchain.pem ] ; then
/opt/scripts/certbot-new.sh "$1" "$(cat ${SERVER_PREFIX}email-lets-encrypt.txt)"
fi
if grep "modsecurity.conf" ${SERVER_PREFIX}server.conf > /dev/null ; then
modsec_custom=""
if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then
modsec_custom="include /modsec-confs/*.conf\n"
fi
if ls /modsec-confs/${1}/*.conf > /dev/null 2>&1 ; then
modsec_custom="${modsec_custom}include /modsec-confs/${server}/*.conf\n"
fi
replace_in_file "${SERVER_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_RULES%" "$modsec_custom"
if grep "owasp-crs.conf" ${SERVER_PREFIX}modsecurity-rules.conf > /dev/null ; then
modsec_crs_custom=""
if ls /modsec-crs-confs/*.conf > /dev/null 2>&1 ; then
modsec_crs_custom="include /modsec-crs-confs/*.conf\n"
fi
if ls /modsec-crs-confs/${1}/*.conf > /dev/null 2>&1 ; then
modsec_crs_custom="${modsec_custom}include /modsec-crs-confs/${server}/*.conf\n"
fi
fi
replace_in_file "${SERVER_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_CRS%" "$modsec_crs_custom"
fi
done
fi

View File

@ -137,9 +137,6 @@ if [ "$REMOTE_PHP" != "" ] ; then
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_PHP%" "include ${NGINX_PREFIX}php.conf;"
replace_in_file "${NGINX_PREFIX}server.conf" "%FASTCGI_PATH%" "include ${NGINX_PREFIX}fastcgi.conf;"
replace_in_file "${NGINX_PREFIX}php.conf" "%REMOTE_PHP%" "$REMOTE_PHP"
if [ "$MULTISITE" = "yes" ] ; then
cp /etc/nginx/fastcgi.conf ${NGINX_PREFIX}fastcgi.conf && chown root:nginx ${NGINX_PREFIX}fastcgi.conf
fi
replace_in_file "${NGINX_PREFIX}fastcgi.conf" "\$document_root" "${REMOTE_PHP_PATH}/"
else
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_PHP%" ""
@ -322,11 +319,8 @@ if [ "$AUTO_LETS_ENCRYPT" = "yes" ] || [ "$USE_CUSTOM_HTTPS" = "yes" ] || [ "$GE
FIRST_SERVER_NAME=$(echo "$SERVER_NAME" | cut -d " " -f 1)
else
FIRST_SERVER_NAME="$1"
if [ ! -f /etc/letsencrypt/live/${1}/fullchain.pem ] ; then
echo "[*] Performing Let's Encrypt challenge for $1 ..."
EMAIL_LETS_ENCRYPT="${EMAIL_LETS_ENCRYPT-contact@$1}"
/opt/scripts/certbot-new.sh "$1" "$EMAIL_LETS_ENCRYPT"
fi
EMAIL_LETS_ENCRYPT="${EMAIL_LETS_ENCRYPT-contact@$1}"
echo -n "$EMAIL_LETS_ENCRYPT" > ${NGINX_PREFIX}email-lets-encrypt.txt
fi
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_CERT%" "/etc/letsencrypt/live/${FIRST_SERVER_NAME}/fullchain.pem"
replace_in_file "${NGINX_PREFIX}https.conf" "%HTTPS_KEY%" "/etc/letsencrypt/live/${FIRST_SERVER_NAME}/privkey.pem"
@ -362,24 +356,22 @@ fi
if [ "$USE_MODSECURITY" = "yes" ] ; then
replace_in_file "${NGINX_PREFIX}modsecurity.conf" "%MODSEC_RULES_FILE%" "${NGINX_PREFIX}/modsecurity-rules.conf"
replace_in_file "${NGINX_PREFIX}server.conf" "%USE_MODSECURITY%" "include ${NGINX_PREFIX}modsecurity.conf;"
modsec_custom=""
if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then
modsec_custom="include /modsec-confs/*.conf\n"
if [ "$MULTISITE" != "yes" ] ; then
modsec_custom=""
if ls /modsec-confs/*.conf > /dev/null 2>&1 ; then
modsec_custom="include /modsec-confs/*.conf\n"
fi
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_RULES%" "$modsec_custom"
fi
if [ "$MULTISITE" = "yes" ] && ls /modsec-confs/${1}/*.conf > /dev/null 2>&1 ; then
modsec_custom="${modsec_custom}include /modsec-confs/${1}/*.conf\n"
fi
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_RULES%" "$modsec_custom"
if [ "$USE_MODSECURITY_CRS" = "yes" ] ; then
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CRS%" "include /etc/nginx/owasp-crs.conf"
modsec_crs_custom=""
if ls /modsec-crs-confs/*.conf > /dev/null 2>&1 ; then
modsec_crs_custom="include /modsec-crs-confs/*.conf\n"
if [ "$MULTISITE" != "yes" ] ; then
modsec_crs_custom=""
if ls /modsec-crs-confs/*.conf > /dev/null 2>&1 ; then
modsec_crs_custom="include /modsec-crs-confs/*.conf\n"
fi
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_CRS%" "$modsec_crs_custom"
fi
if [ "$MULTISITE" = "yes" ] && ls /modsec-crs-confs/${1}/*.conf > /dev/null 2>&1 ; then
modsec_crs_custom="${modsec_custom}include /modsec-crs-confs/${1}/*.conf\n"
fi
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CUSTOM_CRS%" "$modsec_crs_custom"
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CRS_RULES%" "include /etc/nginx/owasp-crs/*.conf"
else
replace_in_file "${NGINX_PREFIX}modsecurity-rules.conf" "%MODSECURITY_INCLUDE_CRS%" ""

View File

@ -9,9 +9,9 @@ services:
- 80:8080
- 443:8443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/etc/letsencrypt
- ./web-files:/www:ro
- autoconf:/etc/nginx
environment:
- SERVER_NAME= # must be left blank if you don't want to setup "static" conf
- MULTISITE=yes
@ -21,6 +21,15 @@ services:
- USE_CLIENT_CACHE=yes
- USE_GZIP=yes
- USE_BROTLI=yes
labels:
- "bunkerized-nginx.AUTOCONF"
myautoconf:
image: bunkerity/bunkerized-nginx:autoconf
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- autoconf:/etc/nginx
myapp1:
image: php:fpm
@ -51,3 +60,6 @@ services:
- "bunkerized-nginx.SERVER_NAME=app3.website.com" # replace with your domain
- "bunkerized-nginx.REMOTE_PHP=myapp3"
- "bunkerized-nginx.REMOTE_PHP_PATH=/app"
volumes:
autoconf:

View File

@ -9,8 +9,8 @@ services:
- 80:8080
- 443:8443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/etc/letsencrypt
- autoconf:/etc/nginx
environment:
- SERVER_NAME= # must be left blank if you don't want to setup "static" conf
- MULTISITE=yes
@ -22,6 +22,13 @@ services:
- USE_BROTLI=yes
- USE_REVERSE_PROXY=yes
myautoconf:
image: bunkerity/bunkerized-nginx:autoconf
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- autoconf:/etc/nginx
myapp1:
build: js-app
restart: always
@ -51,3 +58,6 @@ services:
- "bunkerized-nginx.SERVER_NAME=app3.website.com" # replace with your domain
- "bunkerized-nginx.REVERSE_PROXY_URL=/"
- "bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp3:3000"
volumes:
autoconf: