mirror of
https://github.com/xiaoxinpro/nginx-proxy-manager-zh.git
synced 2025-01-22 21:08:13 -05:00
Custom SSL Validation endpoint
This commit is contained in:
parent
1b68869e6b
commit
c8592503e3
@ -43,6 +43,6 @@ function appStart () {
|
||||
try {
|
||||
appStart();
|
||||
} catch (err) {
|
||||
logger.error(err.message);
|
||||
logger.error(err.message, err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
@ -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;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -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
|
||||
|
@ -163,7 +163,7 @@ router
|
||||
* POST /api/nginx/certificates/123/upload
|
||||
*
|
||||
* Upload certificates
|
||||
*/validate
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
if (!req.files) {
|
||||
res.status(400)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -39,22 +39,32 @@
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 other-ssl">
|
||||
<div class="form-group">
|
||||
<div class="form-label"><%- i18n('all-hosts', 'other-certificate') %></div>
|
||||
<div class="form-label"><%- i18n('certificates', 'other-certificate-key') %><span class="form-required">*</span></div>
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" name="meta[other_ssl_certificate]" id="other_ssl_certificate" required>
|
||||
<input type="file" class="custom-file-input" name="meta[other_certificate_key]" id="other_certificate_key" required>
|
||||
<label class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 other-ssl">
|
||||
<div class="form-group">
|
||||
<div class="form-label"><%- i18n('all-hosts', 'other-certificate-key') %></div>
|
||||
<div class="form-label"><%- i18n('certificates', 'other-certificate') %><span class="form-required">*</span></div>
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" name="meta[other_ssl_certificate_key]" id="other_ssl_certificate_key" required>
|
||||
<input type="file" class="custom-file-input" name="meta[other_certificate]" id="other_certificate">
|
||||
<label class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 other-ssl">
|
||||
<div class="form-group">
|
||||
<div class="form-label"><%- i18n('certificates', 'other-intermediate-certificate') %></div>
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" name="meta[other_intermediate_certificate]" id="other_intermediate_certificate">
|
||||
<label class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% } %>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -20,8 +20,9 @@ module.exports = Mn.View.extend({
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
other_ssl_certificate: '#other_ssl_certificate',
|
||||
other_ssl_certificate_key: '#other_ssl_certificate_key'
|
||||
other_certificate: '#other_certificate',
|
||||
other_certificate_key: '#other_certificate_key',
|
||||
other_intermediate_certificate: '#other_intermediate_certificate'
|
||||
},
|
||||
|
||||
events: {
|
||||
@ -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();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user