浏览代码

Implements backend changes to allow more dns challenges

chaptergy 5 年之前
父节点
当前提交
093b48ad7b
共有 2 个文件被更改,包括 88 次插入32 次删除
  1. 74 30
      backend/internal/certificate.js
  2. 14 2
      backend/schema/endpoints/certificates.json

+ 74 - 30
backend/internal/certificate.js

@@ -13,6 +13,7 @@ const internalNginx    = require('./nginx');
 const internalHost     = require('./host');
 const certbot_command  = '/usr/bin/certbot';
 const le_config        = '/etc/letsencrypt.ini';
+const dns_plugins      = require('../../utils/certbot-dns-plugins')
 
 function omissions() {
 	return ['is_deleted'];
@@ -141,11 +142,11 @@ const internalCertificate = {
 								});
 						})
 						.then((in_use_result) => {
-							// Is CloudFlare, no config needed, so skip 3 and 5.
-							if (data.meta.cloudflare_use) {
+							// With DNS challenge no config is needed, so skip 3 and 5.
+							if (certificate.meta.dns_challenge) {
 								return internalNginx.reload().then(() => {
 									// 4. Request cert
-									return internalCertificate.requestLetsEncryptCloudFlareDnsSsl(certificate, data.meta.cloudflare_token);
+									return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
 								})
 									.then(internalNginx.reload)
 									.then(() => {
@@ -772,35 +773,58 @@ const internalCertificate = {
 	},
 
 	/**
-	 * @param   {Object}  certificate   the certificate row
-	 * @param	{String} apiToken		the cloudflare api token
+	 * @param   {Object}  certificate   			the certificate row
+	 * @param		{String} 	dns_provider				the dns provider name (key used in `certbot-dns-plugins.js`)
+	 * @param		{String | null} 	credentials	the content of this providers credentials file
+	 * @param		{String} 	propagation_seconds	the cloudflare api token
 	 * @returns {Promise}
 	 */
-	requestLetsEncryptCloudFlareDnsSsl: (certificate, apiToken) => {
-		logger.info('Requesting Let\'sEncrypt certificates via Cloudflare DNS for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
+	requestLetsEncryptSslWithDnsChallenge: (certificate) => {
+		const dns_plugin = dns_plugins[certificate.meta.dns_provider];
+
+		if(!dns_plugin){
+			throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`)
+		}
+
+		logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
 
-		let tokenLoc = '~/cloudflare-token';
-		let storeKey = 'echo "dns_cloudflare_api_token = ' + apiToken + '" > ' + tokenLoc;	
+		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 cmd = 
-			storeKey + ' && ' +
+		const main_cmd = 
 			certbot_command + ' certonly --non-interactive ' +
 			'--cert-name "npm-' + certificate.id + '" ' +
 			'--agree-tos ' +
 			'--email "' + certificate.meta.letsencrypt_email + '" ' +			
 			'--domains "' + certificate.domain_names.join(',') + '" ' +
-			'--dns-cloudflare --dns-cloudflare-credentials ' + tokenLoc +
-			(le_staging ? ' --staging' : '')
-			+ ' && rm ' + tokenLoc;
+			'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
+			'--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"' +
+			(
+				certificate.meta.propagation_seconds !== undefined 
+				? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds 
+				: ''
+			) +
+			(le_staging ? ' --staging' : '');
+		
+		const teardown_cmd = `rm '${credentials_loc}'`;
 
 		if (debug_mode) {
-			logger.info('Command:', cmd);
+			logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
 		}
 
-		return utils.exec(cmd).then((result) => {
-			logger.info(result);
-			return result;
-		});
+		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;
+							});
+					});
+			});
 	},
 
 
@@ -817,7 +841,7 @@ const internalCertificate = {
 			})
 			.then((certificate) => {
 				if (certificate.provider === 'letsencrypt') {
-					let renewMethod = certificate.meta.cloudflare_use ? internalCertificate.renewLetsEncryptCloudFlareSsl : internalCertificate.renewLetsEncryptSsl;		
+					let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl;		
 
 					return renewMethod(certificate)
 						.then(() => {
@@ -877,22 +901,42 @@ const internalCertificate = {
 	 * @param   {Object}  certificate   the certificate row
 	 * @returns {Promise}
 	 */
-	renewLetsEncryptCloudFlareSsl: (certificate) => {
-		logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
+	renewLetsEncryptSslWithDnsChallenge: (certificate) => {
+		const dns_plugin = dns_plugins[certificate.meta.dns_provider];
 
-		let cmd = certbot_command + ' renew --non-interactive ' +
+		if(!dns_plugin){
+			throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`)
+		}
+
+		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;
+
+		const main_cmd =
+			certbot_command + ' renew --non-interactive ' +
 			'--cert-name "npm-' + certificate.id + '" ' +
-			'--disable-hook-validation ' +
-			(le_staging ? '--staging' : '');
+			'--disable-hook-validation' +
+			(le_staging ? ' --staging' : '');
+
+		const teardown_cmd = `rm '${credentials_loc}'`;
 
 		if (debug_mode) {
-			logger.info('Command:', cmd);
+			logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
 		}
 
-		return utils.exec(cmd)
-			.then((result) => {
-				logger.info(result);
-				return result;
+		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;
+							});
+					});
 			});
 	},
 

+ 14 - 2
backend/schema/endpoints/certificates.json

@@ -42,11 +42,23 @@
         "letsencrypt_agree": {
           "type": "boolean"
         },
-        "cloudflare_use": {
+        "dns_challenge": {
           "type": "boolean"
         },
-        "cloudflare_token": {
+        "dns_provider": {
           "type": "string"
+        },
+        "dns_provider_credentials": {
+          "type": "string"
+        },
+        "propagation_seconds": {
+          "anyOf": [
+            { 
+              "type": "integer",
+              "minimum": 0 
+            }
+          ]
+          
         }
       }
     }