From b81325d7bf4606a37b3c19a9ab2676c4b209e6ef Mon Sep 17 00:00:00 2001 From: chaptergy <26956711+chaptergy@users.noreply.github.com> Date: Sun, 4 Oct 2020 23:56:02 +0200 Subject: [PATCH] Implements dns challenge provider selection in frontend --- frontend/js/app/nginx/certificates/form.ejs | 87 +++++++++- frontend/js/app/nginx/certificates/form.js | 75 +++++--- .../js/app/nginx/certificates/list/item.ejs | 2 +- .../js/app/nginx/certificates/list/item.js | 12 +- frontend/js/app/nginx/dead/form.ejs | 87 +++++++++- frontend/js/app/nginx/dead/form.js | 99 +++++++---- frontend/js/app/nginx/proxy/form.ejs | 87 +++++++++- frontend/js/app/nginx/proxy/form.js | 101 +++++++---- frontend/js/app/nginx/redirection/form.ejs | 87 +++++++++- frontend/js/app/nginx/redirection/form.js | 103 +++++++---- frontend/js/i18n/messages.json | 10 +- utils/certbot-dns-plugins.js | 160 ++++++++++++++++++ 12 files changed, 755 insertions(+), 155 deletions(-) create mode 100644 utils/certbot-dns-plugins.js diff --git a/frontend/js/app/nginx/certificates/form.ejs b/frontend/js/app/nginx/certificates/form.ejs index 207e59e2..a3d6c07a 100644 --- a/frontend/js/app/nginx/certificates/form.ejs +++ b/frontend/js/app/nginx/certificates/form.ejs @@ -21,21 +21,92 @@ - +
-
-
- - -
+
+
+
<%= i18n('ssl', 'certbot-warning') %>
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'credentials-file-content-info') %> +
+
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'propagation-seconds-info') %> +
+
+
+
+
diff --git a/frontend/js/app/nginx/certificates/form.js b/frontend/js/app/nginx/certificates/form.js index 53a6e143..bc239df3 100644 --- a/frontend/js/app/nginx/certificates/form.js +++ b/frontend/js/app/nginx/certificates/form.js @@ -3,6 +3,8 @@ const Mn = require('backbone.marionette'); const App = require('../../main'); const CertificateModel = require('../../../models/certificate'); const template = require('./form.ejs'); +const i18n = require('../../i18n'); +const dns_providers = require('../../../../../utils/certbot-dns-plugins'); require('jquery-serializejson'); require('selectize'); @@ -21,25 +23,46 @@ module.exports = Mn.View.extend({ other_certificate: '#other_certificate', other_certificate_label: '#other_certificate_label', other_certificate_key: '#other_certificate_key', - cloudflare_switch: 'input[name="meta[cloudflare_use]"]', - cloudflare_token: 'input[name="meta[cloudflare_token]"', - cloudflare: '.cloudflare', + dns_challenge_switch: 'input[name="meta[dns_challenge]"]', + dns_challenge_content: '.dns-challenge', + dns_provider: 'select[name="meta[dns_provider]"]', + credentials_file_content: '.credentials-file-content', + dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', + propagation_seconds: 'input[name="meta[propagation_seconds]"]', other_certificate_key_label: '#other_certificate_key_label', other_intermediate_certificate: '#other_intermediate_certificate', other_intermediate_certificate_label: '#other_intermediate_certificate_label' }, events: { - 'change @ui.cloudflare_switch': function() { - let checked = this.ui.cloudflare_switch.prop('checked'); - if (checked) { - this.ui.cloudflare_token.prop('required', 'required'); - this.ui.cloudflare.show(); - } else { - this.ui.cloudflare_token.prop('required', false); - this.ui.cloudflare.hide(); + 'change @ui.dns_challenge_switch': function () { + const checked = this.ui.dns_challenge_switch.prop('checked'); + if (checked) { + this.ui.dns_provider.prop('required', 'required'); + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ + this.ui.dns_provider_credentials.prop('required', 'required'); + } + this.ui.dns_challenge_content.show(); + } else { + this.ui.dns_provider.prop('required', false); + this.ui.dns_provider_credentials.prop('required', false); + this.ui.dns_challenge_content.hide(); } }, + + 'change @ui.dns_provider': function () { + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { + this.ui.dns_provider_credentials.prop('required', 'required'); + this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; + this.ui.credentials_file_content.show(); + } else { + this.ui.dns_provider_credentials.prop('required', false); + this.ui.credentials_file_content.hide(); + } + }, + 'click @ui.save': function (e) { e.preventDefault(); @@ -56,7 +79,7 @@ module.exports = Mn.View.extend({ let domain_err = false; - if (!data.meta.cloudflare_use) { + if (!data.meta.dns_challenge) { data.domain_names.split(',').map(function (name) { if (name.match(/\*/im)) { domain_err = true; @@ -65,7 +88,7 @@ module.exports = Mn.View.extend({ } if (domain_err) { - alert('Cannot request Let\'s Encrypt Certificate for wildcard domains when not using CloudFlare DNS'); + alert(i18n('ssl', 'no-wildcard-without-dns')); return; } @@ -73,8 +96,9 @@ module.exports = Mn.View.extend({ if (typeof data.meta !== 'undefined' && typeof data.meta.letsencrypt_agree !== 'undefined') { data.meta.letsencrypt_agree = !!data.meta.letsencrypt_agree; } - if (typeof data.meta !== 'undefined' && typeof data.meta.cloudflare_use !== 'undefined') { - data.meta.cloudflare_use = !!data.meta.cloudflare_use; + + if (typeof data.meta !== 'undefined' && typeof data.meta.dns_challenge !== 'undefined') { + data.meta.dns_challenge = !!data.meta.dns_challenge; } if (typeof data.domain_names === 'string' && data.domain_names) { @@ -176,14 +200,22 @@ module.exports = Mn.View.extend({ getLetsencryptEmail: function () { return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : App.Cache.User.get('email'); }, - getLetsencryptAgree: function () { return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false; }, - - getCloudflareUse: function () { - return typeof this.meta.cloudflare_use !== 'undefined' ? this.meta.cloudflare_use : false; - } + getUseDnsChallenge: function () { + return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; + }, + getDnsProvider: function () { + return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; + }, + getDnsProviderCredentials: function () { + return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; + }, + getPropagationSeconds: function () { + return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; + }, + dns_plugins: dns_providers, }, onRender: function () { @@ -199,7 +231,8 @@ module.exports = Mn.View.extend({ }, createFilter: /^(?:[^.]+\.?)+[^.]$/ }); - this.ui.cloudflare.hide(); + this.ui.dns_challenge_content.hide(); + this.ui.credentials_file_content.hide(); }, initialize: function (options) { diff --git a/frontend/js/app/nginx/certificates/list/item.ejs b/frontend/js/app/nginx/certificates/list/item.ejs index 857a08b8..67764b0d 100644 --- a/frontend/js/app/nginx/certificates/list/item.ejs +++ b/frontend/js/app/nginx/certificates/list/item.ejs @@ -28,7 +28,7 @@
- <%- i18n('ssl', provider) %><% if (meta.cloudflare_use) { %> - CloudFlare DNS<% } %> + <%- i18n('ssl', provider) %><% if (meta.dns_provider) { %> - <% dns_providers[meta.dns_provider].display_name } %> <%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %> diff --git a/frontend/js/app/nginx/certificates/list/item.js b/frontend/js/app/nginx/certificates/list/item.js index 69152270..b5b2d85a 100644 --- a/frontend/js/app/nginx/certificates/list/item.js +++ b/frontend/js/app/nginx/certificates/list/item.js @@ -1,7 +1,8 @@ -const Mn = require('backbone.marionette'); -const moment = require('moment'); -const App = require('../../../main'); -const template = require('./item.ejs'); +const Mn = require('backbone.marionette'); +const moment = require('moment'); +const App = require('../../../main'); +const template = require('./item.ejs'); +const dns_providers = require('../../../../../../utils/certbot-dns-plugins') module.exports = Mn.View.extend({ template: template, @@ -35,7 +36,8 @@ module.exports = Mn.View.extend({ canManage: App.Cache.User.canManage('certificates'), isExpired: function () { return moment(this.expires_on).isBefore(moment()); - } + }, + dns_providers: dns_providers }, initialize: function () { diff --git a/frontend/js/app/nginx/dead/form.ejs b/frontend/js/app/nginx/dead/form.ejs index d48820f6..1d01e09b 100644 --- a/frontend/js/app/nginx/dead/form.ejs +++ b/frontend/js/app/nginx/dead/form.ejs @@ -73,21 +73,92 @@
- +
-
-
- - -
+
+
+
<%= i18n('ssl', 'certbot-warning') %>
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'credentials-file-content-info') %> +
+
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'propagation-seconds-info') %> +
+
+
+
+
diff --git a/frontend/js/app/nginx/dead/form.js b/frontend/js/app/nginx/dead/form.js index aca367a5..393788d1 100644 --- a/frontend/js/app/nginx/dead/form.js +++ b/frontend/js/app/nginx/dead/form.js @@ -4,6 +4,8 @@ const DeadHostModel = require('../../../models/dead-host'); const template = require('./form.ejs'); const certListItemTemplate = require('../certificates-list-item.ejs'); const Helpers = require('../../../lib/helpers'); +const i18n = require('../../i18n'); +const dns_providers = require('../../../../../utils/certbot-dns-plugins'); require('jquery-serializejson'); require('selectize'); @@ -13,20 +15,23 @@ module.exports = Mn.View.extend({ className: 'modal-dialog', ui: { - form: 'form', - domain_names: 'input[name="domain_names"]', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - certificate_select: 'select[name="certificate_id"]', - ssl_forced: 'input[name="ssl_forced"]', - hsts_enabled: 'input[name="hsts_enabled"]', - hsts_subdomains: 'input[name="hsts_subdomains"]', - http2_support: 'input[name="http2_support"]', - cloudflare_switch: 'input[name="meta[cloudflare_use]"]', - cloudflare_token: 'input[name="meta[cloudflare_token]"', - cloudflare: '.cloudflare', - letsencrypt: '.letsencrypt' + form: 'form', + domain_names: 'input[name="domain_names"]', + buttons: '.modal-footer button', + cancel: 'button.cancel', + save: 'button.save', + certificate_select: 'select[name="certificate_id"]', + ssl_forced: 'input[name="ssl_forced"]', + hsts_enabled: 'input[name="hsts_enabled"]', + hsts_subdomains: 'input[name="hsts_subdomains"]', + http2_support: 'input[name="http2_support"]', + dns_challenge_switch: 'input[name="meta[dns_challenge]"]', + dns_challenge_content: '.dns-challenge', + dns_provider: 'select[name="meta[dns_provider]"]', + credentials_file_content: '.credentials-file-content', + dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', + propagation_seconds: 'input[name="meta[propagation_seconds]"]', + letsencrypt: '.letsencrypt' }, events: { @@ -34,7 +39,7 @@ module.exports = Mn.View.extend({ let id = this.ui.certificate_select.val(); if (id === 'new') { this.ui.letsencrypt.show().find('input').prop('disabled', false); - this.ui.cloudflare.hide(); + this.ui.dns_challenge_content.hide(); } else { this.ui.letsencrypt.hide().find('input').prop('disabled', true); } @@ -81,14 +86,31 @@ module.exports = Mn.View.extend({ } }, - 'change @ui.cloudflare_switch': function() { - let checked = this.ui.cloudflare_switch.prop('checked'); - if (checked) { - this.ui.cloudflare_token.prop('required', 'required'); - this.ui.cloudflare.show(); - } else { - this.ui.cloudflare_token.prop('required', false); - this.ui.cloudflare.hide(); + 'change @ui.dns_challenge_switch': function () { + const checked = this.ui.dns_challenge_switch.prop('checked'); + if (checked) { + this.ui.dns_provider.prop('required', 'required'); + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ + this.ui.dns_provider_credentials.prop('required', 'required'); + } + this.ui.dns_challenge_content.show(); + } else { + this.ui.dns_provider.prop('required', false); + this.ui.dns_provider_credentials.prop('required', false); + this.ui.dns_challenge_content.hide(); + } + }, + + 'change @ui.dns_provider': function () { + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { + this.ui.dns_provider_credentials.prop('required', 'required'); + this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; + this.ui.credentials_file_content.show(); + } else { + this.ui.dns_provider_credentials.prop('required', false); + this.ui.credentials_file_content.hide(); } }, @@ -104,10 +126,11 @@ module.exports = Mn.View.extend({ let data = this.ui.form.serializeJSON(); // Manipulate - data.hsts_enabled = !!data.hsts_enabled; - data.hsts_subdomains = !!data.hsts_subdomains; - data.http2_support = !!data.http2_support; - data.ssl_forced = !!data.ssl_forced; + data.hsts_enabled = !!data.hsts_enabled; + data.hsts_subdomains = !!data.hsts_subdomains; + data.http2_support = !!data.http2_support; + data.ssl_forced = !!data.ssl_forced; + data.meta.dns_challenge = !!data.meta.dns_challenge; if (typeof data.domain_names === 'string' && data.domain_names) { data.domain_names = data.domain_names.split(','); @@ -116,7 +139,7 @@ module.exports = Mn.View.extend({ // Check for any domain names containing wildcards, which are not allowed with letsencrypt if (data.certificate_id === 'new') { let domain_err = false; - if (!data.meta.cloudflare_use) { + if (!data.meta.dns_challenge) { data.domain_names.map(function (name) { if (name.match(/\*/im)) { domain_err = true; @@ -125,11 +148,10 @@ module.exports = Mn.View.extend({ } if (domain_err) { - alert('Cannot request Let\'s Encrypt Certificate for wildcard domains without CloudFlare DNS.'); + alert(i18n('ssl', 'no-wildcard-without-dns')); return; } - data.meta.cloudflare_use = data.meta.cloudflare_use === '1'; data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1'; } else { data.certificate_id = parseInt(data.certificate_id, 10); @@ -169,7 +191,20 @@ module.exports = Mn.View.extend({ templateContext: { getLetsencryptEmail: function () { return App.Cache.User.get('email'); - } + }, + getUseDnsChallenge: function () { + return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; + }, + getDnsProvider: function () { + return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; + }, + getDnsProviderCredentials: function () { + return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; + }, + getPropagationSeconds: function () { + return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; + }, + dns_plugins: dns_providers, }, onRender: function () { @@ -190,6 +225,8 @@ module.exports = Mn.View.extend({ }); // Certificates + this.ui.dns_challenge_content.hide(); + this.ui.credentials_file_content.hide(); this.ui.letsencrypt.hide(); this.ui.certificate_select.selectize({ valueField: 'id', diff --git a/frontend/js/app/nginx/proxy/form.ejs b/frontend/js/app/nginx/proxy/form.ejs index e0035977..34f319c4 100644 --- a/frontend/js/app/nginx/proxy/form.ejs +++ b/frontend/js/app/nginx/proxy/form.ejs @@ -141,21 +141,92 @@
- +
-
-
- - -
+
+
+
<%= i18n('ssl', 'certbot-warning') %>
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'credentials-file-content-info') %> +
+
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'propagation-seconds-info') %> +
+
+
+
+
diff --git a/frontend/js/app/nginx/proxy/form.js b/frontend/js/app/nginx/proxy/form.js index 0f642814..4211050a 100644 --- a/frontend/js/app/nginx/proxy/form.js +++ b/frontend/js/app/nginx/proxy/form.js @@ -7,6 +7,8 @@ const certListItemTemplate = require('../certificates-list-item.ejs'); const accessListItemTemplate = require('./access-list-item.ejs'); const CustomLocation = require('./location'); const Helpers = require('../../../lib/helpers'); +const i18n = require('../../i18n'); +const dns_providers = require('../../../../../utils/certbot-dns-plugins'); require('jquery-serializejson'); @@ -19,25 +21,28 @@ module.exports = Mn.View.extend({ locationsCollection: new ProxyLocationModel.Collection(), ui: { - form: 'form', - domain_names: 'input[name="domain_names"]', - forward_host: 'input[name="forward_host"]', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - add_location_btn: 'button.add_location', - locations_container:'.locations_container', - certificate_select: 'select[name="certificate_id"]', - access_list_select: 'select[name="access_list_id"]', - ssl_forced: 'input[name="ssl_forced"]', - hsts_enabled: 'input[name="hsts_enabled"]', - hsts_subdomains: 'input[name="hsts_subdomains"]', - http2_support: 'input[name="http2_support"]', - cloudflare_switch: 'input[name="meta[cloudflare_use]"]', - cloudflare_token: 'input[name="meta[cloudflare_token]"', - cloudflare: '.cloudflare', - forward_scheme: 'select[name="forward_scheme"]', - letsencrypt: '.letsencrypt' + form: 'form', + domain_names: 'input[name="domain_names"]', + forward_host: 'input[name="forward_host"]', + buttons: '.modal-footer button', + cancel: 'button.cancel', + save: 'button.save', + add_location_btn: 'button.add_location', + locations_container: '.locations_container', + certificate_select: 'select[name="certificate_id"]', + access_list_select: 'select[name="access_list_id"]', + ssl_forced: 'input[name="ssl_forced"]', + hsts_enabled: 'input[name="hsts_enabled"]', + hsts_subdomains: 'input[name="hsts_subdomains"]', + http2_support: 'input[name="http2_support"]', + dns_challenge_switch: 'input[name="meta[dns_challenge]"]', + dns_challenge_content: '.dns-challenge', + dns_provider: 'select[name="meta[dns_provider]"]', + credentials_file_content: '.credentials-file-content', + dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', + propagation_seconds: 'input[name="meta[propagation_seconds]"]', + forward_scheme: 'select[name="forward_scheme"]', + letsencrypt: '.letsencrypt' }, regions: { @@ -49,7 +54,7 @@ module.exports = Mn.View.extend({ let id = this.ui.certificate_select.val(); if (id === 'new') { this.ui.letsencrypt.show().find('input').prop('disabled', false); - this.ui.cloudflare.hide(); + this.ui.dns_challenge_content.hide(); } else { this.ui.letsencrypt.hide().find('input').prop('disabled', true); } @@ -95,14 +100,31 @@ module.exports = Mn.View.extend({ } }, - 'change @ui.cloudflare_switch': function() { - let checked = this.ui.cloudflare_switch.prop('checked'); - if (checked) { - this.ui.cloudflare_token.prop('required', 'required'); - this.ui.cloudflare.show(); - } else { - this.ui.cloudflare_token.prop('required', false); - this.ui.cloudflare.hide(); + 'change @ui.dns_challenge_switch': function () { + const checked = this.ui.dns_challenge_switch.prop('checked'); + if (checked) { + this.ui.dns_provider.prop('required', 'required'); + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ + this.ui.dns_provider_credentials.prop('required', 'required'); + } + this.ui.dns_challenge_content.show(); + } else { + this.ui.dns_provider.prop('required', false); + this.ui.dns_provider_credentials.prop('required', false); + this.ui.dns_challenge_content.hide(); + } + }, + + 'change @ui.dns_provider': function () { + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { + this.ui.dns_provider_credentials.prop('required', 'required'); + this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; + this.ui.credentials_file_content.show(); + } else { + this.ui.dns_provider_credentials.prop('required', false); + this.ui.credentials_file_content.hide(); } }, @@ -143,6 +165,7 @@ module.exports = Mn.View.extend({ data.hsts_enabled = !!data.hsts_enabled; data.hsts_subdomains = !!data.hsts_subdomains; data.ssl_forced = !!data.ssl_forced; + data.meta.dns_challenge = !!data.meta.dns_challenge; if (typeof data.domain_names === 'string' && data.domain_names) { data.domain_names = data.domain_names.split(','); @@ -151,7 +174,7 @@ module.exports = Mn.View.extend({ // Check for any domain names containing wildcards, which are not allowed with letsencrypt if (data.certificate_id === 'new') { let domain_err = false; - if (!data.meta.cloudflare_use) { + if (!data.meta.dns_challenge) { data.domain_names.map(function (name) { if (name.match(/\*/im)) { domain_err = true; @@ -160,11 +183,10 @@ module.exports = Mn.View.extend({ } if (domain_err) { - alert('Cannot request Let\'s Encrypt Certificate for wildcard domains without CloudFlare DNS.'); + alert(i18n('ssl', 'no-wildcard-without-dns')); return; } - data.meta.cloudflare_use = data.meta.cloudflare_use === '1'; data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1'; } else { data.certificate_id = parseInt(data.certificate_id, 10); @@ -204,7 +226,20 @@ module.exports = Mn.View.extend({ templateContext: { getLetsencryptEmail: function () { return App.Cache.User.get('email'); - } + }, + getUseDnsChallenge: function () { + return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; + }, + getDnsProvider: function () { + return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; + }, + getDnsProviderCredentials: function () { + return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; + }, + getPropagationSeconds: function () { + return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; + }, + dns_plugins: dns_providers, }, onRender: function () { @@ -258,6 +293,8 @@ module.exports = Mn.View.extend({ }); // Certificates + this.ui.dns_challenge_content.hide(); + this.ui.credentials_file_content.hide(); this.ui.letsencrypt.hide(); this.ui.certificate_select.selectize({ valueField: 'id', diff --git a/frontend/js/app/nginx/redirection/form.ejs b/frontend/js/app/nginx/redirection/form.ejs index 7d497699..f4e4375e 100644 --- a/frontend/js/app/nginx/redirection/form.ejs +++ b/frontend/js/app/nginx/redirection/form.ejs @@ -97,21 +97,92 @@
- +
-
-
- - -
+
+
+
<%= i18n('ssl', 'certbot-warning') %>
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'credentials-file-content-info') %> +
+
+
+
+ + +
+
+
+ + +
+ + <%= i18n('ssl', 'propagation-seconds-info') %> +
+
+
+
+
diff --git a/frontend/js/app/nginx/redirection/form.js b/frontend/js/app/nginx/redirection/form.js index 4e5b168c..c1c6d9d1 100644 --- a/frontend/js/app/nginx/redirection/form.js +++ b/frontend/js/app/nginx/redirection/form.js @@ -4,6 +4,9 @@ const RedirectionHostModel = require('../../../models/redirection-host'); const template = require('./form.ejs'); const certListItemTemplate = require('../certificates-list-item.ejs'); const Helpers = require('../../../lib/helpers'); +const i18n = require('../../i18n'); +const dns_providers = require('../../../../../utils/certbot-dns-plugins'); + require('jquery-serializejson'); require('selectize'); @@ -13,20 +16,23 @@ module.exports = Mn.View.extend({ className: 'modal-dialog', ui: { - form: 'form', - domain_names: 'input[name="domain_names"]', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - certificate_select: 'select[name="certificate_id"]', - ssl_forced: 'input[name="ssl_forced"]', - hsts_enabled: 'input[name="hsts_enabled"]', - hsts_subdomains: 'input[name="hsts_subdomains"]', - http2_support: 'input[name="http2_support"]', - cloudflare_switch: 'input[name="meta[cloudflare_use]"]', - cloudflare_token: 'input[name="meta[cloudflare_token]"', - cloudflare: '.cloudflare', - letsencrypt: '.letsencrypt' + form: 'form', + domain_names: 'input[name="domain_names"]', + buttons: '.modal-footer button', + cancel: 'button.cancel', + save: 'button.save', + certificate_select: 'select[name="certificate_id"]', + ssl_forced: 'input[name="ssl_forced"]', + hsts_enabled: 'input[name="hsts_enabled"]', + hsts_subdomains: 'input[name="hsts_subdomains"]', + http2_support: 'input[name="http2_support"]', + dns_challenge_switch: 'input[name="meta[dns_challenge]"]', + dns_challenge_content: '.dns-challenge', + dns_provider: 'select[name="meta[dns_provider]"]', + credentials_file_content: '.credentials-file-content', + dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', + propagation_seconds: 'input[name="meta[propagation_seconds]"]', + letsencrypt: '.letsencrypt' }, events: { @@ -34,7 +40,7 @@ module.exports = Mn.View.extend({ let id = this.ui.certificate_select.val(); if (id === 'new') { this.ui.letsencrypt.show().find('input').prop('disabled', false); - this.ui.cloudflare.hide(); + this.ui.dns_challenge_content.hide(); } else { this.ui.letsencrypt.hide().find('input').prop('disabled', true); } @@ -80,14 +86,31 @@ module.exports = Mn.View.extend({ } }, - 'change @ui.cloudflare_switch': function() { - let checked = this.ui.cloudflare_switch.prop('checked'); - if (checked) { - this.ui.cloudflare_token.prop('required', 'required'); - this.ui.cloudflare.show(); - } else { - this.ui.cloudflare_token.prop('required', false); - this.ui.cloudflare.hide(); + 'change @ui.dns_challenge_switch': function () { + const checked = this.ui.dns_challenge_switch.prop('checked'); + if (checked) { + this.ui.dns_provider.prop('required', 'required'); + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ + this.ui.dns_provider_credentials.prop('required', 'required'); + } + this.ui.dns_challenge_content.show(); + } else { + this.ui.dns_provider.prop('required', false); + this.ui.dns_provider_credentials.prop('required', false); + this.ui.dns_challenge_content.hide(); + } + }, + + 'change @ui.dns_provider': function () { + const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; + if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { + this.ui.dns_provider_credentials.prop('required', 'required'); + this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; + this.ui.credentials_file_content.show(); + } else { + this.ui.dns_provider_credentials.prop('required', false); + this.ui.credentials_file_content.hide(); } }, @@ -103,12 +126,13 @@ module.exports = Mn.View.extend({ let data = this.ui.form.serializeJSON(); // Manipulate - data.block_exploits = !!data.block_exploits; - data.preserve_path = !!data.preserve_path; - data.http2_support = !!data.http2_support; - data.hsts_enabled = !!data.hsts_enabled; - data.hsts_subdomains = !!data.hsts_subdomains; - data.ssl_forced = !!data.ssl_forced; + data.block_exploits = !!data.block_exploits; + data.preserve_path = !!data.preserve_path; + data.http2_support = !!data.http2_support; + data.hsts_enabled = !!data.hsts_enabled; + data.hsts_subdomains = !!data.hsts_subdomains; + data.ssl_forced = !!data.ssl_forced; + data.meta.dns_challenge = !!data.meta.dns_challenge; if (typeof data.domain_names === 'string' && data.domain_names) { data.domain_names = data.domain_names.split(','); @@ -117,7 +141,7 @@ module.exports = Mn.View.extend({ // Check for any domain names containing wildcards, which are not allowed with letsencrypt if (data.certificate_id === 'new') { let domain_err = false; - if (!data.meta.cloudflare_use) { + if (!data.meta.dns_challenge) { data.domain_names.map(function (name) { if (name.match(/\*/im)) { domain_err = true; @@ -126,7 +150,7 @@ module.exports = Mn.View.extend({ } if (domain_err) { - alert('Cannot request Let\'s Encrypt Certificate for wildcard domains without CloudFlare DNS.'); + alert(i18n('ssl', 'no-wildcard-without-dns')); return; } @@ -170,7 +194,20 @@ module.exports = Mn.View.extend({ templateContext: { getLetsencryptEmail: function () { return App.Cache.User.get('email'); - } + }, + getUseDnsChallenge: function () { + return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; + }, + getDnsProvider: function () { + return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; + }, + getDnsProviderCredentials: function () { + return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; + }, + getPropagationSeconds: function () { + return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; + }, + dns_plugins: dns_providers, }, onRender: function () { @@ -191,6 +228,8 @@ module.exports = Mn.View.extend({ }); // Certificates + this.ui.dns_challenge_content.hide(); + this.ui.credentials_file_content.hide(); this.ui.letsencrypt.hide(); this.ui.certificate_select.selectize({ valueField: 'id', diff --git a/frontend/js/i18n/messages.json b/frontend/js/i18n/messages.json index d0c9d8e6..8826afa7 100644 --- a/frontend/js/i18n/messages.json +++ b/frontend/js/i18n/messages.json @@ -102,7 +102,15 @@ "letsencrypt-agree": "I Agree to the Let's Encrypt Terms of Service", "delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.", "hosts-warning": "These domains must be already configured to point to this installation", - "use-cloudflare": "Use CloudFlare DNS verification" + "no-wildcard-without-dns": "Cannot request Let's Encrypt Certificate for wildcard domains when not using DNS challenge", + "dns-challenge": "Use a DNS Challenge", + "certbot-warning": "This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective plugins documentation.", + "dns-provider": "DNS Provider", + "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", + "propagation-seconds": "Propagation Seconds", + "propagation-seconds-info": "Leave empty to use the plugins default value. Number of seconds to wait for DNS propagation." }, "proxy-hosts": { "title": "Proxy Hosts", diff --git a/utils/certbot-dns-plugins.js b/utils/certbot-dns-plugins.js new file mode 100644 index 00000000..4af678ec --- /dev/null +++ b/utils/certbot-dns-plugins.js @@ -0,0 +1,160 @@ +/** + * This file contains info about available Certbot DNS plugins. + * + * File Structure: + * + * { + * cloudflare: { + * display_name: "Name displayed to the user", + * package_name: "Package name in PyPi repo", + * package_version: "Package version in PyPi repo", + * 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 + * }, + * ... + * } + * + */ + +module.exports = { + cloudflare: { + display_name: "Cloudflare", + package_name: "certbot-dns-cloudflare", + package_version: "1.8.0", + credentials: `# Cloudflare API token +dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`, + full_plugin_name: "dns-cloudflare", + }, + //####################################################// + cloudxns: { + display_name: "CloudXNS", + package_name: "certbot-dns-cloudxns", + package_version: "1.8.0", + credentials: `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef +dns_cloudxns_secret_key = 1122334455667788`, + full_plugin_name: "dns-cloudxns", + }, + //####################################################// + digitalocean: { + display_name: "DigitalOcean", + package_name: "certbot-dns-digitalocean", + package_version: "1.8.0", + credentials: `dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff`, + full_plugin_name: "dns-digitalocean", + }, + //####################################################// + dnsimple: { + display_name: "DNSimple", + package_name: "certbot-dns-dnsimple", + package_version: "1.8.0", + credentials: `dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`, + full_plugin_name: "dns-dnsimple", + }, + //####################################################// + dnsmadeeasy: { + display_name: "DNS Made Easy", + package_name: "certbot-dns-dnsmadeeasy", + package_version: "1.8.0", + credentials: `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a +dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`, + full_plugin_name: "dns-dnsmadeeasy", + }, + //####################################################// + google: { + display_name: "Google", + package_name: "certbot-dns-google", + package_version: "1.8.0", + credentials: `{ + "type": "service_account", + ... +}`, + full_plugin_name: "dns-google", + }, + //####################################################// + hetzner: { + display_name: "Hetzner", + package_name: "certbot-dns-hetzner", + package_version: "1.0.4", + credentials: `certbot_dns_hetzner:dns_hetzner_api_token = 0123456789abcdef0123456789abcdef`, + full_plugin_name: "certbot-dns-hetzner:dns-hetzner", + }, + //####################################################// + linode: { + display_name: "Linode", + package_name: "certbot-dns-linode", + package_version: "1.8.0", + credentials: `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64 +dns_linode_version = [|3|4]`, + full_plugin_name: "dns-linode", + }, + //####################################################// + luadns: { + display_name: "LuaDNS", + package_name: "certbot-dns-luadns", + package_version: "1.8.0", + credentials: `dns_luadns_email = user@example.com +dns_luadns_token = 0123456789abcdef0123456789abcdef`, + full_plugin_name: "dns-luadns", + }, + //####################################################// + netcup: { + display_name: "netcup", + package_name: "certbot-dns-netcup", + package_version: "1.0.0", + credentials: `certbot_dns_njalla:dns_njalla_token = 0123456789abcdef0123456789abcdef01234567`, + full_plugin_name: "certbot-dns-netcup:dns-netcup", + }, + //####################################################// + njalla: { + display_name: "Njalla", + package_name: "certbot-dns-nsone", + package_version: "0.0.4", + credentials: `certbot_dns_njalla:dns_njalla_token = 0123456789abcdef0123456789abcdef01234567`, + full_plugin_name: "certbot-dns-njalla:dns-njalla", + }, + //####################################################// + nsone: { + display_name: "NS1", + package_name: "certbot-dns-nsone", + package_version: "1.8.0", + credentials: `dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw`, + full_plugin_name: "dns-nsone", + }, + //####################################################// + ovh: { + display_name: "OVH", + package_name: "certbot-dns-ovh", + package_version: "1.8.0", + credentials: `dns_ovh_endpoint = ovh-eu +dns_ovh_application_key = MDAwMDAwMDAwMDAw +dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw +dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`, + full_plugin_name: "dns-ovh", + }, + //####################################################// + rfc2136: { + display_name: "RFC 2136", + package_name: "certbot-dns-rfc2136", + package_version: "1.8.0", + credentials: `# Target DNS server +dns_rfc2136_server = 192.0.2.1 +# Target DNS port +dns_rfc2136_port = 53 +# TSIG key name +dns_rfc2136_name = keyname. +# TSIG key secret +dns_rfc2136_secret = 4q4wM/2I180UXoMyN4INVhJNi8V9BCV+jMw2mXgZw/CSuxUT8C7NKKFs AmKd7ak51vWKgSl12ib86oQRPkpDjg== +# TSIG key algorithm +dns_rfc2136_algorithm = HMAC-SHA512`, + full_plugin_name: "dns-rfc2136", + }, + //####################################################// + route53: { + display_name: "Route 53 (Amazon)", + package_name: "certbot-dns-route53", + package_version: "1.8.0", + credentials: false, + full_plugin_name: "dns-route53", + }, +};