diff --git a/README.md b/README.md index d3b40dc1..f0702773 100644 --- a/README.md +++ b/README.md @@ -90,22 +90,25 @@ so that the barrier for entry here is low. ## migration - **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 -- since many buttons changed, please edit every host you have and click save. (Please also resave it, if all buttons/values are fine, to update the host config to fully fit the NPMplus template) -- please delete all dnspod certs and recreate them OR you manually change the credentialsfile (see [here](https://github.com/ZoeyVid/npmplus/blob/develop/global/certbot-dns-plugins.js) for the template) +- please delete all dnspod certs and recreate them after migration OR you manually change the credentialsfile (see [here](https://github.com/ZoeyVid/npmplus/blob/develop/global/certbot-dns-plugins.json) for the template) +- stop nginx-proxy-manager download the latest compose.yaml, adjust your paths (of /etc/letsencrypt and /data) to the ones you used with nginx-proxy-manager and adjust the env of the compose file how you like it and then deploy it +- you can now remove the /etc/letsencrypt mount, since it was moved to /data while migration and redeploy the compose file - since this fork has dependency on `network_mode: host`, please don't forget to open port 80/tcp, 443/tcp and 443/udp (and maybe 81/tcp) in your firewall -- if you have a healthcheck defined in your compose yaml file, remove it - this fork defines its own healthcheck in the Dockerfile, so you don't need to have it in compose anymore -- please report all migration issues you have +- since many buttons changed, please edit every host you have and click save. (Please also resave it, if all buttons/values are fine, to update the host config to fully fit the NPMplus template) +- maybe setup crowdsec (see below) +- please report all (migration) issues you may have # Quick Setup 1. Install Docker and Docker Compose (or portainer) - [Docker Install documentation](https://docs.docker.com/engine) - [Docker Compose Install documentation](https://docs.docker.com/compose/install/linux) -2. Create a compose.yaml file similar to [this](https://github.com/ZoeyVid/NPMplus/blob/develop/compose.yaml) (or use it as a portainer stack): -3. Bring up your stack by running (or deploy your portainer stack) +2. Download this [compose.yaml](https://raw.githubusercontent.com/ZoeyVid/NPMplus/refs/heads/develop/compose.yaml) (or use its content as a portainer stack) +3. adjust TZ and ACME_EMAIL to your values and maybe adjust other env options to your needs. +4. start NPMplus by running (or deploy your portainer stack) ```bash docker compose up -d ``` -4. Log in to the Admin UI +5. Log in to the Admin UI When your docker container is running, connect to it on port `81` for the admin interface. Sometimes this can take a little bit because of the entropy of keys. You may need to open port 81 in your firewall. @@ -119,7 +122,7 @@ Password: iArhP1j7p1P6TA92FA2FMbbUGYqwcYzxC4AVEe12Wbi94FY9gNN62aKyF1shrvG4NycjjX Immediately after logging in with this default user you will be asked to modify your details and change your password. # Crowdsec -1. Install crowdsec using this compose file: https://github.com/ZoeyVid/NPMplus/blob/develop/compose.crowdsec.yaml and enable LOGROTATE +1. Install crowdsec using this compose file: https://github.com/ZoeyVid/NPMplus/blob/develop/compose.crowdsec.yaml and enable LOGROTATE in the NPMplus compose file 2. open `/opt/crowdsec/conf/acquis.d/npmplus.yaml` and fill it with: ```yaml filenames: diff --git a/backend/app.js b/backend/app.js index 8f2702ce..d74996f9 100644 --- a/backend/app.js +++ b/backend/app.js @@ -23,28 +23,6 @@ app.disable('x-powered-by'); app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); app.enable('strict routing'); -// CORS for everything -app.use(require('./lib/express/cors')); - -// General security/cache related headers + server header -app.use(function (req, res, next) { - let x_frame_options = 'DENY'; - - if (typeof process.env.X_FRAME_OPTIONS !== 'undefined' && process.env.X_FRAME_OPTIONS) { - x_frame_options = process.env.X_FRAME_OPTIONS; - } - - res.set({ - 'X-XSS-Protection': '1; mode=block', - 'X-Content-Type-Options': 'nosniff', - 'X-Frame-Options': x_frame_options, - 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', - Pragma: 'no-cache', - Expires: 0, - }); - next(); -}); - app.use(require('./lib/express/jwt')()); app.use('/', require('./routes/main')); diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 240f3810..58d56a4d 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -16,7 +16,7 @@ const internalAuditLog = require('./audit-log'); const internalNginx = require('./nginx'); const certbotCommand = 'certbot'; -const certbotArgs = ['--logs-dir', '/tmp/certbot-log', '--work-dir', '/tmp/certbot-work', '--config-dir', '/data/tls/certbot', '--config', '/etc/tls/certbot.ini', '--agree-tos', '--non-interactive', '--no-eff-email', '--register-unsafely-without-email']; +const certbotArgs = ['--logs-dir', '/tmp/certbot-log', '--work-dir', '/tmp/certbot-work', '--config-dir', '/data/tls/certbot', '--config', '/etc/certbot.ini', '--agree-tos', '--non-interactive', '--no-eff-email', '--register-unsafely-without-email']; function omissions() { return ['is_deleted', 'owner.is_deleted']; diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js index fe371a52..81661a41 100644 --- a/backend/internal/nginx.js +++ b/backend/internal/nginx.js @@ -79,7 +79,14 @@ const internalNginx = { * @returns {Promise} */ test: () => { - return utils.execFile('nginx', ['-tq']); + return utils + .execFile('certbot-ocsp-fetcher.sh', ['-c', '/data/tls/certbot', '-o', '/data/tls/certbot/live', '--no-reload-webserver', '--quiet']) + .then(() => { + return utils.execFile('nginx', ['-tq']); + }) + .catch(() => { + return utils.execFile('nginx', ['-tq']); + }); }, /** @@ -88,26 +95,13 @@ const internalNginx = { reload: () => { return internalNginx.test().then(() => { - return utils - .execFile('certbot-ocsp-fetcher.sh', ['-c', '/data/tls/certbot', '-o', '/data/tls/certbot/live', '--no-reload-webserver', '--quiet']) - .then(() => { - if (fs.existsSync('/usr/local/nginx/logs/nginx.pid') && fs.readFileSync('/usr/local/nginx/logs/nginx.pid', 'utf8').trim().length > 0) { - logger.info('Reloading Nginx'); - return utils.execFile('nginx', ['-s', 'reload']); - } else { - logger.info('Starting Nginx'); - return utils.execfg('nginx', ['-e', 'stderr']); - } - }) - .catch(() => { - if (fs.existsSync('/usr/local/nginx/logs/nginx.pid') && fs.readFileSync('/usr/local/nginx/logs/nginx.pid', 'utf8').trim().length > 0) { - logger.info('Reloading Nginx'); - return utils.execFile('nginx', ['-s', 'reload']); - } else { - logger.info('Starting Nginx'); - return utils.execfg('nginx', ['-e', 'stderr']); - } - }); + if (fs.existsSync('/usr/local/nginx/logs/nginx.pid') && fs.readFileSync('/usr/local/nginx/logs/nginx.pid', 'utf8').trim().length > 0) { + logger.info('Reloading Nginx'); + return utils.execFile('nginx', ['-s', 'reload']); + } else { + logger.info('Starting Nginx'); + utils.execfg('nginx', ['-e', 'stderr']); + } }); }, diff --git a/backend/lib/express/cors.js b/backend/lib/express/cors.js deleted file mode 100644 index 2b33c420..00000000 --- a/backend/lib/express/cors.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = function (req, res, next) { - if (req.headers.origin) { - res.set({ - 'Access-Control-Allow-Origin': req.headers.origin, - 'Access-Control-Allow-Credentials': true, - 'Access-Control-Allow-Methods': 'OPTIONS, GET, POST, PUT', - 'Access-Control-Allow-Headers': 'Content-Type, Cache-Control, Pragma, Expires, Authorization, X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit', - 'Access-Control-Max-Age': 5 * 60, - 'Access-Control-Expose-Headers': 'X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit', - }); - next(); - } else { - // No origin - next(); - } -}; diff --git a/compose.yaml b/compose.yaml index 940035d7..7d86f35b 100644 --- a/compose.yaml +++ b/compose.yaml @@ -19,7 +19,7 @@ services: # - "NIBEP=48694" # internal port of the NPMplus API, always bound to 127.0.0.1, default 48693, you need to change it, if you want to run multiple npm instances in network mode host # - "GOAIWSP=48684" # internal port of goaccess, always bound to 127.0.0.1, default 48683, you need to change it, if you want to run multiple npm with goaccess instances in network mode host # - "NPM_PORT=82" # Port the NPM UI should be bound to, default 81, you need to change it, if you want to run multiple npm instances in network mode host -# - "NPM_PORT=92" # Port the goaccess should be bound to, default 91, you need to change it, if you want to run multiple npm with goaccess instances in network mode host +# - "GOA_PORT=92" # Port the goaccess should be bound to, default 91, you need to change it, if you want to run multiple npm with goaccess instances in network mode host # - "IPV4_BINDING=127.0.0.1" # IPv4 address to bind, defaults to all # - "NPM_IPV4_BINDING=127.0.0.1" # IPv4 address to bind for the NPM UI, defaults to all # - "GOA_IPV4_BINDING=127.0.0.1" # IPv4 address to bind for the goaccess, defaults to all diff --git a/frontend/package.json b/frontend/package.json index 93700252..3d4654c7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,7 +4,7 @@ "description": "A beautiful interface for creating Nginx endpoints", "main": "js/index.js", "dependencies": { - "@babel/core": "7.25.8", + "@babel/core": "7.25.9", "babel-core": "6.26.3", "babel-loader": "8.4.1", "babel-preset-env": "1.7.0", diff --git a/rootfs/etc/tls/certbot.ini b/rootfs/etc/certbot.ini similarity index 100% rename from rootfs/etc/tls/certbot.ini rename to rootfs/etc/certbot.ini diff --git a/rootfs/etc/tls/dhparam b/rootfs/etc/dhparam similarity index 100% rename from rootfs/etc/tls/dhparam rename to rootfs/etc/dhparam diff --git a/rootfs/usr/local/bin/start.sh b/rootfs/usr/local/bin/start.sh index 808aa5b7..5342708c 100755 --- a/rootfs/usr/local/bin/start.sh +++ b/rootfs/usr/local/bin/start.sh @@ -34,6 +34,11 @@ if [ -n "$DEBUG" ]; then sleep inf fi +if [ -n "$X_FRAME_OPTIONS" ]; then + echo "X_FRAME_OPTIONS env is unsopported." + sleep inf +fi + if [ -n "$LE_STAGING" ]; then echo "LE_STAGING env is unsopported, please use ACME_SERVER." sleep inf diff --git a/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers-no-stapling.conf b/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers-no-stapling.conf index 09a30d26..9716e3bf 100644 --- a/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers-no-stapling.conf +++ b/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers-no-stapling.conf @@ -6,11 +6,11 @@ ssl_stapling_verify off; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; -ssl_dhparam /etc/tls/dhparam; +ssl_dhparam /etc/dhparam; ssl_protocols TLSv1.2 TLSv1.3; ssl_ecdh_curve X25519MLKEM768:x25519_kyber768:x25519:x448:secp521r1:secp384r1:secp256r1; ssl_prefer_server_ciphers on; ssl_conf_command Options PrioritizeChaCha; -ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305; +ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256; diff --git a/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers.conf b/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers.conf index 81267b26..42590857 100644 --- a/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers.conf +++ b/rootfs/usr/local/nginx/conf/conf.d/include/tls-ciphers.conf @@ -6,11 +6,11 @@ ssl_stapling_verify on; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; -ssl_dhparam /etc/tls/dhparam; +ssl_dhparam /etc/dhparam; ssl_protocols TLSv1.2 TLSv1.3; ssl_ecdh_curve X25519MLKEM768:x25519_kyber768:x25519:x448:secp521r1:secp384r1:secp256r1; ssl_prefer_server_ciphers on; ssl_conf_command Options PrioritizeChaCha; -ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305; +ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256;