From 0d9da2b72a74cd5af93bae668f89ebf81c09875f Mon Sep 17 00:00:00 2001 From: Zoey Date: Thu, 6 Apr 2023 18:24:20 +0200 Subject: [PATCH] merge upstream --- Dockerfile | 3 +- README.md | 8 ++-- backend/db.js | 5 ++- backend/internal/certificate.js | 5 +-- backend/lib/config.js | 9 ++-- docker/rootfs/bin/common.sh | 42 ------------------- .../etc/s6-overlay/s6-rc.d/prepare/00-all.sh | 18 -------- .../s6-overlay/s6-rc.d/prepare/10-npmuser.sh | 20 --------- .../s6-overlay/s6-rc.d/prepare/20-paths.sh | 41 ------------------ .../s6-rc.d/prepare/30-ownership.sh | 24 ----------- .../s6-overlay/s6-rc.d/prepare/40-dynamic.sh | 17 -------- .../etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh | 36 ---------------- .../s6-overlay/s6-rc.d/prepare/60-secrets.sh | 30 ------------- .../s6-overlay/s6-rc.d/prepare/90-banner.sh | 17 -------- rootfs/bin/start.sh | 28 ++++++++++++- 15 files changed, 45 insertions(+), 258 deletions(-) delete mode 100644 docker/rootfs/bin/common.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh delete mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh diff --git a/Dockerfile b/Dockerfile index d61e89bd..f7619805 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,8 @@ RUN ln -s /app/password-reset.js /usr/local/bin/password-reset.js && \ ln -s /app/index.js /usr/local/bin/index.js ENV NODE_ENV=production \ - DB_SQLITE_FILE=/data/database.sqlite + NODE_CONFIG_DIR=/data/etc/npm \ + DB_SQLITE_FILE=/data/etc/npm/database.sqlite WORKDIR /app ENTRYPOINT ["start.sh"] diff --git a/README.md b/README.md index 29e4adfd..13cf8f8d 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ so that the barrier for entry here is low. - Only use TLSv1.2 and TLSv1.3 - Uses OCSP Stapling - Needs manual migration if you use custom certificates, just upload the CA/Intermediate Certificate (file name: `chain.pem`) in the `/opt/npm/tls/custom/npm-[certificate-id]` folder +- fixed dnspod plugin + - Needs manual migration, please delete all dnspod certs and recreate them OR you manually change the credentialsfile (see [here](https://github.com/ZoeyVid/nginx-proxy-manager/blob/develop/global/certbot-dns-plugins.js) for the template) - Smaller then the original - Runs the admin interface on port 81 with https - Default page runs also with https @@ -64,10 +66,10 @@ so that the barrier for entry here is low. - Auto database vacuum (only sqlite) (FULLCLEAN=true) - Auto certbot old certs clean (FULLCLEAN=true) - Passwort reset (only sqlite) (`docker exec -it nginx-proxy-manager password-reset.js USER_EMAIL PASSWORD`) +- TLS supported for MariaDB/MySQL, please set the `DB_MYSQL_TLS` env to true. If you use self signed certificates you can upload them for example to `/data/etc/npm/ca.crt` and set the `DB_MYSQL_CA` to `/data/etc/npm/ca.crt` (not tested) ## Soon -- disabling IPv4/IPv6 -- MariaDB/MySQL TLS support (if requested) +- disabling IPv4/IPv6 ([1](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh) / [2](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh) / nginx templates (nginx.js lines 200-300)) - support changing the PUID/PGID (maybe) - more @@ -75,7 +77,7 @@ so that the barrier for entry here is low. - **NOTE: migrating back to the original is not possible**, so make first a **backup** before migration, so you can use the backup to switch back - if you use custom certificates, you need to upload the CA/Intermediate Certificate (file name: `chain.pem`) in the `/opt/npm/tls/custom/npm-[certificate-id]` folder - some buttons have changed, check if they are still correct -- please delete all dnspod certs and recreate them +- please delete all dnspod certs and recreate them OR you manually change the credentialsfile (see [here](https://github.com/ZoeyVid/nginx-proxy-manager/blob/develop/global/certbot-dns-plugins.js) for the template) - changing the PUID/PGID is not supported (since it would break running in network_mode host) # Use as webserver diff --git a/backend/db.js b/backend/db.js index 1a8b1634..5d5886b0 100644 --- a/backend/db.js +++ b/backend/db.js @@ -1,7 +1,7 @@ const config = require('./lib/config'); if (!config.has('database')) { - throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/'); + throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup'); } function generateDbConfig() { @@ -16,7 +16,8 @@ function generateDbConfig() { user: cfg.user, password: cfg.password, database: cfg.name, - port: cfg.port + port: cfg.port, + ssl: cfg.tls, }, migrations: { tableName: 'migrations' diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 09949fbc..c13c9aa7 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -4,7 +4,6 @@ const https = require('https'); const tempWrite = require('temp-write'); const moment = require('moment'); const logger = require('../logger').ssl; -const config = require('../lib/config'); const error = require('../lib/error'); const utils = require('../lib/utils'); const certificateModel = require('../models/certificate'); @@ -16,8 +15,8 @@ const archiver = require('archiver'); const path = require('path'); const { isArray } = require('lodash'); -const letsencryptConfig = '/data/tls/certbot/config.ini'; -const certbotCommand = 'certbot --config-dir /data/tls/certbot'; +const certbotConfig = '/data/tls/certbot/config.ini'; +const certbotCommand = 'certbot --config-dir /data/tls/certbot'; function omissions() { return ['is_deleted']; diff --git a/backend/lib/config.js b/backend/lib/config.js index caa57fcf..e8b4a16b 100644 --- a/backend/lib/config.js +++ b/backend/lib/config.js @@ -2,14 +2,14 @@ const fs = require('fs'); const NodeRSA = require('node-rsa'); const logger = require('../logger').global; -const keysFile = '/data/keys.json'; +const keysFile = '/data/etc/npm/keys.json'; let instance = null; // 1. Load from config file first (not recommended anymore) // 2. Use config env variables next const configure = () => { - const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json'; + const filename = (process.env.NODE_CONFIG_DIR || '/data/etc/npm') + '/' + (process.env.NODE_ENV || 'default') + '.json'; if (fs.existsSync(filename)) { let configData; try { @@ -29,6 +29,8 @@ const configure = () => { const envMysqlHost = process.env.DB_MYSQL_HOST || null; const envMysqlUser = process.env.DB_MYSQL_USER || null; const envMysqlName = process.env.DB_MYSQL_NAME || null; + const envMysqlTls = process.env.DB_MYSQL_TLS || null; + const envMysqlCa = process.env.DB_MYSQL_CA || '/etc/ssl/certs/ca-certificates.crt'; if (envMysqlHost && envMysqlUser && envMysqlName) { // we have enough mysql creds to go with mysql logger.info('Using MySQL configuration'); @@ -40,13 +42,14 @@ const configure = () => { user: envMysqlUser, password: process.env.DB_MYSQL_PASSWORD, name: envMysqlName, + ssl: envMysqlTls ? { ca: fs.readFileSync(envMysqlCa) } : false, }, keys: getKeys(), }; return; } - const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite'; + const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/etc/npm/database.sqlite'; logger.info(`Using Sqlite: ${envSqliteFile}`); instance = { database: { diff --git a/docker/rootfs/bin/common.sh b/docker/rootfs/bin/common.sh deleted file mode 100644 index 0bc6468d..00000000 --- a/docker/rootfs/bin/common.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -set -e - -CYAN='\E[1;36m' -BLUE='\E[1;34m' -YELLOW='\E[1;33m' -RED='\E[1;31m' -RESET='\E[0m' -export CYAN BLUE YELLOW RED RESET - -PUID=${PUID:-0} -PGID=${PGID:-0} - -if [[ "$PUID" -ne '0' ]] && [ "$PGID" = '0' ]; then - # set group id to same as user id, - # the user probably forgot to specify the group id and - # it would be rediculous to intentionally use the root group - # for a non-root user - PGID=$PUID -fi - -export PUID PGID - -log_info () { - echo -e "${BLUE}❯ ${CYAN}$1${RESET}" -} - -log_error () { - echo -e "${RED}❯ $1${RESET}" -} - -# The `run` file will only execute 1 line so this helps keep things -# logically separated - -log_fatal () { - echo -e "${RED}--------------------------------------${RESET}" - echo -e "${RED}ERROR: $1${RESET}" - echo -e "${RED}--------------------------------------${RESET}" - /run/s6/basedir/bin/halt - exit 1 -} diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh deleted file mode 100755 index 1d5899e4..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -. /bin/common.sh - -if [ "$(id -u)" != "0" ]; then - log_fatal "This docker container must be run as root, do not specify a user.\nYou can specify PUID and PGID env vars to run processes as that user and group after initialization." -fi - -. /etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh -. /etc/s6-overlay/s6-rc.d/prepare/20-paths.sh -. /etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh -. /etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh -. /etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh -. /etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh -. /etc/s6-overlay/s6-rc.d/prepare/90-banner.sh diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh deleted file mode 100755 index c5cf5435..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -log_info 'Configuring npmuser ...' - -if id -u npmuser; then - # user already exists - usermod -u "$PUID" npmuser || exit 1 -else - # Add npmuser user - useradd -o -u "$PUID" -U -d /tmp/npmuserhome -s /bin/false npmuser || exit 1 -fi - -usermod -G "$PGID" npmuser || exit 1 -groupmod -o -g "$PGID" npmuser || exit 1 -# Home for npmuser -mkdir -p /tmp/npmuserhome -chown -R "$PUID:$PGID" /tmp/npmuserhome diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh deleted file mode 100755 index 2f59ef41..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -log_info 'Checking paths ...' - -# Ensure /data is mounted -if [ ! -d '/data' ]; then - log_fatal '/data is not mounted! Check your docker configuration.' -fi -# Ensure /etc/letsencrypt is mounted -if [ ! -d '/etc/letsencrypt' ]; then - log_fatal '/etc/letsencrypt is not mounted! Check your docker configuration.' -fi - -# Create required folders -mkdir -p \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /data/letsencrypt-acme-challenge \ - /run/nginx \ - /tmp/nginx/body \ - /var/log/nginx \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - -touch /var/log/nginx/error.log || true -chmod 777 /var/log/nginx/error.log || true -chmod -R 777 /var/cache/nginx || true -chmod 644 /etc/logrotate.d/nginx-proxy-manager diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh deleted file mode 100755 index 684166e1..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -log_info 'Setting ownership ...' - -# root -chown root /tmp/nginx - -# npmuser -chown -R "$PUID:$PGID" /data \ - /etc/letsencrypt \ - /run/nginx \ - /tmp/nginx \ - /var/cache/nginx \ - /var/lib/logrotate \ - /var/lib/nginx \ - /var/log/nginx - -# Don't chown entire /etc/nginx folder as this causes crashes on some systems -chown -R "$PUID:$PGID" /etc/nginx/nginx \ - /etc/nginx/nginx.conf \ - /etc/nginx/conf.d diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh deleted file mode 100755 index 0cb9f126..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -log_info 'Dynamic resolvers ...' - -DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') - -# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]` -# thanks @tfmm -if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; -then - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf -else - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf -fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh deleted file mode 100755 index bc27eb14..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# This command reads the `DISABLE_IPV6` env var and will either enable -# or disable ipv6 in all nginx configs based on this setting. - -log_info 'IPv6 ...' - -# Lowercase -DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') - -process_folder () { - FILES=$(find "$1" -type f -name "*.conf") - SED_REGEX= - - if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then - # IPV6 is disabled - echo "Disabling IPV6 in hosts in: $1" - SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g' - else - # IPV6 is enabled - echo "Enabling IPV6 in hosts in: $1" - SED_REGEX='s/^(\s*)#listen \[::\]/\1listen [::]/g' - fi - - for FILE in $FILES - do - echo "- ${FILE}" - sed -E -i "$SED_REGEX" "$FILE" - done - - # ensure the files are still owned by the npmuser - chown -R "$PUID:$PGID" "$1" -} - -process_folder /etc/nginx/conf.d -process_folder /data/nginx diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh deleted file mode 100755 index faa22acc..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -# in s6, environmental variables are written as text files for s6 to monitor -# search through full-path filenames for files ending in "__FILE" -log_info 'Docker secrets ...' - -for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do - echo "[secret-init] Evaluating ${FILENAME##*/} ..." - - # set SECRETFILE to the contents of the full-path textfile - SECRETFILE=$(cat "${FILENAME}") - # if SECRETFILE exists / is not null - if [[ -f "${SECRETFILE}" ]]; then - # strip the appended "__FILE" from environmental variable name ... - STRIPFILE=$(echo "${FILENAME}" | sed "s/__FILE//g") - # echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod! - - # ... and set value to contents of secretfile - # since s6 uses text files, this is effectively "export ..." - printf $(cat "${SECRETFILE}") > "${STRIPFILE}" - # echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!" - echo "Success: ${STRIPFILE##*/} set from ${FILENAME##*/}" - - else - echo "Cannot find secret in ${FILENAME}" - fi -done diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh deleted file mode 100755 index 7991ddf4..00000000 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash - -set -e - -echo " -------------------------------------- - _ _ ____ __ __ -| \ | | _ \| \/ | -| \| | |_) | |\/| | -| |\ | __/| | | | -|_| \_|_| |_| |_| -------------------------------------- -User ID: $PUID -Group ID: $PGID -------------------------------------- -" diff --git a/rootfs/bin/start.sh b/rootfs/bin/start.sh index d459c6ea..d1251185 100755 --- a/rootfs/bin/start.sh +++ b/rootfs/bin/start.sh @@ -1,8 +1,15 @@ #!/bin/sh +if [ "$(id -u)" != "0" ]; then + echo '--------------------------------------' + echo "This docker container must be run as root, do not specify a user." + echo '--------------------------------------' + sleep inf || exit 1 +fi + if [ ! -d /data ]; then echo '--------------------------------------' - echo "ERROR: \"/data\" is not mounted! Check your compose file!." + echo "/data is not mounted! Check your docker configuration." echo '--------------------------------------' sleep inf || exit 1 fi @@ -97,6 +104,7 @@ mkdir -p /tmp/acme-challenge || sleep inf mkdir -vp /data/tls/certbot/renewal \ /data/tls/custom \ + /data/etc/npm \ /data/etc/html \ /data/etc/access \ /data/nginx/redirection_host \ @@ -105,6 +113,10 @@ mkdir -vp /data/tls/certbot/renewal \ /data/nginx/stream \ /data/nginx/custom || sleep inf +if [ -f /data/database.sqlite ] && [ "$DB_SQLITE_FILE" != "/data/database.sqlite" ]; then + mv -vn /data/database.sqlite "$DB_SQLITE_FILE" || sleep inf +fi + if [ -f /data/nginx/default_host/site.conf ]; then mv -vn /data/nginx/default_host/site.conf /data/nginx/default.conf || sleep inf fi @@ -397,6 +409,20 @@ if [ "$PHP82" = "true" ]; then fi fi +echo " +------------------------------------- + _ _ ____ __ __ +| \ | | _ \| \/ | +| \| | |_) | |\/| | +| |\ | __/| | | | +|_| \_|_| |_| |_| +------------------------------------- +User: $(whoami) +User ID: $(id -u) +Group ID: $(id -g) +------------------------------------- +" + while (nginx -t > /dev/null 2>&1 && if [ "$PHP81" = true ]; then PHP_INI_SCAN_DIR=/data/php/81/conf.d php-fpm81 -c /data/php/81 -y /data/php/81/php-fpm.conf -FORt > /dev/null 2>&1; fi && if [ "$PHP82" = true ]; then PHP_INI_SCAN_DIR=/data/php/82/conf.d php-fpm82 -c /data/php/82 -y /data/php/82/php-fpm.conf -FORt > /dev/null 2>&1; fi); do nginx || exit 1 & if [ "$PHP81" = "true" ]; then PHP_INI_SCAN_DIR=/data/php/81/conf.d php-fpm81 -c /data/php/81 -y /data/php/81/php-fpm.conf -FOR || exit 1; fi &