diff --git a/src/backend/index.js b/src/backend/index.js
index 0de363a..5a29476 100644
--- a/src/backend/index.js
+++ b/src/backend/index.js
@@ -43,6 +43,6 @@ function appStart () {
try {
appStart();
} catch (err) {
- logger.error(err.message);
+ logger.error(err.message, err);
process.exit(1);
}
diff --git a/src/backend/internal/certificate.js b/src/backend/internal/certificate.js
index ffa0b42..42d9a71 100644
--- a/src/backend/internal/certificate.js
+++ b/src/backend/internal/certificate.js
@@ -1,10 +1,13 @@
'use strict';
+const fs = require('fs');
const _ = require('lodash');
const error = require('../lib/error');
const certificateModel = require('../models/certificate');
const internalAuditLog = require('./audit-log');
const internalHost = require('./host');
+const tempWrite = require('temp-write');
+const utils = require('../lib/utils');
function omissions () {
return ['is_deleted'];
@@ -200,7 +203,8 @@ const internalCertificate = {
},
/**
- * Validates that the certs provided are good
+ * Validates that the certs provided are good.
+ * This is probably a horrible way to do this.
*
* @param {Access} access
* @param {Object} data
@@ -208,7 +212,8 @@ const internalCertificate = {
* @returns {Promise}
*/
validate: (access, data) => {
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
+ // Put file contents into an object
let files = {};
_.map(data.files, (file, name) => {
if (internalHost.allowed_ssl_files.indexOf(name) !== -1) {
@@ -219,12 +224,62 @@ const internalCertificate = {
resolve(files);
})
.then(files => {
+ // For each file, create a temp file and write the contents to it
+ // Then test it depending on the file type
+ let promises = [];
+ _.map(files, (content, type) => {
+ promises.push(tempWrite(content, '/tmp')
+ .then(filepath => {
+ if (type === 'certificate_key') {
+ return utils.exec('openssl rsa -in ' + filepath + ' -check')
+ .then(result => {
+ return {tmp: filepath, result: result.split("\n").shift()};
+ }).catch(err => {
+ return {tmp: filepath, result: false, err: new error.ValidationError('Certificate Key is not valid')};
+ });
- // TODO: validate using openssl
- // files.certificate
- // files.certificate_key
+ } else if (type === 'certificate') {
+ return utils.exec('openssl x509 -in ' + filepath + ' -text -noout')
+ .then(result => {
+ return {tmp: filepath, result: result};
+ }).catch(err => {
+ return {tmp: filepath, result: false, err: new error.ValidationError('Certificate is not valid')};
+ });
+ } else {
+ return {tmp: filepath, result: false};
+ }
+ })
+ .then(file_result => {
+ // Remove temp files
+ fs.unlinkSync(file_result.tmp);
+ delete file_result.tmp;
- return true;
+ return {[type]: file_result};
+ })
+ );
+ });
+
+ // With the results, delete the temp files for security mainly.
+ // If there was an error with any of them, wait until we've done the deleting
+ // before throwing it.
+ return Promise.all(promises)
+ .then(files => {
+ let data = {};
+ let err = null;
+
+ _.each(files, file => {
+ data = _.assign({}, data, file);
+ if (typeof file.err !== 'undefined' && file.err) {
+ err = file.err;
+ }
+ });
+
+ if (err) {
+ throw err;
+ }
+
+ return data;
+ });
});
},
diff --git a/src/backend/internal/host.js b/src/backend/internal/host.js
index c314c8b..b0f3bd1 100644
--- a/src/backend/internal/host.js
+++ b/src/backend/internal/host.js
@@ -8,7 +8,7 @@ const deadHostModel = require('../models/dead_host');
const internalHost = {
- allowed_ssl_files: ['certificate', 'certificate_key'],
+ allowed_ssl_files: ['certificate', 'certificate_key', 'intermediate_certificate'],
/**
* Internal use only, checks to see if the domain is already taken by any other record
diff --git a/src/backend/routes/api/nginx/certificates.js b/src/backend/routes/api/nginx/certificates.js
index a6328a2..2525ea7 100644
--- a/src/backend/routes/api/nginx/certificates.js
+++ b/src/backend/routes/api/nginx/certificates.js
@@ -163,7 +163,7 @@ router
* POST /api/nginx/certificates/123/upload
*
* Upload certificates
- */validate
+ */
.post((req, res, next) => {
if (!req.files) {
res.status(400)
diff --git a/src/frontend/js/app/api.js b/src/frontend/js/app/api.js
index 4f27144..76ec5c9 100644
--- a/src/frontend/js/app/api.js
+++ b/src/frontend/js/app/api.js
@@ -535,6 +535,14 @@ module.exports = {
*/
upload: function (id, form_data) {
return FileUpload('nginx/certificates/' + id + '/upload', form_data);
+ },
+
+ /**
+ * @param {FormData} form_data
+ * @params {Promise}
+ */
+ validate: function (form_data) {
+ return FileUpload('nginx/certificates/validate', form_data);
}
}
},
diff --git a/src/frontend/js/app/nginx/certificates/form.ejs b/src/frontend/js/app/nginx/certificates/form.ejs
index ef20f69..32edb6b 100644
--- a/src/frontend/js/app/nginx/certificates/form.ejs
+++ b/src/frontend/js/app/nginx/certificates/form.ejs
@@ -39,22 +39,32 @@
+
+
<% } %>
diff --git a/src/frontend/js/app/nginx/certificates/form.js b/src/frontend/js/app/nginx/certificates/form.js
index 7b5fa2b..9a7fa54 100644
--- a/src/frontend/js/app/nginx/certificates/form.js
+++ b/src/frontend/js/app/nginx/certificates/form.js
@@ -15,13 +15,14 @@ module.exports = Mn.View.extend({
max_file_size: 5120,
ui: {
- form: 'form',
- domain_names: 'input[name="domain_names"]',
- buttons: '.modal-footer button',
- cancel: 'button.cancel',
- save: 'button.save',
- other_ssl_certificate: '#other_ssl_certificate',
- other_ssl_certificate_key: '#other_ssl_certificate_key'
+ form: 'form',
+ domain_names: 'input[name="domain_names"]',
+ buttons: '.modal-footer button',
+ cancel: 'button.cancel',
+ save: 'button.save',
+ other_certificate: '#other_certificate',
+ other_certificate_key: '#other_certificate_key',
+ other_intermediate_certificate: '#other_intermediate_certificate'
},
events: {
@@ -33,8 +34,8 @@ module.exports = Mn.View.extend({
return;
}
- let view = this;
- let data = this.ui.form.serializeJSON();
+ let view = this;
+ let data = this.ui.form.serializeJSON();
data.provider = this.model.get('provider');
// Manipulate
@@ -46,55 +47,66 @@ module.exports = Mn.View.extend({
data.domain_names = data.domain_names.split(',');
}
- let method = App.Api.Nginx.Certificates.create;
- let is_new = true;
let ssl_files = [];
- if (this.model.get('id')) {
- // edit
- is_new = false;
- method = App.Api.Nginx.Certificates.update;
- data.id = this.model.get('id');
- }
-
// check files are attached
if (this.model.get('provider') === 'other' && !this.model.hasSslFiles()) {
- if (!this.ui.other_ssl_certificate[0].files.length || !this.ui.other_ssl_certificate[0].files[0].size) {
- alert('certificate file is not attached');
+ if (!this.ui.other_certificate[0].files.length || !this.ui.other_certificate[0].files[0].size) {
+ alert('Certificate file is not attached');
return;
} else {
- if (this.ui.other_ssl_certificate[0].files[0].size > this.max_file_size) {
- alert('certificate file is too large (> 5kb)');
+ if (this.ui.other_certificate[0].files[0].size > this.max_file_size) {
+ alert('Certificate file is too large (> 5kb)');
return;
}
- ssl_files.push({name: 'certificate', file: this.ui.other_ssl_certificate[0].files[0]});
+ ssl_files.push({name: 'certificate', file: this.ui.other_certificate[0].files[0]});
}
- if (!this.ui.other_ssl_certificate_key[0].files.length || !this.ui.other_ssl_certificate_key[0].files[0].size) {
- alert('certificate key file is not attached');
+ if (!this.ui.other_certificate_key[0].files.length || !this.ui.other_certificate_key[0].files[0].size) {
+ alert('Certificate key file is not attached');
return;
} else {
- if (this.ui.other_ssl_certificate_key[0].files[0].size > this.max_file_size) {
- alert('certificate key file is too large (> 5kb)');
+ if (this.ui.other_certificate_key[0].files[0].size > this.max_file_size) {
+ alert('Certificate key file is too large (> 5kb)');
return;
}
- ssl_files.push({name: 'certificate_key', file: this.ui.other_ssl_certificate_key[0].files[0]});
+ ssl_files.push({name: 'certificate_key', file: this.ui.other_certificate_key[0].files[0]});
+ }
+
+ if (this.ui.other_intermediate_certificate[0].files.length && this.ui.other_intermediate_certificate[0].files[0].size) {
+ if (this.ui.other_intermediate_certificate[0].files[0].size > this.max_file_size) {
+ alert('Intermediate Certificate file is too large (> 5kb)');
+ return;
+ }
+ ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]});
}
}
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
- method(data)
+
+ // compile file data
+ let form_data = new FormData();
+ if (view.model.get('provider') && ssl_files.length) {
+ ssl_files.map(function (file) {
+ form_data.append(file.name, file.file);
+ });
+ }
+
+ new Promise(resolve => {
+ if (view.model.get('provider') === 'other') {
+ resolve(App.Api.Nginx.Certificates.validate(form_data));
+ } else {
+ resolve();
+ }
+ })
+ .then(() => {
+ return App.Api.Nginx.Certificates.create(data);
+ })
.then(result => {
view.model.set(result);
// Now upload the certs if we need to
- if (ssl_files.length) {
- let form_data = new FormData();
-
- ssl_files.map(function (file) {
- form_data.append(file.name, file.file);
- });
-
+ if (view.model.get('provider') === 'other') {
return App.Api.Nginx.Certificates.upload(view.model.get('id'), form_data)
.then(result => {
view.model.set('meta', _.assign({}, view.model.get('meta'), result));
@@ -103,9 +115,7 @@ module.exports = Mn.View.extend({
})
.then(() => {
App.UI.closeModal(function () {
- if (is_new) {
- App.Controller.showNginxCertificates();
- }
+ App.Controller.showNginxCertificates();
});
})
.catch(err => {
diff --git a/src/frontend/js/i18n/messages.json b/src/frontend/js/i18n/messages.json
index 2b2bb3a..232b716 100644
--- a/src/frontend/js/i18n/messages.json
+++ b/src/frontend/js/i18n/messages.json
@@ -66,8 +66,6 @@
"force-ssl": "Force SSL",
"domain-names": "Domain Names",
"cert-provider": "Certificate Provider",
- "other-certificate": "Certificate",
- "other-certificate-key": "Certificate Key",
"block-exploits": "Block Common Exploits",
"caching-enabled": "Cache Assets"
},
@@ -141,7 +139,10 @@
"delete": "Delete SSL Certificate",
"delete-confirm": "Are you sure you want to delete this SSL Certificate? Any hosts using it will need to be updated later.",
"help-title": "SSL Certificates",
- "help-content": "TODO"
+ "help-content": "TODO",
+ "other-certificate": "Certificate",
+ "other-certificate-key": "Certificate Key",
+ "other-intermediate-certificate": "Intermediate Certificate"
},
"access-lists": {
"title": "Access Lists",