Skip to content

Commit 093b48a

Browse files
committed
Implements backend changes to allow more dns challenges
1 parent 05f6a55 commit 093b48a

File tree

2 files changed

+88
-32
lines changed

2 files changed

+88
-32
lines changed

backend/internal/certificate.js

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const internalNginx = require('./nginx');
1313
const internalHost = require('./host');
1414
const certbot_command = '/usr/bin/certbot';
1515
const le_config = '/etc/letsencrypt.ini';
16+
const dns_plugins = require('../../utils/certbot-dns-plugins')
1617

1718
function omissions() {
1819
return ['is_deleted'];
@@ -141,11 +142,11 @@ const internalCertificate = {
141142
});
142143
})
143144
.then((in_use_result) => {
144-
// Is CloudFlare, no config needed, so skip 3 and 5.
145-
if (data.meta.cloudflare_use) {
145+
// With DNS challenge no config is needed, so skip 3 and 5.
146+
if (certificate.meta.dns_challenge) {
146147
return internalNginx.reload().then(() => {
147148
// 4. Request cert
148-
return internalCertificate.requestLetsEncryptCloudFlareDnsSsl(certificate, data.meta.cloudflare_token);
149+
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
149150
})
150151
.then(internalNginx.reload)
151152
.then(() => {
@@ -772,35 +773,58 @@ const internalCertificate = {
772773
},
773774

774775
/**
775-
* @param {Object} certificate the certificate row
776-
* @param {String} apiToken the cloudflare api token
776+
* @param {Object} certificate the certificate row
777+
* @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`)
778+
* @param {String | null} credentials the content of this providers credentials file
779+
* @param {String} propagation_seconds the cloudflare api token
777780
* @returns {Promise}
778781
*/
779-
requestLetsEncryptCloudFlareDnsSsl: (certificate, apiToken) => {
780-
logger.info('Requesting Let\'sEncrypt certificates via Cloudflare DNS for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
782+
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
783+
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
784+
785+
if(!dns_plugin){
786+
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`)
787+
}
788+
789+
logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
781790

782-
let tokenLoc = '~/cloudflare-token';
783-
let storeKey = 'echo "dns_cloudflare_api_token = ' + apiToken + '" > ' + tokenLoc;
791+
const credentials_loc = `/etc/letsencrypt/credentials-${certificate.id}`;
792+
const credentials_cmd = `echo '${certificate.meta.dns_provider_credentials.replace("'", "\'")}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'`;
793+
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
784794

785-
let cmd =
786-
storeKey + ' && ' +
795+
const main_cmd =
787796
certbot_command + ' certonly --non-interactive ' +
788797
'--cert-name "npm-' + certificate.id + '" ' +
789798
'--agree-tos ' +
790799
'--email "' + certificate.meta.letsencrypt_email + '" ' +
791800
'--domains "' + certificate.domain_names.join(',') + '" ' +
792-
'--dns-cloudflare --dns-cloudflare-credentials ' + tokenLoc +
793-
(le_staging ? ' --staging' : '')
794-
+ ' && rm ' + tokenLoc;
801+
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
802+
'--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"' +
803+
(
804+
certificate.meta.propagation_seconds !== undefined
805+
? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
806+
: ''
807+
) +
808+
(le_staging ? ' --staging' : '');
809+
810+
const teardown_cmd = `rm '${credentials_loc}'`;
795811

796812
if (debug_mode) {
797-
logger.info('Command:', cmd);
813+
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
798814
}
799815

800-
return utils.exec(cmd).then((result) => {
801-
logger.info(result);
802-
return result;
803-
});
816+
return utils.exec(credentials_cmd)
817+
.then(() => {
818+
return utils.exec(prepare_cmd)
819+
.then(() => {
820+
return utils.exec(main_cmd)
821+
.then(async (result) => {
822+
await utils.exec(teardown_cmd);
823+
logger.info(result);
824+
return result;
825+
});
826+
});
827+
});
804828
},
805829

806830

@@ -817,7 +841,7 @@ const internalCertificate = {
817841
})
818842
.then((certificate) => {
819843
if (certificate.provider === 'letsencrypt') {
820-
let renewMethod = certificate.meta.cloudflare_use ? internalCertificate.renewLetsEncryptCloudFlareSsl : internalCertificate.renewLetsEncryptSsl;
844+
let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl;
821845

822846
return renewMethod(certificate)
823847
.then(() => {
@@ -877,22 +901,42 @@ const internalCertificate = {
877901
* @param {Object} certificate the certificate row
878902
* @returns {Promise}
879903
*/
880-
renewLetsEncryptCloudFlareSsl: (certificate) => {
881-
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
904+
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
905+
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
882906

883-
let cmd = certbot_command + ' renew --non-interactive ' +
907+
if(!dns_plugin){
908+
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`)
909+
}
910+
911+
logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
912+
913+
const credentials_loc = `/etc/letsencrypt/credentials-${certificate.id}`;
914+
const credentials_cmd = `echo '${certificate.meta.dns_provider_credentials.replace("'", "\'")}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'`;
915+
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
916+
917+
const main_cmd =
918+
certbot_command + ' renew --non-interactive ' +
884919
'--cert-name "npm-' + certificate.id + '" ' +
885-
'--disable-hook-validation ' +
886-
(le_staging ? '--staging' : '');
920+
'--disable-hook-validation' +
921+
(le_staging ? ' --staging' : '');
922+
923+
const teardown_cmd = `rm '${credentials_loc}'`;
887924

888925
if (debug_mode) {
889-
logger.info('Command:', cmd);
926+
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
890927
}
891928

892-
return utils.exec(cmd)
893-
.then((result) => {
894-
logger.info(result);
895-
return result;
929+
return utils.exec(credentials_cmd)
930+
.then(() => {
931+
return utils.exec(prepare_cmd)
932+
.then(() => {
933+
return utils.exec(main_cmd)
934+
.then(async (result) => {
935+
await utils.exec(teardown_cmd);
936+
logger.info(result);
937+
return result;
938+
});
939+
});
896940
});
897941
},
898942

backend/schema/endpoints/certificates.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,23 @@
4242
"letsencrypt_agree": {
4343
"type": "boolean"
4444
},
45-
"cloudflare_use": {
45+
"dns_challenge": {
4646
"type": "boolean"
4747
},
48-
"cloudflare_token": {
48+
"dns_provider": {
4949
"type": "string"
50+
},
51+
"dns_provider_credentials": {
52+
"type": "string"
53+
},
54+
"propagation_seconds": {
55+
"anyOf": [
56+
{
57+
"type": "integer",
58+
"minimum": 0
59+
}
60+
]
61+
5062
}
5163
}
5264
}

0 commit comments

Comments
 (0)