Skip to content

v2.12.5 #4651

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 9, 2025
Merged

v2.12.5 #4651

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.12.4
2.12.5
15 changes: 10 additions & 5 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
17 changes: 12 additions & 5 deletions backend/lib/certbot.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const certbot = {
/**
* @param {array} pluginKeys
*/
installPlugins: async function (pluginKeys) {
installPlugins: async (pluginKeys) => {
let hasErrors = false;

return new Promise((resolve, reject) => {
Expand All @@ -21,7 +21,7 @@ const certbot = {
}

batchflow(pluginKeys).sequential()
.each((i, pluginKey, next) => {
.each((_i, pluginKey, next) => {
certbot.installPlugin(pluginKey)
.then(() => {
next();
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
18 changes: 9 additions & 9 deletions backend/lib/utils.js
Original file line number Diff line number Diff line change
@@ -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) => {
Expand All @@ -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 {
Expand All @@ -51,7 +51,7 @@ module.exports = {
* @param {Array} omissions
* @returns {Function}
*/
omitRow: function (omissions) {
omitRow: (omissions) => {
/**
* @param {Object} row
* @returns {Object}
Expand All @@ -67,7 +67,7 @@ module.exports = {
* @param {Array} omissions
* @returns {Function}
*/
omitRows: function (omissions) {
omitRows: (omissions) => {
/**
* @param {Array} rows
* @returns {Object}
Expand All @@ -83,9 +83,9 @@ module.exports = {
/**
* @returns {Object} Liquid render engine
*/
getRenderEngine: function () {
getRenderEngine: () => {
const renderEngine = new Liquid({
root: __dirname + '/../templates/'
root: `${__dirname}/../templates/`
});

/**
Expand Down
27 changes: 15 additions & 12 deletions docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion docker/scripts/install-s6
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 9 additions & 9 deletions global/certbot-dns-plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
6 changes: 3 additions & 3 deletions test/cypress/e2e/api/Certificates.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -25,7 +25,7 @@ describe('Certificates endpoints', () => {
});
});

it('Custom certificate lifecycle', function() {
it('Custom certificate lifecycle', () => {
// Create custom cert
cy.task('backendApiPost', {
token: token,
Expand Down Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion test/cypress/e2e/api/Dashboard.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
4 changes: 2 additions & 2 deletions test/cypress/e2e/api/FullCertProvision.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand Down
6 changes: 3 additions & 3 deletions test/cypress/e2e/api/Health.cy.js
Original file line number Diff line number Diff line change
@@ -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) => {
Expand All @@ -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');
});
Expand Down
6 changes: 3 additions & 3 deletions test/cypress/e2e/api/Ldap.cy.js
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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',
Expand Down
6 changes: 3 additions & 3 deletions test/cypress/e2e/api/OAuth.cy.js
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion test/cypress/e2e/api/ProxyHosts.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading