From 1028de8158a3e9396930c2e91d5862de5d7866ba Mon Sep 17 00:00:00 2001 From: chaptergy <26956711+chaptergy@users.noreply.github.com> Date: Sat, 17 Oct 2020 12:13:08 +0200 Subject: [PATCH] Adds certbot plugin installation check on startup --- backend/internal/certificate.js | 50 +++++++++++++-------------------- backend/setup.js | 43 +++++++++++++++++++++++++++- frontend/js/i18n/messages.json | 2 +- global/certbot-dns-plugins.js | 2 +- 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 613c837c..e00d1f24 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -788,7 +788,7 @@ 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_loc = '/etc/letsencrypt/credentials/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; @@ -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 + '" ' + @@ -937,23 +935,14 @@ const internalCertificate = { 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 +954,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; }) diff --git a/backend/setup.js b/backend/setup.js index e47431f0..13ebc700 100644 --- a/backend/setup.js +++ b/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,46 @@ 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 package = `${dns_plugin.package_name}==${dns_plugin.package_version}`; + if (plugins.indexOf(package) === -1) plugins.push(package); + + // Make sure credentials file exists + const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; + const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir /etc/letsencrypt/credentials; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }'; + promises.push(utils.exec(credentials_cmd)); + } + }); + + const install_cmd = 'pip3 install ' + plugins.join(' '); + promises.push(utils.exec(install_cmd)); + 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); }; diff --git a/frontend/js/i18n/messages.json b/frontend/js/i18n/messages.json index 8ce201aa..eb0f2d29 100644 --- a/frontend/js/i18n/messages.json +++ b/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." diff --git a/global/certbot-dns-plugins.js b/global/certbot-dns-plugins.js index 8170f73e..e87425ca 100644 --- a/global/certbot-dns-plugins.js +++ b/global/certbot-dns-plugins.js @@ -181,7 +181,7 @@ dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`, njalla: { display_name: 'Njalla', package_name: 'certbot-dns-njalla', - package_version: '0.0.4', + package_version: '1.0.0', credentials: 'certbot_dns_njalla:dns_njalla_token = 0123456789abcdef0123456789abcdef01234567', full_plugin_name: 'certbot-dns-njalla:dns-njalla', },