From ed1d90ee7ff521dee0088e952ce37b3c394be329 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 9 Jul 2025 11:34:19 +1000 Subject: [PATCH 1/5] Fix powerdns dns plugin install, deps are outrageously old ;( --- backend/lib/certbot.js | 16 +++++++++++----- global/certbot-dns-plugins.json | 5 ++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/lib/certbot.js b/backend/lib/certbot.js index eb1966dc7..0f4918835 100644 --- a/backend/lib/certbot.js +++ b/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,14 @@ 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) + const opts = {}; + if (typeof plugin.env === 'object') { + // include process.env in opts + opts.env = Object.assign({}, process.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, opts) .then((result) => { logger.complete(`Installed ${pluginKey}`); return result; diff --git a/global/certbot-dns-plugins.json b/global/certbot-dns-plugins.json index 9f8788096..87e1de607 100644 --- a/global/certbot-dns-plugins.json +++ b/global/certbot-dns-plugins.json @@ -477,7 +477,10 @@ "version": "~=0.2.1", "dependencies": "PyYAML==5.3.1", "credentials": "dns_powerdns_api_url = https://api.mypowerdns.example.org\ndns_powerdns_api_key = AbCbASsd!@34", - "full_plugin_name": "dns-powerdns" + "full_plugin_name": "dns-powerdns", + "env": { + "SETUPTOOLS_USE_DISTUTILS": "stdlib" + } }, "regru": { "name": "reg.ru", From c97b8a339d7610677de7e7dda4a9b692d376d13b Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 9 Jul 2025 11:34:57 +1000 Subject: [PATCH 2/5] Some auto formatting changes suggested by ide --- backend/lib/utils.js | 18 +++++++++--------- test/cypress/e2e/api/Certificates.cy.js | 6 +++--- test/cypress/e2e/api/Dashboard.cy.js | 2 +- test/cypress/e2e/api/FullCertProvision.cy.js | 4 ++-- test/cypress/e2e/api/Health.cy.js | 6 +++--- test/cypress/e2e/api/Ldap.cy.js | 6 +++--- test/cypress/e2e/api/OAuth.cy.js | 6 +++--- test/cypress/e2e/api/ProxyHosts.cy.js | 2 +- test/cypress/e2e/api/Settings.cy.js | 14 +++++++------- test/cypress/e2e/api/Streams.cy.js | 12 ++++++------ test/cypress/e2e/api/Users.cy.js | 6 +++--- 11 files changed, 41 insertions(+), 41 deletions(-) diff --git a/backend/lib/utils.js b/backend/lib/utils.js index bcdb3341c..66f2dfd95 100644 --- a/backend/lib/utils.js +++ b/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/` }); /** diff --git a/test/cypress/e2e/api/Certificates.cy.js b/test/cypress/e2e/api/Certificates.cy.js index 1e8a6fed4..9f47edcbf 100644 --- a/test/cypress/e2e/api/Certificates.cy.js +++ b/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', diff --git a/test/cypress/e2e/api/Dashboard.cy.js b/test/cypress/e2e/api/Dashboard.cy.js index 8fbb97155..62cb40e44 100644 --- a/test/cypress/e2e/api/Dashboard.cy.js +++ b/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' diff --git a/test/cypress/e2e/api/FullCertProvision.cy.js b/test/cypress/e2e/api/FullCertProvision.cy.js index 5ca5692cd..9c6a7d2d2 100644 --- a/test/cypress/e2e/api/FullCertProvision.cy.js +++ b/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', diff --git a/test/cypress/e2e/api/Health.cy.js b/test/cypress/e2e/api/Health.cy.js index 49881e97b..d3e3306d4 100644 --- a/test/cypress/e2e/api/Health.cy.js +++ b/test/cypress/e2e/api/Health.cy.js @@ -1,7 +1,7 @@ /// 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'); }); diff --git a/test/cypress/e2e/api/Ldap.cy.js b/test/cypress/e2e/api/Ldap.cy.js index 6b7e5f76c..715c793be 100644 --- a/test/cypress/e2e/api/Ldap.cy.js +++ b/test/cypress/e2e/api/Ldap.cy.js @@ -1,12 +1,12 @@ /// 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', diff --git a/test/cypress/e2e/api/OAuth.cy.js b/test/cypress/e2e/api/OAuth.cy.js index 044bb2757..c5c819f9b 100644 --- a/test/cypress/e2e/api/OAuth.cy.js +++ b/test/cypress/e2e/api/OAuth.cy.js @@ -1,12 +1,12 @@ /// 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) => { diff --git a/test/cypress/e2e/api/ProxyHosts.cy.js b/test/cypress/e2e/api/ProxyHosts.cy.js index 5bc645800..476945e79 100644 --- a/test/cypress/e2e/api/ProxyHosts.cy.js +++ b/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', diff --git a/test/cypress/e2e/api/Settings.cy.js b/test/cypress/e2e/api/Settings.cy.js index 6942760c7..fcaa0628e 100644 --- a/test/cypress/e2e/api/Settings.cy.js +++ b/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', diff --git a/test/cypress/e2e/api/Streams.cy.js b/test/cypress/e2e/api/Streams.cy.js index fce04101b..e38f9dba8 100644 --- a/test/cypress/e2e/api/Streams.cy.js +++ b/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', diff --git a/test/cypress/e2e/api/Users.cy.js b/test/cypress/e2e/api/Users.cy.js index 06b183176..7307f87d0 100644 --- a/test/cypress/e2e/api/Users.cy.js +++ b/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', From 5f54490d86fa79f2abccbdbcee3e01f656525959 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 9 Jul 2025 12:35:20 +1000 Subject: [PATCH 3/5] Set SETUPTOOLS_USE_DISTUTILS for all plugin installs, seems like they all need it. --- backend/lib/certbot.js | 9 +++++---- global/certbot-dns-plugins.json | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/backend/lib/certbot.js b/backend/lib/certbot.js index 0f4918835..96d947102 100644 --- a/backend/lib/certbot.js +++ b/backend/lib/certbot.js @@ -63,14 +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 opts = {}; + // 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') { - // include process.env in opts - opts.env = Object.assign({}, process.env, plugin.env); + 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, opts) + return utils.exec(cmd, {env}) .then((result) => { logger.complete(`Installed ${pluginKey}`); return result; diff --git a/global/certbot-dns-plugins.json b/global/certbot-dns-plugins.json index 87e1de607..9f8788096 100644 --- a/global/certbot-dns-plugins.json +++ b/global/certbot-dns-plugins.json @@ -477,10 +477,7 @@ "version": "~=0.2.1", "dependencies": "PyYAML==5.3.1", "credentials": "dns_powerdns_api_url = https://api.mypowerdns.example.org\ndns_powerdns_api_key = AbCbASsd!@34", - "full_plugin_name": "dns-powerdns", - "env": { - "SETUPTOOLS_USE_DISTUTILS": "stdlib" - } + "full_plugin_name": "dns-powerdns" }, "regru": { "name": "reg.ru", From 1357774f219d9617455731a23e3642910821de17 Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 9 Jul 2025 13:14:27 +1000 Subject: [PATCH 4/5] Add SKIP_CERTBOT_OWNERSHIP env var support to skip certbot folder ownership --- docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 index 5632b91e0..1ec117e1b 100755 --- 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 @@ -28,7 +28,10 @@ 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 + 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 From 1710a263c06c69ee51242a10f55221a247508c9c Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 9 Jul 2025 13:15:15 +1000 Subject: [PATCH 5/5] Bump version --- .version | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.version b/.version index 56beced9a..dcb27a75e 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.12.4 +2.12.5 diff --git a/README.md b/README.md index 905e83cfb..3f7c21781 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@



- +