From 9844df27de4845bdb8589d4c10913566c8f50cce Mon Sep 17 00:00:00 2001 From: Semjon Nordmann Date: Thu, 31 Oct 2024 16:14:07 +0100 Subject: [PATCH 1/4] Support for chaning the HTTP and HTTPS port that hosts will listen on I have seen a few issues that mention that changing the port that nginx listens on is important (especially when using hostNetwork). I am not totally convinced that NPM should allow these use-cases but I was bored enough to implement it anyway. Related Issue: #4122 --- backend/internal/nginx.js | 41 +++++++++++++ backend/templates/_listen.conf | 12 ++-- docker/docker-compose.dev.yml | 2 + .../etc/s6-overlay/s6-rc.d/prepare/00-all.sh | 1 + .../s6-rc.d/prepare/55-http-https-port.sh | 59 +++++++++++++++++++ docs/src/advanced-config/index.md | 12 ++++ 6 files changed, 121 insertions(+), 6 deletions(-) create mode 100755 docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js index 5f802c004..d94fc04e3 100644 --- a/backend/internal/nginx.js +++ b/backend/internal/nginx.js @@ -5,6 +5,20 @@ const config = require('../lib/config'); const utils = require('../lib/utils'); const error = require('../lib/error'); +/** + * + * @param {int} user_port + * @param {int} default_port + * @returns {int} port + */ +const validatePort = (user_port, default_port) => { + if (user_port === NaN || user_port < 1 || user_port > 65535) { + console.error(`Environment variable HTTP_PORT must be an integer between 1 and 65535 (got: ${user_port}). Using default port ${default_port}`); + return default_port; + } + return user_port; +} + const internalNginx = { /** @@ -234,6 +248,8 @@ const internalNginx = { // Set the IPv6 setting for the host host.ipv6 = internalNginx.ipv6Enabled(); + host.http_port = internalNginx.httpPort(); + host.https_port = internalNginx.httpsPort(); locationsPromise.then(() => { renderEngine @@ -288,6 +304,8 @@ const internalNginx = { } certificate.ipv6 = internalNginx.ipv6Enabled(); + certificate.http_port = internalNginx.httpPort(); + certificate.https_port = internalNginx.httpsPort(); renderEngine .parseAndRender(template, certificate) @@ -432,7 +450,30 @@ const internalNginx = { } return true; + }, + + /** + * @returns {integer} + */ + httpPort: function () { + if (typeof process.env.HTTP_PORT !== 'undefined') { + let httpPort = parseInt(process.env.HTTP_PORT); + return validatePort(httpPort, 443); + } + return 80; + }, + + /** + * @returns {integer} + */ + httpsPort: function () { + if (typeof process.env.HTTPS_PORT !== 'undefined') { + let httpPort = parseInt(process.env.HTTPS_PORT); + return validatePort(httpPort, 443); + } + return 80; } + }; module.exports = internalNginx; diff --git a/backend/templates/_listen.conf b/backend/templates/_listen.conf index 34a808e6a..6448023b7 100644 --- a/backend/templates/_listen.conf +++ b/backend/templates/_listen.conf @@ -1,15 +1,15 @@ - listen 80; + listen {{ http_port }}; {% if ipv6 -%} - listen [::]:80; + listen [::]:{{ http_port }}; {% else -%} - #listen [::]:80; + #listen [::]:{{ http_port }}; {% endif %} {% if certificate -%} - listen 443 ssl; + listen {{ https_port }} ssl; {% if ipv6 -%} - listen [::]:443 ssl; + listen [::]:{{ https_port }} ssl; {% else -%} - #listen [::]:443; + #listen [::]:{{ https_port }}; {% endif %} {% endif %} server_name {{ domain_names | join: " " }}; diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 2bfa2b798..d6b42b66d 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -33,6 +33,8 @@ services: DB_MYSQL_NAME: 'npm' # DB_SQLITE_FILE: "/data/database.sqlite" # DISABLE_IPV6: "true" + # HTTP_PORT: "1234" + # HTTPS_PORT: "5678" # Required for DNS Certificate provisioning testing: LE_SERVER: 'https://ca.internal/acme/acme/directory' REQUESTS_CA_BUNDLE: '/etc/ssl/certs/NginxProxyManager.crt' 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 index d2e62f3bb..07f3c4c06 100755 --- 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 @@ -18,5 +18,6 @@ fi . /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/55-http-https-port.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/55-http-https-port.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh new file mode 100755 index 000000000..23f6ccfeb --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh @@ -0,0 +1,59 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +# This command reads the `HTTP_PORT` and `HTTPS_PORT` env vars and will rerender +# the nginx files to the port defined in these variables + +set -e + +log_info 'HTTP_PORT ...' + +DEFAULT_HTTP_PORT="80" +DEFAULT_HTTPS_PORT="443" + +# Make sure HTTP_PORT and HTTPS_PORT are set correctly +case "$HTTP_PORT" in + ''|*[!0-9]*) + echo "Could not parse HTTP_PORT as integer (got \"$HTTP_PORT\")." + echo "Using default http port \"$DEFAULT_HTTP_PORT\"" + HTTP_PORT="$DEFAULT_HTTP_PORT" + ;; + *) true ;; +esac +if [ "$HTTP_PORT" -lt "1" ] || [ "$HTTP_PORT" -gt "65535" ]; then + echo "HTTP_PORT must be between 1 and 65535 (got \"$HTTP_PORT\")." + echo "Using default http port \"$DEFAULT_HTTP_PORT\"" + HTTP_PORT="$DEFAULT_HTTP_PORT" +fi +case "$HTTPS_PORT" in + ''|*[!0-9]*) + echo "Could not parse HTTPS_PORT as integer (got \"$HTTPS_PORT\")." + echo "Using default https port \"$DEFAULT_HTTPS_PORT\"" + HTTPS_PORT="$DEFAULT_HTTPS_PORT" + ;; + *) true ;; +esac +if [ "$HTTPS_PORT" -lt "1" ] || [ "$HTTPS_PORT" -gt "65535" ]; then + echo "HTTPS_PORT must be between 1 and 65535 (got \"$HTTPS_PORT\")." + echo "Using default https port \"$DEFAULT_HTTPS_PORT\"" + HTTPS_PORT="$DEFAULT_HTTPS_PORT" +fi + +process_folder () { + FILES=$(find "$1" -type f -name "*.conf") + + HTTP_SED_REGEX='/ssl/! s/listen (\[::\]:)?[0-9]+/listen \1'$HTTP_PORT'/g' + HTTPS_SED_REGEX='/ssl/ s/listen (\[::\]:)?[0-9]+/listen \1'$HTTPS_PORT'/g' + + for FILE in $FILES + do + echo "- ${FILE}" + echo "$(sed -E "$HTTP_SED_REGEX" "$FILE")" > $FILE + echo "$(sed -E "$HTTPS_SED_REGEX" "$FILE")" > $FILE + done + + # ensure the files are still owned by the npm user + chown -R "$PUID:$PGID" "$1" +} + +process_folder /data/nginx diff --git a/docs/src/advanced-config/index.md b/docs/src/advanced-config/index.md index efeaefec3..f74d48960 100644 --- a/docs/src/advanced-config/index.md +++ b/docs/src/advanced-config/index.md @@ -164,6 +164,18 @@ The easy fix is to add a Docker environment variable to the Nginx Proxy Manager DISABLE_IPV6: 'true' ``` +## Chaning the HTTP and HTTPS Listen Port + +If you are unable to configure the port mapping within Docker (eg. when using +`hostNetwork: true`) you can change the port that proxy-hosts and +redirection-hosts listen on by setting the environment variables `HTTP_PORT` and +`HTTPS_PORT`: + +```yml + environment: + HTTP_PORT: "1234" + HTTPS_PORT: "5678" +``` ## Custom Nginx Configurations From c377db5d62dd110697d3834c21a05a7432d28be6 Mon Sep 17 00:00:00 2001 From: Semjon Nordmann Date: Thu, 31 Oct 2024 16:39:37 +0100 Subject: [PATCH 2/4] ups, forgot eslint --- backend/internal/nginx.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js index d94fc04e3..0b0590513 100644 --- a/backend/internal/nginx.js +++ b/backend/internal/nginx.js @@ -12,12 +12,12 @@ const error = require('../lib/error'); * @returns {int} port */ const validatePort = (user_port, default_port) => { - if (user_port === NaN || user_port < 1 || user_port > 65535) { + if (isNaN(user_port) || user_port < 1 || user_port > 65535) { console.error(`Environment variable HTTP_PORT must be an integer between 1 and 65535 (got: ${user_port}). Using default port ${default_port}`); return default_port; } return user_port; -} +}; const internalNginx = { @@ -246,9 +246,9 @@ const internalNginx = { locationsPromise = Promise.resolve(); } - // Set the IPv6 setting for the host - host.ipv6 = internalNginx.ipv6Enabled(); - host.http_port = internalNginx.httpPort(); + // Set the IPv6 and port setting for the host + host.ipv6 = internalNginx.ipv6Enabled(); + host.http_port = internalNginx.httpPort(); host.https_port = internalNginx.httpsPort(); locationsPromise.then(() => { @@ -303,8 +303,8 @@ const internalNginx = { return; } - certificate.ipv6 = internalNginx.ipv6Enabled(); - certificate.http_port = internalNginx.httpPort(); + certificate.ipv6 = internalNginx.ipv6Enabled(); + certificate.http_port = internalNginx.httpPort(); certificate.https_port = internalNginx.httpsPort(); renderEngine From 8d0ed27851e18fbc8a3d108a4eefd8d9ba9e1503 Mon Sep 17 00:00:00 2001 From: Semjon Nordmann Date: Mon, 16 Dec 2024 22:09:24 +0100 Subject: [PATCH 3/4] Process folder /etc/nginx/conf.d for HTTP/HTTPS port migration --- .../etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh index 23f6ccfeb..9099faa51 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh @@ -45,6 +45,9 @@ process_folder () { HTTP_SED_REGEX='/ssl/! s/listen (\[::\]:)?[0-9]+/listen \1'$HTTP_PORT'/g' HTTPS_SED_REGEX='/ssl/ s/listen (\[::\]:)?[0-9]+/listen \1'$HTTPS_PORT'/g' + echo "Setting HTTP listen port to $HTTP_PORT in: $FILES" + echo "Setting HTTPS listen port to $HTTPS_PORT in: $FILES" + for FILE in $FILES do echo "- ${FILE}" @@ -56,4 +59,5 @@ process_folder () { chown -R "$PUID:$PGID" "$1" } +process_folder /etc/nginx/conf.d process_folder /data/nginx From 2a9e573c61fe6d1a0907ab9b204403b393c9caf1 Mon Sep 17 00:00:00 2001 From: Semjon Nordmann Date: Mon, 16 Dec 2024 22:45:16 +0100 Subject: [PATCH 4/4] Adjust log message format --- .../etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh index 9099faa51..c5c05616e 100755 --- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/55-http-https-port.sh @@ -45,8 +45,7 @@ process_folder () { HTTP_SED_REGEX='/ssl/! s/listen (\[::\]:)?[0-9]+/listen \1'$HTTP_PORT'/g' HTTPS_SED_REGEX='/ssl/ s/listen (\[::\]:)?[0-9]+/listen \1'$HTTPS_PORT'/g' - echo "Setting HTTP listen port to $HTTP_PORT in: $FILES" - echo "Setting HTTPS listen port to $HTTPS_PORT in: $FILES" + echo "Setting HTTP listen port to $HTTP_PORT and HTTPS listen port to $HTTPS_PORT in: $1" for FILE in $FILES do