Ver código fonte

Merge pull request #692 from jc21/develop

v2.6.2
jc21 5 anos atrás
pai
commit
cf8812c932

+ 1 - 1
.version

@@ -1 +1 @@
-2.6.1
+2.6.2

+ 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.6.1-green.svg?style=for-the-badge">
+	<img src="https://img.shields.io/badge/version-2.6.2-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>

+ 28 - 37
backend/internal/certificate.js

@@ -608,12 +608,12 @@ const internalCertificate = {
 	checkPrivateKey: (private_key) => {
 		return tempWrite(private_key, '/tmp')
 			.then((filepath) => {
-				return utils.exec('openssl rsa -in ' + filepath + ' -check -noout')
+				let key_type = private_key.includes('-----BEGIN RSA') ? 'rsa' : 'ec';
+				return utils.exec('openssl ' + key_type + ' -in ' + filepath + ' -check -noout 2>&1 ')
 					.then((result) => {
-						if (!result.toLowerCase().includes('key ok')) {
-							throw new error.ValidationError(result);
+						if (!result.toLowerCase().includes('key ok') && !result.toLowerCase().includes('key valid') ) {
+							throw new error.ValidationError('Result Validation Error: ' + result);
 						}
-
 						fs.unlinkSync(filepath);
 						return true;
 					}).catch((err) => {
@@ -788,9 +788,9 @@ const internalCertificate = {
 
 		logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
 
-		const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
-		const credentials_cmd = 'echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
-		const prepare_cmd     = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
+		const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
+		const credentials_cmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
+		const prepare_cmd     = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version + ' ' + dns_plugin.dependencies;
 
 		// Whether the plugin has a --<name>-credentials argument
 		const has_config_arg = certificate.meta.dns_provider !== 'route53';
@@ -818,11 +818,9 @@ const internalCertificate = {
 		if (certificate.meta.dns_provider === 'route53') {
 			main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
 		}
-		
-		const teardown_cmd = `rm '${credentials_loc}'`;
 
 		if (debug_mode) {
-			logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
+			logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd}`);
 		}
 
 		return utils.exec(credentials_cmd)
@@ -831,11 +829,15 @@ const internalCertificate = {
 					.then(() => {
 						return utils.exec(main_cmd)
 							.then(async (result) => {
-								await utils.exec(teardown_cmd);
 								logger.info(result);
 								return result;
 							});
 					});
+			}).catch(async (err) => {
+				// Don't fail if file does not exist
+				const delete_credentials_cmd = `rm -f '${credentials_loc}' || true`;
+				await utils.exec(delete_credentials_cmd);
+				throw err;
 			});
 	},
 
@@ -922,10 +924,6 @@ const internalCertificate = {
 
 		logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
 
-		const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
-		const credentials_cmd = 'echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
-		const prepare_cmd     = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
-
 		let main_cmd = 
 			certbot_command + ' renew --non-interactive ' +
 			'--cert-name "npm-' + certificate.id + '" ' +
@@ -934,26 +932,18 @@ const internalCertificate = {
 
 		// Prepend the path to the credentials file as an environment variable
 		if (certificate.meta.dns_provider === 'route53') {
-			main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
+			const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
+			main_cmd              = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
 		}
 
-		const teardown_cmd = `rm '${credentials_loc}'`;
-
 		if (debug_mode) {
-			logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
+			logger.info('Command:', main_cmd);
 		}
 
-		return utils.exec(credentials_cmd)
-			.then(() => {
-				return utils.exec(prepare_cmd)
-					.then(() => {
-						return utils.exec(main_cmd)
-							.then(async (result) => {
-								await utils.exec(teardown_cmd);
-								logger.info(result);
-								return result;
-							});
-					});
+		return utils.exec(main_cmd)
+			.then(async (result) => {
+				logger.info(result);
+				return result;
 			});
 	},
 
@@ -965,20 +955,21 @@ const internalCertificate = {
 	revokeLetsEncryptSsl: (certificate, throw_errors) => {
 		logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
 
-		let cmd = certbot_command + ' revoke --non-interactive ' +
+		const main_cmd = certbot_command + ' revoke --non-interactive ' +
 			'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
 			'--delete-after-revoke ' +
 			(le_staging ? '--staging' : '');
 
+		// Don't fail command if file does not exist
+		const delete_credentials_cmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
+
 		if (debug_mode) {
-			logger.info('Command:', cmd);
+			logger.info('Command:', main_cmd + '; ' + delete_credentials_cmd);
 		}
 
-		return utils.exec(cmd)
-			.then((result) => {
-				if (debug_mode) {
-					logger.info('Command:', cmd);
-				}
+		return utils.exec(main_cmd)
+			.then(async (result) => {
+				await utils.exec(delete_credentials_cmd);
 				logger.info(result);
 				return result;
 			})

+ 49 - 1
backend/setup.js

@@ -2,10 +2,13 @@ const fs                  = require('fs');
 const NodeRSA             = require('node-rsa');
 const config              = require('config');
 const logger              = require('./logger').setup;
+const certificateModel    = require('./models/certificate');
 const userModel           = require('./models/user');
 const userPermissionModel = require('./models/user_permission');
+const utils               = require('./lib/utils');
 const authModel           = require('./models/auth');
 const settingModel        = require('./models/setting');
+const dns_plugins         = require('./global/certbot-dns-plugins');
 const debug_mode          = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
 
 /**
@@ -155,8 +158,53 @@ const setupDefaultSettings = () => {
 		});
 };
 
+/**
+ * Installs all Certbot plugins which are required for an installed certificate
+ *
+ * @returns {Promise}
+ */
+const setupCertbotPlugins = () => {
+	return certificateModel
+		.query()
+		.where('is_deleted', 0)
+		.andWhere('provider', 'letsencrypt')
+		.then((certificates) => {
+			if (certificates && certificates.length) {
+				let plugins  = [];
+				let promises = [];
+
+				certificates.map(function (certificate) {
+					if (certificate.meta && certificate.meta.dns_challenge === true) {
+						const dns_plugin          = dns_plugins[certificate.meta.dns_provider];
+						const packages_to_install = `${dns_plugin.package_name}==${dns_plugin.package_version} ${dns_plugin.dependencies}`;
+
+						if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
+
+						// Make sure credentials file exists
+						const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; 
+						const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }';
+						promises.push(utils.exec(credentials_cmd));
+					}
+				});
+
+				if (plugins.length) {
+					const install_cmd = 'pip3 install ' + plugins.join(' ');
+					promises.push(utils.exec(install_cmd));
+				}
+
+				if (promises.length) {
+					return Promise.all(promises)
+						.then(() => { 
+							logger.info('Added Certbot plugins ' + plugins.join(', ')); 
+						});
+				}
+			}
+		});
+};
+
 module.exports = function () {
 	return setupJwt()
 		.then(setupDefaultUser)
-		.then(setupDefaultSettings);
+		.then(setupDefaultSettings)
+		.then(setupCertbotPlugins);
 };

+ 2 - 196
docker/rootfs/etc/nginx/conf.d/include/ip_ranges.conf

@@ -1,196 +1,2 @@
-
-set_real_ip_from 144.220.0.0/16;
-
-set_real_ip_from 52.124.128.0/17;
-
-set_real_ip_from 54.230.0.0/16;
-
-set_real_ip_from 54.239.128.0/18;
-
-set_real_ip_from 52.82.128.0/19;
-
-set_real_ip_from 99.84.0.0/16;
-
-set_real_ip_from 204.246.172.0/24;
-
-set_real_ip_from 205.251.192.0/19;
-
-set_real_ip_from 54.239.192.0/19;
-
-set_real_ip_from 70.132.0.0/18;
-
-set_real_ip_from 13.32.0.0/15;
-
-set_real_ip_from 13.224.0.0/14;
-
-set_real_ip_from 13.35.0.0/16;
-
-set_real_ip_from 204.246.164.0/22;
-
-set_real_ip_from 204.246.168.0/22;
-
-set_real_ip_from 71.152.0.0/17;
-
-set_real_ip_from 216.137.32.0/19;
-
-set_real_ip_from 205.251.249.0/24;
-
-set_real_ip_from 99.86.0.0/16;
-
-set_real_ip_from 52.46.0.0/18;
-
-set_real_ip_from 52.84.0.0/15;
-
-set_real_ip_from 204.246.173.0/24;
-
-set_real_ip_from 130.176.0.0/16;
-
-set_real_ip_from 64.252.64.0/18;
-
-set_real_ip_from 204.246.174.0/23;
-
-set_real_ip_from 64.252.128.0/18;
-
-set_real_ip_from 205.251.254.0/24;
-
-set_real_ip_from 143.204.0.0/16;
-
-set_real_ip_from 205.251.252.0/23;
-
-set_real_ip_from 204.246.176.0/20;
-
-set_real_ip_from 13.249.0.0/16;
-
-set_real_ip_from 54.240.128.0/18;
-
-set_real_ip_from 205.251.250.0/23;
-
-set_real_ip_from 52.222.128.0/17;
-
-set_real_ip_from 54.182.0.0/16;
-
-set_real_ip_from 54.192.0.0/16;
-
-set_real_ip_from 13.124.199.0/24;
-
-set_real_ip_from 34.226.14.0/24;
-
-set_real_ip_from 52.15.127.128/26;
-
-set_real_ip_from 35.158.136.0/24;
-
-set_real_ip_from 52.57.254.0/24;
-
-set_real_ip_from 18.216.170.128/25;
-
-set_real_ip_from 13.52.204.0/23;
-
-set_real_ip_from 13.54.63.128/26;
-
-set_real_ip_from 13.59.250.0/26;
-
-set_real_ip_from 13.210.67.128/26;
-
-set_real_ip_from 35.167.191.128/26;
-
-set_real_ip_from 52.47.139.0/24;
-
-set_real_ip_from 52.199.127.192/26;
-
-set_real_ip_from 52.212.248.0/26;
-
-set_real_ip_from 52.66.194.128/26;
-
-set_real_ip_from 13.113.203.0/24;
-
-set_real_ip_from 99.79.168.0/23;
-
-set_real_ip_from 34.195.252.0/24;
-
-set_real_ip_from 35.162.63.192/26;
-
-set_real_ip_from 34.223.12.224/27;
-
-set_real_ip_from 52.56.127.0/25;
-
-set_real_ip_from 34.223.80.192/26;
-
-set_real_ip_from 13.228.69.0/24;
-
-set_real_ip_from 34.216.51.0/25;
-
-set_real_ip_from 3.231.2.0/25;
-
-set_real_ip_from 54.233.255.128/26;
-
-set_real_ip_from 18.200.212.0/23;
-
-set_real_ip_from 52.52.191.128/26;
-
-set_real_ip_from 3.234.232.224/27;
-
-set_real_ip_from 52.78.247.128/26;
-
-set_real_ip_from 52.220.191.0/26;
-
-set_real_ip_from 34.232.163.208/29;
-
-set_real_ip_from 2600:9000:eee::/48;
-
-set_real_ip_from 2600:9000:4000::/36;
-
-set_real_ip_from 2600:9000:3000::/36;
-
-set_real_ip_from 2600:9000:f000::/36;
-
-set_real_ip_from 2600:9000:fff::/48;
-
-set_real_ip_from 2600:9000:2000::/36;
-
-set_real_ip_from 2600:9000:1000::/36;
-
-set_real_ip_from 2600:9000:ddd::/48;
-
-set_real_ip_from 2600:9000:5300::/40;
-
-set_real_ip_from 173.245.48.0/20;
-
-set_real_ip_from 103.21.244.0/22;
-
-set_real_ip_from 103.22.200.0/22;
-
-set_real_ip_from 103.31.4.0/22;
-
-set_real_ip_from 141.101.64.0/18;
-
-set_real_ip_from 108.162.192.0/18;
-
-set_real_ip_from 190.93.240.0/20;
-
-set_real_ip_from 188.114.96.0/20;
-
-set_real_ip_from 197.234.240.0/22;
-
-set_real_ip_from 198.41.128.0/17;
-
-set_real_ip_from 162.158.0.0/15;
-
-set_real_ip_from 104.16.0.0/12;
-
-set_real_ip_from 172.64.0.0/13;
-
-set_real_ip_from 131.0.72.0/22;
-
-set_real_ip_from 2400:cb00::/32;
-
-set_real_ip_from 2606:4700::/32;
-
-set_real_ip_from 2803:f800::/32;
-
-set_real_ip_from 2405:b500::/32;
-
-set_real_ip_from 2405:8100::/32;
-
-set_real_ip_from 2a06:98c0::/29;
-
-set_real_ip_from 2c0f:f248::/32;
+# This should be left blank is it is populated programatically
+# by the application backend.

+ 2 - 0
docker/rootfs/etc/nginx/conf.d/include/proxy.conf

@@ -3,4 +3,6 @@ proxy_set_header Host $host;
 proxy_set_header X-Forwarded-Scheme $scheme;
 proxy_set_header X-Forwarded-Proto  $scheme;
 proxy_set_header X-Forwarded-For    $remote_addr;
+proxy_set_header X-Real-IP          $remote_addr;
 proxy_pass       $forward_scheme://$server:$port;
+

+ 1 - 1
docker/rootfs/etc/nginx/nginx.conf

@@ -66,7 +66,7 @@ http {
 	# NPM generated CDN ip ranges:
 	include conf.d/include/ip_ranges.conf;
 	# always put the following 2 lines after ip subnets:
-	real_ip_header X-Forwarded-For;
+	real_ip_header X-Real-IP;
 	real_ip_recursive on;
 
 	# Files generated by NPM

+ 1 - 1
frontend/js/i18n/messages.json

@@ -109,7 +109,7 @@
       "please-choose": "Please Choose...",
       "credentials-file-content": "Credentials File Content",
       "credentials-file-content-info": "This plugin requires a configuration file containing an API token or other credentials to your provider",
-      "stored-as-plaintext-info": "This data will be stored as plaintext in the database!",
+      "stored-as-plaintext-info": "This data will be stored as plaintext in the database and in a file!",
       "propagation-seconds": "Propagation Seconds",
       "propagation-seconds-info": "Leave empty to use the plugins default value. Number of seconds to wait for DNS propagation.",
       "processing-info": "Processing... This might take a few minutes."

+ 29 - 5
global/certbot-dns-plugins.js

@@ -10,9 +10,9 @@
  *      display_name: "Name displayed to the user",
  *      package_name: "Package name in PyPi repo",
  *      package_version: "Package version in PyPi repo",
+ *      dependencies: "Additional dependencies, space separated (as you would pass it to pip install)",
  *      credentials: `Template of the credentials file`,
  *      full_plugin_name: "The full plugin name as used in the commandline with certbot, including prefixes, e.g. 'certbot-dns-njalla:dns-njalla'",
- *      credentials_file: Whether the plugin has a credentials file
  *    },
  *    ...
  *  }
@@ -24,6 +24,7 @@ module.exports = {
 		display_name:    'Cloudflare',
 		package_name:    'certbot-dns-cloudflare',
 		package_version: '1.8.0',
+		dependencies:    'cloudflare',
 		credentials:     `# Cloudflare API token
 dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`,
 		full_plugin_name: 'dns-cloudflare',
@@ -33,6 +34,7 @@ dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`,
 		display_name:    'CloudXNS',
 		package_name:    'certbot-dns-cloudxns',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef
 dns_cloudxns_secret_key = 1122334455667788`,
 		full_plugin_name: 'dns-cloudxns',
@@ -42,6 +44,7 @@ dns_cloudxns_secret_key = 1122334455667788`,
 		display_name:    'Core Networks',
 		package_name:    'certbot-dns-corenetworks',
 		package_version: '0.1.4',
+		dependencies:    '',
 		credentials:     `certbot_dns_corenetworks:dns_corenetworks_username = asaHB12r
 certbot_dns_corenetworks:dns_corenetworks_password = secure_password`,
 		full_plugin_name: 'certbot-dns-corenetworks:dns-corenetworks',
@@ -51,6 +54,7 @@ certbot_dns_corenetworks:dns_corenetworks_password = secure_password`,
 		display_name:    'cPanel',
 		package_name:    'certbot-dns-cpanel',
 		package_version: '0.2.2',
+		dependencies:    '',
 		credentials:     `certbot_dns_cpanel:cpanel_url = https://cpanel.example.com:2083
 certbot_dns_cpanel:cpanel_username = user
 certbot_dns_cpanel:cpanel_password = hunter2`,
@@ -61,6 +65,7 @@ certbot_dns_cpanel:cpanel_password = hunter2`,
 		display_name:     'DigitalOcean',
 		package_name:     'certbot-dns-digitalocean',
 		package_version:  '1.8.0',
+		dependencies:     '',
 		credentials:      'dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff',
 		full_plugin_name: 'dns-digitalocean',
 	},
@@ -69,6 +74,7 @@ certbot_dns_cpanel:cpanel_password = hunter2`,
 		display_name:    'DirectAdmin',
 		package_name:    'certbot-dns-directadmin',
 		package_version: '0.0.20',
+		dependencies:    '',
 		credentials:     `directadmin_url = https://my.directadminserver.com:2222
 directadmin_username = username
 directadmin_password = aSuperStrongPassword`,
@@ -79,6 +85,7 @@ directadmin_password = aSuperStrongPassword`,
 		display_name:     'DNSimple',
 		package_name:     'certbot-dns-dnsimple',
 		package_version:  '1.8.0',
+		dependencies:     '',
 		credentials:      'dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw',
 		full_plugin_name: 'dns-dnsimple',
 	},
@@ -87,6 +94,7 @@ directadmin_password = aSuperStrongPassword`,
 		display_name:    'DNS Made Easy',
 		package_name:    'certbot-dns-dnsmadeeasy',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a
 dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`,
 		full_plugin_name: 'dns-dnsmadeeasy',
@@ -96,6 +104,7 @@ dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`,
 		display_name:    'DNSPod',
 		package_name:    'certbot-dns-dnspod',
 		package_version: '0.1.0',
+		dependencies:    '',
 		credentials:     `certbot_dns_dnspod:dns_dnspod_email = "DNSPOD-API-REQUIRES-A-VALID-EMAIL"
 certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
 		full_plugin_name: 'certbot-dns-dnspod:dns-dnspod',
@@ -105,9 +114,10 @@ certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
 		display_name:    'Google',
 		package_name:    'certbot-dns-google',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `{
-	"type": "service_account",
-	...
+"type": "service_account",
+...
 }`,
 		full_plugin_name: 'dns-google',
 	},
@@ -116,6 +126,7 @@ certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
 		display_name:     'Hetzner',
 		package_name:     'certbot-dns-hetzner',
 		package_version:  '1.0.4',
+		dependencies:     '',
 		credentials:      'certbot_dns_hetzner:dns_hetzner_api_token = 0123456789abcdef0123456789abcdef',
 		full_plugin_name: 'certbot-dns-hetzner:dns-hetzner',
 	},
@@ -124,6 +135,7 @@ certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
 		display_name:    'INWX',
 		package_name:    'certbot-dns-inwx',
 		package_version: '2.1.2',
+		dependencies:    '',
 		credentials:     `certbot_dns_inwx:dns_inwx_url = https://api.domrobot.com/xmlrpc/
 certbot_dns_inwx:dns_inwx_username = your_username
 certbot_dns_inwx:dns_inwx_password = your_password
@@ -135,6 +147,7 @@ certbot_dns_inwx:dns_inwx_shared_secret = your_shared_secret optional`,
 		display_name:    'ISPConfig',
 		package_name:    'certbot-dns-ispconfig',
 		package_version: '0.2.0',
+		dependencies:    '',
 		credentials:     `certbot_dns_ispconfig:dns_ispconfig_username = myremoteuser
 certbot_dns_ispconfig:dns_ispconfig_password = verysecureremoteuserpassword
 certbot_dns_ispconfig:dns_ispconfig_endpoint = https://localhost:8080`,
@@ -145,6 +158,7 @@ certbot_dns_ispconfig:dns_ispconfig_endpoint = https://localhost:8080`,
 		display_name:    'Isset',
 		package_name:    'certbot-dns-isset',
 		package_version: '0.0.3',
+		dependencies:    '',
 		credentials:     `certbot_dns_isset:dns_isset_endpoint="https://customer.isset.net/api"
 certbot_dns_isset:dns_isset_token="<token>"`,
 		full_plugin_name: 'certbot-dns-isset:dns-isset',
@@ -154,6 +168,7 @@ certbot_dns_isset:dns_isset_token="<token>"`,
 		display_name:    'Linode',
 		package_name:    'certbot-dns-linode',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64
 dns_linode_version = [<blank>|3|4]`,
 		full_plugin_name: 'dns-linode',
@@ -163,6 +178,7 @@ dns_linode_version = [<blank>|3|4]`,
 		display_name:    'LuaDNS',
 		package_name:    'certbot-dns-luadns',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `dns_luadns_email = [email protected]
 dns_luadns_token = 0123456789abcdef0123456789abcdef`,
 		full_plugin_name: 'dns-luadns',
@@ -172,6 +188,7 @@ dns_luadns_token = 0123456789abcdef0123456789abcdef`,
 		display_name:    'netcup',
 		package_name:    'certbot-dns-netcup',
 		package_version: '1.0.0',
+		dependencies:    '',
 		credentials:     `dns_netcup_customer_id  = 123456
 dns_netcup_api_key      = 0123456789abcdef0123456789abcdef01234567
 dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
@@ -181,7 +198,8 @@ dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
 	njalla: {
 		display_name:     'Njalla',
 		package_name:     'certbot-dns-njalla',
-		package_version:  '0.0.4',
+		package_version:  '1.0.0',
+		dependencies:     '',
 		credentials:      'certbot_dns_njalla:dns_njalla_token = 0123456789abcdef0123456789abcdef01234567',
 		full_plugin_name: 'certbot-dns-njalla:dns-njalla',
 	},
@@ -190,6 +208,7 @@ dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
 		display_name:     'NS1',
 		package_name:     'certbot-dns-nsone',
 		package_version:  '1.8.0',
+		dependencies:     '',
 		credentials:      'dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw',
 		full_plugin_name: 'dns-nsone',
 	},
@@ -198,6 +217,7 @@ dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
 		display_name:    'OVH',
 		package_name:    'certbot-dns-ovh',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `dns_ovh_endpoint = ovh-eu
 dns_ovh_application_key = MDAwMDAwMDAwMDAw
 dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
@@ -209,6 +229,7 @@ dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`,
 		display_name:    'PowerDNS',
 		package_name:    'certbot-dns-powerdns',
 		package_version: '0.2.0',
+		dependencies:    '',
 		credentials:     `certbot_dns_powerdns:dns_powerdns_api_url = https://api.mypowerdns.example.org
 certbot_dns_powerdns:dns_powerdns_api_key = AbCbASsd!@34`,
 		full_plugin_name: 'certbot-dns-powerdns:dns-powerdns',
@@ -218,6 +239,7 @@ certbot_dns_powerdns:dns_powerdns_api_key = AbCbASsd!@34`,
 		display_name:    'RFC 2136',
 		package_name:    'certbot-dns-rfc2136',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `# Target DNS server
 dns_rfc2136_server = 192.0.2.1
 # Target DNS port
@@ -235,6 +257,7 @@ dns_rfc2136_algorithm = HMAC-SHA512`,
 		display_name:    'Route 53 (Amazon)',
 		package_name:    'certbot-dns-route53',
 		package_version: '1.8.0',
+		dependencies:    '',
 		credentials:     `[default]
 aws_access_key_id=AKIAIOSFODNN7EXAMPLE
 aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
@@ -245,7 +268,8 @@ aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
 		display_name:     'Vultr',
 		package_name:     'certbot-dns-vultr',
 		package_version:  '1.0.3',
+		dependencies:     '',
 		credentials:      'certbot_dns_vultr:dns_vultr_key = YOUR_VULTR_API_KEY',
 		full_plugin_name: 'certbot-dns-vultr:dns-vultr',
 	},
-};
+};