diff --git a/.version b/.version index 56beced9a..e4643748f 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.12.4 +2.12.6 diff --git a/Jenkinsfile b/Jenkinsfile index 66ed7cb6a..af913c2e0 100644 --- a/Jenkinsfile +++ b/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) } } diff --git a/README.md b/README.md index 905e83cfb..2116a55ae 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
-
+
diff --git a/backend/lib/certbot.js b/backend/lib/certbot.js
index eb1966dc7..96d947102 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,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;
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/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 17bfa1a95..fa9465189 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
@@ -8,34 +8,53 @@ log_info 'Setting ownership ...'
# root
chown root /tmp/nginx
-# npm user and group
-chown -R "$PUID:$PGID" /data
-chown -R "$PUID:$PGID" /etc/letsencrypt
-chown -R "$PUID:$PGID" /run/nginx
-chown -R "$PUID:$PGID" /tmp/nginx
-chown -R "$PUID:$PGID" /var/cache/nginx
-chown -R "$PUID:$PGID" /var/lib/logrotate
-chown -R "$PUID:$PGID" /var/lib/nginx
-chown -R "$PUID:$PGID" /var/log/nginx
-
-# Don't chown entire /etc/nginx folder as this causes crashes on some systems
-chown -R "$PUID:$PGID" /etc/nginx/nginx
-chown -R "$PUID:$PGID" /etc/nginx/nginx.conf
-chown -R "$PUID:$PGID" /etc/nginx/conf.d
-
-# Certbot directories - optimized approach
-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
\ No newline at end of file
+locations=(
+ "/data"
+ "/etc/letsencrypt"
+ "/run/nginx"
+ "/tmp/nginx"
+ "/var/cache/nginx"
+ "/var/lib/logrotate"
+ "/var/lib/nginx"
+ "/var/log/nginx"
+ "/etc/nginx/nginx"
+ "/etc/nginx/nginx.conf"
+ "/etc/nginx/conf.d"
+)
+
+chownit() {
+ local dir="$1"
+ local recursive="${2:-true}"
+
+ local have
+ have="$(stat -c '%u:%g' "$dir")"
+ echo "- $dir ... "
+
+ if [ "$have" != "$PUID:$PGID" ]; then
+ if [ "$recursive" = 'true' ] && [ -d "$dir" ]; then
+ chown -R "$PUID:$PGID" "$dir"
+ else
+ chown "$PUID:$PGID" "$dir"
+ fi
+ echo " DONE"
+ else
+ echo " SKIPPED"
+ fi
+}
+
+for loc in "${locations[@]}"; do
+ chownit "$loc"
+done
+
+if [ "$(is_true "${SKIP_CERTBOT_OWNERSHIP:-}")" = '1' ]; then
+ log_info 'Skipping ownership change of certbot directories'
+else
+ log_info 'Changing ownership of certbot directories, this may take some time ...'
+ chownit "/opt/certbot" false
+ chownit "/opt/certbot/bin" false
+
+ # Handle all site-packages directories efficiently
+ find /opt/certbot/lib -type d -name "site-packages" | while read -r SITE_PACKAGES_DIR; do
+ chownit "$SITE_PACKAGES_DIR"
+ done
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
index 0cb9f1264..e02f41ca1 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
@@ -5,12 +5,9 @@ set -e
log_info 'Dynamic resolvers ...'
-DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
-
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
# thanks @tfmm
-if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ];
-then
+if [ "$(is_true "$DISABLE_IPV6")" = '1' ]; then
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
else
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
index 0c4d261ce..2ae61ae55 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
@@ -8,14 +8,11 @@ set -e
log_info 'IPv6 ...'
-# Lowercase
-DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
-
process_folder () {
FILES=$(find "$1" -type f -name "*.conf")
SED_REGEX=
- if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
+ if [ "$(is_true "$DISABLE_IPV6")" = '1' ]; then
# IPV6 is disabled
echo "Disabling IPV6 in hosts in: $1"
SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g'
diff --git a/docker/rootfs/usr/bin/common.sh b/docker/rootfs/usr/bin/common.sh
index 13cf06acd..46529870a 100644
--- a/docker/rootfs/usr/bin/common.sh
+++ b/docker/rootfs/usr/bin/common.sh
@@ -56,3 +56,13 @@ get_group_id () {
getent group "$1" | cut -d: -f3
fi
}
+
+# param $1: value
+is_true () {
+ VAL=$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')
+ if [ "$VAL" == 'true' ] || [ "$VAL" == 'on' ] || [ "$VAL" == '1' ] || [ "$VAL" == 'yes' ]; then
+ echo '1'
+ else
+ echo '0'
+ fi
+}
diff --git a/docker/scripts/install-s6 b/docker/scripts/install-s6
index 5f3b73ec5..639c65dd6 100755
--- a/docker/scripts/install-s6
+++ b/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
diff --git a/global/certbot-dns-plugins.json b/global/certbot-dns-plugins.json
index 65e45f3af..9f8788096 100644
--- a/global/certbot-dns-plugins.json
+++ b/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 = cloudflare@example.com\ndns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234",
+ "dependencies": "acme=={{certbot-version}}",
+ "credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
"full_plugin_name": "dns-cloudflare"
},
"cloudns": {
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 @@
///