Browse Source

Merge pull request #4651 from NginxProxyManager/develop

v2.12.5
jc21 5 months ago
parent
commit
307cb94e84

+ 1 - 1
.version

@@ -1 +1 @@
-2.12.4
+2.12.5

+ 10 - 5
Jenkinsfile

@@ -241,12 +241,17 @@ pipeline {
 					}
 					steps {
 						script {
-							npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on
-[DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev)
-as `nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}`
+							npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev):
+```
+nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}
+```
 
-**Note:** ensure you backup your NPM instance before testing this image! Especially if there are database changes
-**Note:** this is a different docker image namespace than the official image
+> [!NOTE]
+> Ensure you backup your NPM instance before testing this image! Especially if there are database changes.
+> This is a different docker image namespace than the official image.
+
+> [!WARNING]
+> Changes and additions to DNS Providers require verification by at least 2 members of the community!
 """, true)
 						}
 					}

+ 1 - 1
README.md

@@ -1,7 +1,7 @@
 <p align="center">
 	<img src="https://nginxproxymanager.com/github.png">
 	<br><br>
-	<img src="https://img.shields.io/badge/version-2.12.4-green.svg?style=for-the-badge">
+	<img src="https://img.shields.io/badge/version-2.12.5-green.svg?style=for-the-badge">
 	<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
 		<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
 	</a>

+ 12 - 5
backend/lib/certbot.js

@@ -11,7 +11,7 @@ const certbot = {
 	/**
 	 * @param {array} pluginKeys
 	 */
-	installPlugins: async function (pluginKeys) {
+	installPlugins: async (pluginKeys) => {
 		let hasErrors = false;
 
 		return new Promise((resolve, reject) => {
@@ -21,7 +21,7 @@ const certbot = {
 			}
 
 			batchflow(pluginKeys).sequential()
-				.each((i, pluginKey, next) => {
+				.each((_i, pluginKey, next) => {
 					certbot.installPlugin(pluginKey)
 						.then(() => {
 							next();
@@ -51,7 +51,7 @@ const certbot = {
 	 * @param   {string}  pluginKey
 	 * @returns {Object}
 	 */
-	installPlugin: async function (pluginKey) {
+	installPlugin: async (pluginKey) => {
 		if (typeof dnsPlugins[pluginKey] === 'undefined') {
 			// throw Error(`Certbot plugin ${pluginKey} not found`);
 			throw new error.ItemNotFoundError(pluginKey);
@@ -63,8 +63,15 @@ const certbot = {
 		plugin.version      = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
 		plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
 
-		const cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugin.dependencies + ' ' + plugin.package_name + plugin.version + ' ' + ' && deactivate';
-		return utils.exec(cmd)
+		// SETUPTOOLS_USE_DISTUTILS is required for certbot plugins to install correctly
+		// in new versions of Python
+		let env = Object.assign({}, process.env, {SETUPTOOLS_USE_DISTUTILS: 'stdlib'});
+		if (typeof plugin.env === 'object') {
+			env = Object.assign(env, plugin.env);
+		}
+
+		const cmd = `. /opt/certbot/bin/activate && pip install --no-cache-dir ${plugin.dependencies} ${plugin.package_name}${plugin.version}  && deactivate`;
+		return utils.exec(cmd, {env})
 			.then((result) => {
 				logger.complete(`Installed ${pluginKey}`);
 				return result;

+ 9 - 9
backend/lib/utils.js

@@ -1,13 +1,13 @@
 const _          = require('lodash');
-const exec       = require('child_process').exec;
-const execFile   = require('child_process').execFile;
+const exec       = require('node:child_process').exec;
+const execFile   = require('node:child_process').execFile;
 const { Liquid } = require('liquidjs');
 const logger     = require('../logger').global;
 const error      = require('./error');
 
 module.exports = {
 
-	exec: async function(cmd, options = {}) {
+	exec: async (cmd, options = {}) => {
 		logger.debug('CMD:', cmd);
 
 		const { stdout, stderr } = await new Promise((resolve, reject) => {
@@ -31,11 +31,11 @@ module.exports = {
 	 * @param   {Array}  args
 	 * @returns {Promise}
 	 */
-	execFile: function (cmd, args) {
+	execFile: (cmd, args) => {
 		// logger.debug('CMD: ' + cmd + ' ' + (args ? args.join(' ') : ''));
 
 		return new Promise((resolve, reject) => {
-			execFile(cmd, args, function (err, stdout, /*stderr*/) {
+			execFile(cmd, args, (err, stdout, /*stderr*/) => {
 				if (err && typeof err === 'object') {
 					reject(err);
 				} else {
@@ -51,7 +51,7 @@ module.exports = {
 	 * @param   {Array}  omissions
 	 * @returns {Function}
 	 */
-	omitRow: function (omissions) {
+	omitRow: (omissions) => {
 		/**
 		 * @param   {Object} row
 		 * @returns {Object}
@@ -67,7 +67,7 @@ module.exports = {
 	 * @param   {Array}  omissions
 	 * @returns {Function}
 	 */
-	omitRows: function (omissions) {
+	omitRows: (omissions) => {
 		/**
 		 * @param   {Array} rows
 		 * @returns {Object}
@@ -83,9 +83,9 @@ module.exports = {
 	/**
 	 * @returns {Object} Liquid render engine
 	 */
-	getRenderEngine: function () {
+	getRenderEngine: () => {
 		const renderEngine = new Liquid({
-			root: __dirname + '/../templates/'
+			root: `${__dirname}/../templates/`
 		});
 
 		/**

+ 15 - 12
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh

@@ -27,15 +27,18 @@ chown -R "$PUID:$PGID" /etc/nginx/conf.d
 CERT_INIT_FLAG="/opt/certbot/.ownership_initialized"
 
 if [ ! -f "$CERT_INIT_FLAG" ]; then
-    # Prevents errors when installing python certbot plugins when non-root
-    chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin
-
-    # Handle all site-packages directories efficiently
-    find /opt/certbot/lib -type d -name "site-packages" | while read -r SITE_PACKAGES_DIR; do
-        chown -R "$PUID:$PGID" "$SITE_PACKAGES_DIR"
-    done
-
-    # Create a flag file to skip this step on subsequent runs
-    touch "$CERT_INIT_FLAG"
-    chown "$PUID:$PGID" "$CERT_INIT_FLAG"
-fi
+	# Prevents errors when installing python certbot plugins when non-root
+	if [ "$SKIP_CERTBOT_OWNERSHIP" != "true" ]; then
+		log_info 'Changing ownership of /opt/certbot directories ...'
+		chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin
+	fi
+
+	# Handle all site-packages directories efficiently
+	find /opt/certbot/lib -type d -name "site-packages" | while read -r SITE_PACKAGES_DIR; do
+		chown -R "$PUID:$PGID" "$SITE_PACKAGES_DIR"
+	done
+
+	# Create a flag file to skip this step on subsequent runs
+	touch "$CERT_INIT_FLAG"
+	chown "$PUID:$PGID" "$CERT_INIT_FLAG"
+fi

+ 1 - 1
docker/scripts/install-s6

@@ -8,7 +8,7 @@ BLUE='\E[1;34m'
 GREEN='\E[1;32m'
 RESET='\E[0m'
 
-S6_OVERLAY_VERSION=3.2.0.2
+S6_OVERLAY_VERSION=3.2.1.0
 TARGETPLATFORM=${1:-linux/amd64}
 
 # Determine the correct binary file for the architecture given

+ 9 - 9
global/certbot-dns-plugins.json

@@ -56,19 +56,19 @@
 		"full_plugin_name": "dns-bunny"
 	},
 	"cdmon": {
-                "name": "cdmon",
-                "package_name": "certbot-dns-cdmon",
-                "version": "~=0.4.1",
-                "dependencies": "",
-                "credentials": "dns_cdmon_api_key=your-cdmon-api-token\ndns_cdmon_domain=your_domain_is_optional",
-                "full_plugin_name": "dns-cdmon"
-        },
+		"name": "cdmon",
+		"package_name": "certbot-dns-cdmon",
+		"version": "~=0.4.1",
+		"dependencies": "",
+		"credentials": "dns_cdmon_api_key=your-cdmon-api-token\ndns_cdmon_domain=your_domain_is_optional",
+		"full_plugin_name": "dns-cdmon"
+	},
 	"cloudflare": {
 		"name": "Cloudflare",
 		"package_name": "certbot-dns-cloudflare",
 		"version": "=={{certbot-version}}",
-		"dependencies": "cloudflare==4.0.* acme=={{certbot-version}}",
-		"credentials": "# Cloudflare API credentials used by Certbot\ndns_cloudflare_email = [email protected]\ndns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234",
+		"dependencies": "acme=={{certbot-version}}",
+		"credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
 		"full_plugin_name": "dns-cloudflare"
 	},
 	"cloudns": {

+ 3 - 3
test/cypress/e2e/api/Certificates.cy.js

@@ -10,7 +10,7 @@ describe('Certificates endpoints', () => {
 		});
 	});
 
-	it('Validate custom certificate', function() {
+	it('Validate custom certificate', () => {
 		cy.task('backendApiPostFiles', {
 			token: token,
 			path:  '/api/nginx/certificates/validate',
@@ -25,7 +25,7 @@ describe('Certificates endpoints', () => {
 		});
 	});
 
-	it('Custom certificate lifecycle', function() {
+	it('Custom certificate lifecycle', () => {
 		// Create custom cert
 		cy.task('backendApiPost', {
 			token: token,
@@ -73,7 +73,7 @@ describe('Certificates endpoints', () => {
 		});
 	});
 
-	it('Request Certificate - CVE-2024-46256/CVE-2024-46257', function() {
+	it('Request Certificate - CVE-2024-46256/CVE-2024-46257', () => {
 		cy.task('backendApiPost', {
 			token: token,
 			path:  '/api/nginx/certificates',

+ 1 - 1
test/cypress/e2e/api/Dashboard.cy.js

@@ -9,7 +9,7 @@ describe('Dashboard endpoints', () => {
 		});
 	});
 
-	it('Should be able to get host counts', function() {
+	it('Should be able to get host counts', () => {
 		cy.task('backendApiGet', {
 			token: token,
 			path:  '/api/reports/hosts'

+ 2 - 2
test/cypress/e2e/api/FullCertProvision.cy.js

@@ -9,7 +9,7 @@ describe('Full Certificate Provisions', () => {
 		});
 	});
 
-	it('Should be able to create new http certificate', function() {
+	it('Should be able to create new http certificate', () => {
 		cy.task('backendApiPost', {
 			token: token,
 			path:  '/api/nginx/certificates',
@@ -32,7 +32,7 @@ describe('Full Certificate Provisions', () => {
 		});
 	});
 
-	it('Should be able to create new DNS certificate with Powerdns', function() {
+	it('Should be able to create new DNS certificate with Powerdns', () => {
 		cy.task('backendApiPost', {
 			token: token,
 			path:  '/api/nginx/certificates',

+ 3 - 3
test/cypress/e2e/api/Health.cy.js

@@ -1,7 +1,7 @@
 /// <reference types="cypress" />
 
 describe('Basic API checks', () => {
-	it('Should return a valid health payload', function () {
+	it('Should return a valid health payload', () => {
 		cy.task('backendApiGet', {
 			path: '/api/',
 		}).then((data) => {
@@ -10,9 +10,9 @@ describe('Basic API checks', () => {
 		});
 	});
 
-	it('Should return a valid schema payload', function () {
+	it('Should return a valid schema payload', () => {
 		cy.task('backendApiGet', {
-			path: '/api/schema?ts=' + Date.now(),
+			path: `/api/schema?ts=${Date.now()}`,
 		}).then((data) => {
 			expect(data.openapi).to.be.equal('3.1.0');
 		});

+ 3 - 3
test/cypress/e2e/api/Ldap.cy.js

@@ -1,12 +1,12 @@
 /// <reference types="cypress" />
 
 describe('LDAP with Authentik', () => {
-	let token;
+	let _token;
 	if (Cypress.env('skipStackCheck') === 'true' || Cypress.env('stack') === 'postgres') {
 
 		before(() => {
 			cy.getToken().then((tok) => {
-				token = tok;
+				_token = tok;
 
 				// cy.task('backendApiPut', {
 				// 	token: token,
@@ -45,7 +45,7 @@ describe('LDAP with Authentik', () => {
 			});
 		});
 
-		it.skip('Should log in with LDAP', function() {
+		it.skip('Should log in with LDAP', () => {
 			// cy.task('backendApiPost', {
 			// 	token: token,
 			// 	path:  '/api/auth',

+ 3 - 3
test/cypress/e2e/api/OAuth.cy.js

@@ -1,12 +1,12 @@
 /// <reference types="cypress" />
 
 describe('OAuth with Authentik', () => {
-	let token;
+	let _token;
 	if (Cypress.env('skipStackCheck') === 'true' || Cypress.env('stack') === 'postgres') {
 
 		before(() => {
 			cy.getToken().then((tok) => {
-				token = tok;
+				_token = tok;
 
 				// cy.task('backendApiPut', {
 				// 	token: token,
@@ -47,7 +47,7 @@ describe('OAuth with Authentik', () => {
 			});
 		});
 
-		it.skip('Should log in with OAuth', function() {
+		it.skip('Should log in with OAuth', () => {
 			// cy.task('backendApiGet', {
 			// 	path:  '/oauth/login?redirect_base=' + encodeURI(Cypress.config('baseUrl')),
 			// }).then((data) => {

+ 1 - 1
test/cypress/e2e/api/ProxyHosts.cy.js

@@ -9,7 +9,7 @@ describe('Proxy Hosts endpoints', () => {
 		});
 	});
 
-	it('Should be able to create a http host', function() {
+	it('Should be able to create a http host', () => {
 		cy.task('backendApiPost', {
 			token: token,
 			path:  '/api/nginx/proxy-hosts',

+ 7 - 7
test/cypress/e2e/api/Settings.cy.js

@@ -9,7 +9,7 @@ describe('Settings endpoints', () => {
 		});
 	});
 
-	it('Get all settings', function() {
+	it('Get all settings', () => {
 		cy.task('backendApiGet', {
 			token: token,
 			path:  '/api/settings',
@@ -19,7 +19,7 @@ describe('Settings endpoints', () => {
 		});
 	});
 
-	it('Get default-site setting', function() {
+	it('Get default-site setting', () => {
 		cy.task('backendApiGet', {
 			token: token,
 			path:  '/api/settings/default-site',
@@ -30,7 +30,7 @@ describe('Settings endpoints', () => {
 		});
 	});
 
-	it('Default Site congratulations', function() {
+	it('Default Site congratulations', () => {
 		cy.task('backendApiPut', {
 			token: token,
 			path:  '/api/settings/default-site',
@@ -46,7 +46,7 @@ describe('Settings endpoints', () => {
 		});
 	});
 
-	it('Default Site 404', function() {
+	it('Default Site 404', () => {
 		cy.task('backendApiPut', {
 			token: token,
 			path:  '/api/settings/default-site',
@@ -62,7 +62,7 @@ describe('Settings endpoints', () => {
 		});
 	});
 
-	it('Default Site 444', function() {
+	it('Default Site 444', () => {
 		cy.task('backendApiPut', {
 			token: token,
 			path:  '/api/settings/default-site',
@@ -78,7 +78,7 @@ describe('Settings endpoints', () => {
 		});
 	});
 
-	it('Default Site redirect', function() {
+	it('Default Site redirect', () => {
 		cy.task('backendApiPut', {
 			token: token,
 			path:  '/api/settings/default-site',
@@ -100,7 +100,7 @@ describe('Settings endpoints', () => {
 		});
 	});
 
-	it('Default Site html', function() {
+	it('Default Site html', () => {
 		cy.task('backendApiPut', {
 			token: token,
 			path:  '/api/settings/default-site',

+ 6 - 6
test/cypress/e2e/api/Streams.cy.js

@@ -33,7 +33,7 @@ describe('Streams', () => {
 		cy.exec('rm -f /test/results/testssl.json');
 	});
 
-	it('Should be able to create TCP Stream', function() {
+	it('Should be able to create TCP Stream', () => {
 		cy.task('backendApiPost', {
 			token: token,
 			path:  '/api/nginx/streams',
@@ -65,7 +65,7 @@ describe('Streams', () => {
 		});
 	});
 
-	it('Should be able to create UDP Stream', function() {
+	it('Should be able to create UDP Stream', () => {
 		cy.task('backendApiPost', {
 			token: token,
 			path:  '/api/nginx/streams',
@@ -92,7 +92,7 @@ describe('Streams', () => {
 		});
 	});
 
-	it('Should be able to create TCP/UDP Stream', function() {
+	it('Should be able to create TCP/UDP Stream', () => {
 		cy.task('backendApiPost', {
 			token: token,
 			path:  '/api/nginx/streams',
@@ -124,7 +124,7 @@ describe('Streams', () => {
 		});
 	});
 
-	it('Should be able to create SSL TCP Stream', function() {
+	it('Should be able to create SSL TCP Stream', () => {
 		let certID = 0;
 
 		// Create custom cert
@@ -184,7 +184,7 @@ describe('Streams', () => {
 					cy.exec('/testssl/testssl.sh --quiet --add-ca="$(/bin/mkcert -CAROOT)/rootCA.pem" --jsonfile=/test/results/testssl.json website1.example.com:1503', {
 						timeout: 120000, // 2 minutes
 					}).then((result) => {
-						cy.task('log', '[testssl.sh] ' + result.stdout);
+						cy.task('log', `[testssl.sh] ${result.stdout}`);
 
 						const allowedSeverities = ["INFO", "OK", "LOW", "MEDIUM"];
 						const ignoredIDs = [
@@ -210,7 +210,7 @@ describe('Streams', () => {
 		});
 	});
 
-	it('Should be able to List Streams', function() {
+	it('Should be able to List Streams', () => {
 		cy.task('backendApiGet', {
 			token: token,
 			path:  '/api/nginx/streams?expand=owner,certificate',

+ 3 - 3
test/cypress/e2e/api/Users.cy.js

@@ -9,7 +9,7 @@ describe('Users endpoints', () => {
 		});
 	});
 
-	it('Should be able to get yourself', function() {
+	it('Should be able to get yourself', () => {
 		cy.task('backendApiGet', {
 			token: token,
 			path:  '/api/users/me'
@@ -20,7 +20,7 @@ describe('Users endpoints', () => {
 		});
 	});
 
-	it('Should be able to get all users', function() {
+	it('Should be able to get all users', () => {
 		cy.task('backendApiGet', {
 			token: token,
 			path:  '/api/users'
@@ -30,7 +30,7 @@ describe('Users endpoints', () => {
 		});
 	});
 
-	it('Should be able to update yourself', function() {
+	it('Should be able to update yourself', () => {
 		cy.task('backendApiPut', {
 			token: token,
 			path:  '/api/users/me',