Skip to content

Commit 9ca59b9

Browse files
committed
allow to run as non-root (NginxProxyManager#246)
Signed-off-by: Zoey <[email protected]>
1 parent ec29d4c commit 9ca59b9

File tree

10 files changed

+128
-78
lines changed

10 files changed

+128
-78
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,6 @@ x86/
436436
[Aa][Rr][Mm]/
437437
[Aa][Rr][Mm]64/
438438
bld/
439-
[Bb]in/
440439
[Oo]bj/
441440
[Ll]og/
442441
[Ll]ogs/

Dockerfile

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,30 @@ RUN apk add --no-cache ca-certificates nodejs-current yarn && \
2929
node-prune && \
3030
yarn cache clean --all
3131

32+
FROM python:3.11.3-alpine3.17 as certbot
33+
RUN apk add --no-cache build-base libffi-dev && \
34+
python3 -m venv /usr/local/certbot && \
35+
. /usr/local/certbot/bin/activate && \
36+
pip install --no-cache-dir certbot
3237

33-
FROM zoeyvid/nginx-quic:110
38+
FROM zoeyvid/nginx-quic:111
3439
RUN apk add --no-cache ca-certificates tzdata \
3540
nodejs-current \
3641
openssl apache2-utils \
37-
coreutils grep jq curl \
38-
build-base libffi-dev && \
39-
# Install Certbot
40-
pip install --no-cache-dir certbot && \
41-
# Clean
42-
apk del --no-cache build-base libffi-dev
42+
coreutils grep jq curl shadow sudo
4343

4444
COPY rootfs /
4545
COPY --from=backend /build/backend /app
4646
COPY --from=frontend /build/frontend/dist /app/frontend
47+
COPY --from=certbot /usr/local/certbot /usr/local/certbot
4748

4849
RUN ln -s /app/password-reset.js /usr/local/bin/password-reset.js && \
4950
ln -s /app/sqlite-vaccum.js /usr/local/bin/sqlite-vaccum.js && \
5051
ln -s /app/index.js /usr/local/bin/index.js
5152

5253
ENV NODE_ENV=production \
5354
NODE_CONFIG_DIR=/data/etc/npm \
55+
PATH="/usr/local/certbot/bin:$PATH" \
5456
DB_SQLITE_FILE=/data/etc/npm/database.sqlite
5557

5658
WORKDIR /app

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ so that the barrier for entry here is low.
6767
- Auto certbot old certs clean (FULLCLEAN=true)
6868
- Passwort reset (only sqlite) (`docker exec -it nginx-proxy-manager password-reset.js USER_EMAIL PASSWORD`)
6969
- 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)
70+
- PUID/GGID support in network mode host (please add `net.ipv4.ip_unprivileged_port_start=0` at the end of `/etc/sysctl.conf`)
7071

7172
## Soon
7273
- 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))
7374
- custom IP-Bindings in nginx/backend to allow multiple instances in host network mode
74-
- support changing the PUID/PGID (maybe)
75+
- dark mode
7576
- more
7677

7778
## migration
7879
- **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
7980
- 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
8081
- some buttons have changed, check if they are still correct
8182
- 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)
82-
- changing the PUID/PGID is not supported (since it would break running in network_mode host)
8383

8484
# Use as webserver
8585

@@ -136,10 +136,12 @@ services:
136136
network_mode: host
137137
volumes:
138138
- "/opt/npm:/data"
139-
# - "/opt/npm-letsencrypt:/etc/letsencrypt" # Only needed for first time migration from original nginx-proxy-manager to this fork
140139
# - "/var/www:/var/www" # optional, if you want to use it as webserver for html/php
140+
# - "/opt/npm-letsencrypt:/etc/letsencrypt" # Only needed for first time migration from original nginx-proxy-manager to this fork
141141
environment:
142-
- "TZ=Europe/Berlin"
142+
- "TZ=Europe/Berlin" # set timezone
143+
# - "PUID=1000" # set group id
144+
# - "PGID=1000" # set user id
143145
# - "NGINX_LOG_NOT_FOUND=true" # Allow logging of 404 errors
144146
# - "NPM_LISTEN_LOCALHOST=true" # Bind the NPM Dashboard on Port 81 only to localhost
145147
# - "NPM_CERT_ID=1" # ID of cert, which should be used instead of dummycerts

backend/internal/certificate.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const path = require('path');
1616
const { isArray } = require('lodash');
1717

1818
const certbotConfig = '/data/tls/certbot/config.ini';
19-
const certbotCommand = 'certbot --config-dir /data/tls/certbot';
19+
const certbotCommand = 'certbot --logs-dir /tmp/certbot-log --work-dir /tmp/certbot-work --config-dir /data/tls/certbot';
2020

2121
function omissions() {
2222
return ['is_deleted'];
@@ -875,7 +875,7 @@ const internalCertificate = {
875875
// Escape single quotes and backslashes
876876
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
877877
const credentialsCmd = 'mkdir -p /data/tls/certbot/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
878-
const prepareCmd = 'pip install --no-cache-dir ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies;
878+
const prepareCmd = 'pip install --no-cache-dir ' + dns_plugin.package_name;
879879

880880
// Whether the plugin has a --<name>-credentials argument
881881
const hasConfigArg = certificate.meta.dns_provider !== 'route53';

backend/internal/ip_ranges.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ const internalIpRanges = {
122122
const renderEngine = utils.getRenderEngine();
123123
return new Promise((resolve, reject) => {
124124
let template = null;
125-
let filename = '/usr/local/nginx/conf/conf.d/include/ip_ranges.conf';
125+
let filename = '/data/nginx/ip_ranges.conf';
126126
try {
127127
template = fs.readFileSync(__dirname + '/../templates/ip_ranges.conf', {encoding: 'utf8'});
128128
} catch (err) {

backend/setup.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ const setupCertbotPlugins = () => {
117117
certificates.map(function (certificate) {
118118
if (certificate.meta && certificate.meta.dns_challenge === true) {
119119
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
120-
const packages_to_install = `${dns_plugin.package_name}${dns_plugin.version_requirement || ''} ${dns_plugin.dependencies}`;
120+
const packages_to_install = `${dns_plugin.package_name}`;
121121

122122
if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
123123

compose.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ services:
77
network_mode: host
88
volumes:
99
- "/opt/npm:/data"
10-
# - "/opt/npm-letsencrypt:/etc/letsencrypt" # Only needed for first time migration from original nginx-proxy-manager to this fork
1110
# - "/var/www:/var/www" # optional, if you want to use it as webserver for html/php
11+
# - "/opt/npm-letsencrypt:/etc/letsencrypt" # Only needed for first time migration from original nginx-proxy-manager to this fork
1212
environment:
13-
- "TZ=Europe/Berlin"
13+
- "TZ=Europe/Berlin" # set timezone
14+
# - "PUID=1000" # set group id
15+
# - "PGID=1000" # set user id
1416
# - "NGINX_LOG_NOT_FOUND=true" # Allow logging of 404 errors
1517
# - "NPM_LISTEN_LOCALHOST=true" # Bind the NPM Dashboard on Port 81 only to localhost
1618
# - "NPM_CERT_ID=1" # ID of cert, which should be used instead of dummycerts

rootfs/bin/launch.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/bin/sh
2+
3+
echo "
4+
-------------------------------------
5+
_ _ ____ __ __
6+
| \ | | _ \| \/ |
7+
| \| | |_) | |\/| |
8+
| |\ | __/| | | |
9+
|_| \_|_| |_| |_|
10+
-------------------------------------
11+
User: $(whoami)
12+
User ID: $(id -u)
13+
Group ID: $(id -g)
14+
-------------------------------------
15+
"
16+
17+
if ! nginx -t > /dev/null 2>&1; then
18+
nginx -T || sleep inf
19+
sleep inf
20+
fi
21+
22+
if [ "$PHP81" = "true" ]; then
23+
if ! 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; then
24+
PHP_INI_SCAN_DIR=/data/php/81/conf.d php-fpm81 -c /data/php/81 -y /data/php/81/php-fpm.conf -FORt || sleep inf
25+
sleep inf
26+
fi
27+
fi
28+
29+
if [ "$PHP82" = "true" ]; then
30+
if ! 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; then
31+
PHP_INI_SCAN_DIR=/data/php/82/conf.d php-fpm82 -c /data/php/82 -y /data/php/82/php-fpm.conf -FORt || sleep inf
32+
sleep inf
33+
fi
34+
fi
35+
36+
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
37+
nginx &
38+
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; fi &
39+
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 -FOR; fi &
40+
index.js &
41+
wait
42+
done
43+
44+
if ! nginx -t > /dev/null 2>&1; then
45+
nginx -T || sleep inf
46+
fi
47+
48+
if [ "$PHP81" = "true" ]; then
49+
if ! 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; then
50+
PHP_INI_SCAN_DIR=/data/php/81/conf.d php-fpm81 -c /data/php/81 -y /data/php/81/php-fpm.conf -FORt || sleep inf
51+
fi
52+
fi
53+
54+
if [ "$PHP82" = "true" ]; then
55+
if ! 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; then
56+
PHP_INI_SCAN_DIR=/data/php/82/conf.d php-fpm82 -c /data/php/82 -y /data/php/82/php-fpm.conf -FORt || sleep inf
57+
fi
58+
fi

rootfs/bin/start.sh

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ if [ ! -d /data ]; then
1414
sleep inf || exit 1
1515
fi
1616

17+
export PUID="${PUID:-0}" || exit 1
18+
if ! echo "$PUID" | grep -q "^[0-9]\+$"; then
19+
echo "You've set PUID but not to an allowed value." || sleep inf
20+
echo "It needs to be a string. Allowed are small digits 0-9" || sleep inf
21+
echo "It is set to \"$PUID\"." || sleep inf
22+
sleep inf || exit 1
23+
fi
24+
25+
export PGID="${PGID:-0}" || exit 1
26+
if ! echo "$PGID" | grep -q "^[0-9]\+$"; then
27+
echo "You've set PGID but not to an allowed value." || sleep inf
28+
echo "It needs to be a string. Allowed are small digits 0-9" || sleep inf
29+
echo "It is set to \"$PGID\"." || sleep inf
30+
sleep inf || exit 1
31+
fi
32+
1733
if [ "$PHP81" = true ] || [ "$PHP82" = true ]; then
1834
apk add --no-cache fcgi
1935
fi
@@ -100,7 +116,9 @@ else
100116
rm -vrf /data/php/82
101117
fi
102118

103-
mkdir -p /tmp/acme-challenge || sleep inf
119+
mkdir -p /tmp/acme-challenge \
120+
/tmp/certbot-work \
121+
/tmp/certbot-log || sleep inf
104122

105123
mkdir -vp /data/tls/certbot/renewal \
106124
/data/tls/custom \
@@ -237,6 +255,7 @@ find /data/nginx -type f -name '*.conf' -exec sed -i "/ssl_stapling_verify/d" {}
237255

238256
touch /data/etc/html/index.html \
239257
/data/nginx/default.conf \
258+
/data/nginx/ip_ranges.conf \
240259
/data/nginx/custom/root.conf \
241260
/data/nginx/custom/events.conf \
242261
/data/nginx/custom/http.conf \
@@ -247,8 +266,7 @@ touch /data/etc/html/index.html \
247266
/data/nginx/custom/stream.conf \
248267
/data/nginx/custom/server_stream.conf \
249268
/data/nginx/custom/server_stream_tcp.conf \
250-
/data/nginx/custom/server_stream_udp.conf \
251-
/usr/local/nginx/conf/conf.d/include/ip_ranges.conf || sleep inf
269+
/data/nginx/custom/server_stream_udp.conf || sleep inf
252270

253271
if [ -z "$NPM_CERT_ID" ]; then
254272
export NPM_CERT=/data/tls/dummycert.pem || sleep inf
@@ -375,9 +393,6 @@ else
375393
/data/tls/dummykey.pem || sleep inf
376394
fi
377395

378-
chmod -R 600 /data/tls \
379-
/data/etc/access
380-
381396
if [ ! -f /data/nginx/default.conf ]; then
382397
mv -vn /usr/local/nginx/conf/conf.d/include/default.conf /data/nginx/default.conf || sleep inf
383398
fi
@@ -390,59 +405,32 @@ sed -i "s|ssl_certificate .*|ssl_certificate $NPM_CERT;|g" /data/nginx/default.c
390405
sed -i "s|ssl_certificate_key .*|ssl_certificate_key $NPM_KEY;|g" /data/nginx/default.conf || sleep inf
391406
if [ -n "$NPM_CHAIN" ]; then sed -i "s|ssl_trusted_certificate .*|ssl_trusted_certificate $NPM_CHAIN;|g" /data/nginx/default.conf || sleep inf; fi
392407

393-
if ! nginx -t > /dev/null 2>&1; then
394-
nginx -T || sleep inf
395-
sleep inf || exit 1
396-
fi
397408

398-
if [ "$PHP81" = "true" ]; then
399-
if ! 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; then
400-
PHP_INI_SCAN_DIR=/data/php/81/conf.d php-fpm81 -c /data/php/81 -y /data/php/81/php-fpm.conf -FORt || sleep inf
401-
sleep inf || exit 1
402-
fi
403-
fi
409+
chmod -R o-rwx /data/tls \
410+
/data/etc/npm \
411+
/data/etc/access || exit 1
404412

405-
if [ "$PHP82" = "true" ]; then
406-
if ! 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; then
407-
PHP_INI_SCAN_DIR=/data/php/82/conf.d php-fpm82 -c /data/php/82 -y /data/php/82/php-fpm.conf -FORt || sleep inf
408-
sleep inf || exit 1
409-
fi
410-
fi
411-
412-
echo "
413-
-------------------------------------
414-
_ _ ____ __ __
415-
| \ | | _ \| \/ |
416-
| \| | |_) | |\/| |
417-
| |\ | __/| | | |
418-
|_| \_|_| |_| |_|
419-
-------------------------------------
420-
User: $(whoami)
421-
User ID: $(id -u)
422-
Group ID: $(id -g)
423-
-------------------------------------
424-
"
425-
426-
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
427-
nginx || exit 1 &
428-
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 &
429-
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 -FOR || exit 1; fi &
430-
index.js || exit 1 &
431-
wait
432-
done
433-
434-
if ! nginx -t > /dev/null 2>&1; then
435-
nginx -T || sleep inf
436-
fi
437-
438-
if [ "$PHP81" = "true" ]; then
439-
if ! 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; then
440-
PHP_INI_SCAN_DIR=/data/php/81/conf.d php-fpm81 -c /data/php/81 -y /data/php/81/php-fpm.conf -FORt || sleep inf
441-
fi
442-
fi
443-
444-
if [ "$PHP82" = "true" ]; then
445-
if ! 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; then
446-
PHP_INI_SCAN_DIR=/data/php/82/conf.d php-fpm82 -c /data/php/82 -y /data/php/82/php-fpm.conf -FORt || sleep inf
413+
if [ "$PUID" != "0" ]; then
414+
if id -u npmuser > /dev/null 2>&1; then
415+
usermod -u "$PUID" npmuser || exit 1
416+
else
417+
useradd -o -u "$PUID" -U -d /tmp/npmuserhome -s /bin/false npmuser || exit 1
447418
fi
419+
usermod -G "$PGID" npmuser || exit 1
420+
groupmod -o -g "$PGID" npmuser || exit 1
421+
chown -R "$PUID:$PGID" /usr/local/certbot \
422+
/usr/local/nginx \
423+
/data \
424+
/tmp/acme-challenge \
425+
/tmp/certbot-work \
426+
/tmp/certbot-log || exit 1
427+
sudo -Eu npmuser launch.sh || exit 1
428+
else
429+
chown -R 0:0 /usr/local/certbot \
430+
/usr/local/nginx \
431+
/data \
432+
/tmp/acme-challenge \
433+
/tmp/certbot-work \
434+
/tmp/certbot-log || exit 1
435+
launch.sh || exit 1
448436
fi

rootfs/usr/local/nginx/conf/nginx.conf

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
user root;
21
daemon off;
32
pcre_jit on;
43
worker_processes auto;
@@ -101,7 +100,7 @@ http {
101100

102101
include fastcgi.conf;
103102

104-
include conf.d/include/ip_ranges.conf;
103+
include /data/nginx/ip_ranges.conf;
105104

106105
include /data/nginx/default.conf;
107106
include conf.d/*.conf;

0 commit comments

Comments
 (0)