Add storing for Client CA certificates in the database

Add initial support for managing Client Certificate Authority public
certificates as certificate objects in the database. The new provider
type 'clientca' is defined to implement this.
This commit is contained in:
Will Rouesnel
2023-05-25 00:21:32 +10:00
parent b19a272403
commit c664e864ce
6 changed files with 58 additions and 16 deletions

View File

@ -552,13 +552,18 @@ const internalCertificate = {
})
.then(() => {
return new Promise((resolve, reject) => {
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
if (certificate.provider === 'clientca') {
// Client CAs have no private key associated, so just succeed.
resolve();
} else {
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
}
});
});
},
@ -639,7 +644,7 @@ const internalCertificate = {
upload: (access, data) => {
return internalCertificate.get(access, {id: data.id})
.then((row) => {
if (row.provider !== 'other') {
if (row.provider !== 'other' && row.provider !== 'clientca') {
throw new error.ValidationError('Cannot upload certificates for this type of provider');
}

View File

@ -219,7 +219,7 @@
},
"ssl_provider": {
"type": "string",
"pattern": "^(letsencrypt|other)$"
"pattern": "^(letsencrypt|other|clientca)$"
},
"http2_support": {
"description": "HTTP2 Protocol Support",

View File

@ -173,7 +173,23 @@
</div>
</div>
</div>
<% } else if (provider === 'clientca') { %>
<!-- Client Certificate Authority -->
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label"><%- i18n('str', 'name') %> <span class="form-required">*</span></label>
<input name="nice_name" type="text" class="form-control" placeholder="" value="<%- nice_name %>" required>
</div>
</div>
<div class="col-sm-12 col-md-12 other-ssl">
<div class="form-group">
<div class="form-label"><%- i18n('certificates', 'clientca-certificate') %><span class="form-required">*</span></div>
<div class="custom-file">
<input type="file" class="custom-file-input" name="meta[clientca_certificate]" id="clientca_certificate">
<label id="clientca_certificate_label" class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
</div>
</div>
</div>
<% } %>
</div>
</form>

View File

@ -45,7 +45,9 @@ module.exports = Mn.View.extend({
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'
other_intermediate_certificate_label: '#other_intermediate_certificate_label',
clientca_certificate: '#clientca_certificate',
clientca_certificate_label: '#clientca_certificate_label'
},
events: {
@ -156,6 +158,18 @@ module.exports = Mn.View.extend({
}
ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]});
}
} else if (data.provider === 'clientca' && !this.model.hasSslFiles()) {
// check files are attached
if (!this.ui.clientca_certificate[0].files.length || !this.ui.clientca_certificate[0].files[0].size) {
alert('Certificate file is not attached');
return;
} else {
if (this.ui.clientca_certificate[0].files[0].size > this.max_file_size) {
alert('Certificate file is too large (> 100kb)');
return;
}
ssl_files.push({name: 'certificate', file: this.ui.clientca_certificate[0].files[0]});
}
}
this.ui.loader_content.show();
@ -163,14 +177,14 @@ module.exports = Mn.View.extend({
// compile file data
let form_data = new FormData();
if (data.provider === 'other' && ssl_files.length) {
if ((data.provider === 'other' || data.provider === 'clientca') && ssl_files.length) {
ssl_files.map(function (file) {
form_data.append(file.name, file.file);
});
}
new Promise(resolve => {
if (data.provider === 'other') {
if (data.provider === 'other' || data.provider === 'clientca') {
resolve(App.Api.Nginx.Certificates.validate(form_data));
} else {
resolve();
@ -183,7 +197,7 @@ module.exports = Mn.View.extend({
this.model.set(result);
// Now upload the certs if we need to
if (data.provider === 'other') {
if (data.provider === 'other' || data.provider === 'clientca') {
return App.Api.Nginx.Certificates.upload(this.model.get('id'), form_data)
.then(result => {
this.model.set('meta', _.assign({}, this.model.get('meta'), result));
@ -234,6 +248,9 @@ module.exports = Mn.View.extend({
},
'change @ui.other_intermediate_certificate': function(e){
this.setFileName("other_intermediate_certificate_label", e)
},
'change @ui.clientca_certificate': function(e){
this.setFileName("clientca_certificate_label", e)
}
},
setFileName(ui, e){

View File

@ -20,6 +20,7 @@
<div class="dropdown-menu">
<a class="dropdown-item add-item" data-cert="letsencrypt" href="#"><%- i18n('ssl', 'letsencrypt') %></a>
<a class="dropdown-item add-item" data-cert="other" href="#"><%- i18n('ssl', 'other') %></a>
<a class="dropdown-item add-item" data-cert="clientca" href="#"><%- i18n('ssl', 'clientca') %></a>
</div>
</div>
<% } %>

View File

@ -99,6 +99,7 @@
"ssl": {
"letsencrypt": "Let's Encrypt",
"other": "Custom",
"clientca": "Client Certificate Authority",
"none": "HTTP only",
"letsencrypt-email": "Email Address for Let's Encrypt",
"letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>",
@ -185,7 +186,7 @@
"title": "SSL Certificates",
"empty": "There are no SSL Certificates",
"add": "Add SSL Certificate",
"form-title": "Add {provider, select, letsencrypt{Let's Encrypt} other{Custom}} Certificate",
"form-title": "Add {provider, select, letsencrypt{Let's Encrypt Certificate} other{Custom Certificate} clientca{Client Certificate Authority}}",
"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",
@ -193,6 +194,7 @@
"other-certificate": "Certificate",
"other-certificate-key": "Certificate Key",
"other-intermediate-certificate": "Intermediate Certificate",
"clientca-certificate": "Certificate",
"force-renew": "Renew Now",
"test-reachability": "Test Server Reachability",
"reachability-title": "Test Server Reachability",
@ -231,7 +233,8 @@
"pass-auth": "Pass Auth to Host",
"access-add": "Add",
"auth-add": "Add",
"search": "Search Access…"
"search": "Search Access…",
"client-certificates": "Client Certificates"
},
"users": {
"title": "Users",