diff --git a/autoconf/Dockerfile b/autoconf/Dockerfile index ecb595c..479f4a3 100644 --- a/autoconf/Dockerfile +++ b/autoconf/Dockerfile @@ -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"] diff --git a/autoconf/config.py b/autoconf/config.py index 6314f89..68b73c3 100644 --- a/autoconf/config.py +++ b/autoconf/config.py @@ -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 diff --git a/autoconf/entrypoint.py b/autoconf/entrypoint.py index 2a601dc..d314966 100644 --- a/autoconf/entrypoint.py +++ b/autoconf/entrypoint.py @@ -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" diff --git a/confs/site/fastcgi.conf b/confs/site/fastcgi.conf new file mode 100644 index 0000000..acd0b38 --- /dev/null +++ b/confs/site/fastcgi.conf @@ -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; diff --git a/entrypoint/entrypoint.sh b/entrypoint/entrypoint.sh index 7e92fc0..61d63c0 100644 --- a/entrypoint/entrypoint.sh +++ b/entrypoint/entrypoint.sh @@ -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 diff --git a/entrypoint/multisite-config.sh b/entrypoint/multisite-config.sh new file mode 100644 index 0000000..6ac2c6a --- /dev/null +++ b/entrypoint/multisite-config.sh @@ -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 diff --git a/entrypoint/site-config.sh b/entrypoint/site-config.sh index d84e07e..4651952 100644 --- a/entrypoint/site-config.sh +++ b/entrypoint/site-config.sh @@ -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%" "" diff --git a/examples/autoconf-php/docker-compose.yml b/examples/autoconf-php/docker-compose.yml index f8a56ba..38c0014 100644 --- a/examples/autoconf-php/docker-compose.yml +++ b/examples/autoconf-php/docker-compose.yml @@ -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: diff --git a/examples/autoconf-reverse-proxy/docker-compose.yml b/examples/autoconf-reverse-proxy/docker-compose.yml index 0a8d731..daaa53b 100644 --- a/examples/autoconf-reverse-proxy/docker-compose.yml +++ b/examples/autoconf-reverse-proxy/docker-compose.yml @@ -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: