mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-07-17 15:04:34 +00:00
Add support for writing client CAs when access-lists are updated
This commit adds the basic support necessary to produce the combined client CA files when certificates are updated.
This commit is contained in:
@ -86,7 +86,7 @@ const internalAccessList = {
|
|||||||
// re-fetch with expansions
|
// re-fetch with expansions
|
||||||
return internalAccessList.get(access, {
|
return internalAccessList.get(access, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
expand: ['owner', 'items', 'clients', 'clientcas', 'proxy_hosts.access_list.[clientcas.certificate,clients,items]']
|
expand: ['owner', 'items', 'clients', 'clientcas.certificate', 'proxy_hosts.access_list.[clientcas,clients,items]']
|
||||||
}, true /* <- skip masking */);
|
}, true /* <- skip masking */);
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
@ -247,7 +247,6 @@ const internalAccessList = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(internalNginx.reload)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
return internalAuditLog.add(access, {
|
return internalAuditLog.add(access, {
|
||||||
@ -261,10 +260,11 @@ const internalAccessList = {
|
|||||||
// re-fetch with expansions
|
// re-fetch with expansions
|
||||||
return internalAccessList.get(access, {
|
return internalAccessList.get(access, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
expand: ['owner', 'items', 'clients', 'clientcas', 'proxy_hosts.[certificate,access_list.[clientcas.certificate,clients,items]]']
|
expand: ['owner', 'items', 'clients', 'clientcas.certificate', 'proxy_hosts.[certificate,access_list.[clientcas,clients,items]]']
|
||||||
}, true /* <- skip masking */);
|
}, true /* <- skip masking */);
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
|
console.log(row);
|
||||||
return internalAccessList.build(row)
|
return internalAccessList.build(row)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (row.proxy_host_count) {
|
if (row.proxy_host_count) {
|
||||||
@ -274,6 +274,11 @@ const internalAccessList = {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
return internalAccessList.maskItems(row);
|
return internalAccessList.maskItems(row);
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
return internalNginx.reload().then(() => {
|
||||||
|
return row;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -299,7 +304,7 @@ const internalAccessList = {
|
|||||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.andWhere('access_list.id', data.id)
|
.andWhere('access_list.id', data.id)
|
||||||
.withGraphFetched('[owner,items,clients,clientcas,proxy_hosts.[certificate,access_list.[clientcas.certificate,clients,items]]]')
|
.allowGraph('[owner,items,clients,clientcas.certificate,proxy_hosts.[certificate,access_list.[clientcas,clients,items]]]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
@ -420,7 +425,7 @@ const internalAccessList = {
|
|||||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.groupBy('access_list.id')
|
.groupBy('access_list.id')
|
||||||
.withGraphFetched('[owner,items,clients,clientcas.certificate]')
|
.allowGraph('[owner,items,clients,clientcas.certificate]')
|
||||||
.orderBy('access_list.name', 'ASC');
|
.orderBy('access_list.name', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
@ -477,6 +482,8 @@ const internalAccessList = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Mask sensitive items in access list responses
|
||||||
|
*
|
||||||
* @param {Object} list
|
* @param {Object} list
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
@ -496,6 +503,24 @@ const internalAccessList = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mask certificates in clientcas responses
|
||||||
|
if (list && typeof list.clientcas !== 'undefined') {
|
||||||
|
list.clientcas.map(function(val, idx) {
|
||||||
|
if (typeof val.certificate !== 'undefined') {
|
||||||
|
list.clientcas[idx].certificate.meta = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask certificates in ProxyHost responses (clear the meta field)
|
||||||
|
if (list && typeof list.proxy_hosts !== 'undefined') {
|
||||||
|
list.proxy_hosts.map(function(val, idx) {
|
||||||
|
if (typeof val.certificate !== 'undefined') {
|
||||||
|
list.proxy_hosts[idx].certificate.meta = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -508,17 +533,27 @@ const internalAccessList = {
|
|||||||
return '/data/access/' + list.id;
|
return '/data/access/' + list.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} list
|
||||||
|
* @param {Integer} list.id
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
getClientCAFilename: (list) => {
|
||||||
|
return '/data/clientca/' + list.id;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} list
|
* @param {Object} list
|
||||||
* @param {Integer} list.id
|
* @param {Integer} list.id
|
||||||
* @param {String} list.name
|
* @param {String} list.name
|
||||||
* @param {Array} list.items
|
* @param {Array} list.items
|
||||||
|
* @param {Array} list.clientcas
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
build: (list) => {
|
build: (list) => {
|
||||||
logger.info('Building Access file #' + list.id + ' for: ' + list.name);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
const htPasswdBuild = new Promise((resolve, reject) => {
|
||||||
|
logger.info('Building Access file #' + list.id + ' for: ' + list.name);
|
||||||
let htpasswd_file = internalAccessList.getFilename(list);
|
let htpasswd_file = internalAccessList.getFilename(list);
|
||||||
|
|
||||||
// 1. remove any existing access file
|
// 1. remove any existing access file
|
||||||
@ -566,6 +601,39 @@ const internalAccessList = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const caCertificateBuild = new Promise((resolve, reject) => {
|
||||||
|
// TODO: we need to ensure this rebuild is run if any certificates change
|
||||||
|
logger.info('Building Client CA file #' + list.id + ' for: ' + list.name);
|
||||||
|
let clientca_file = internalAccessList.getClientCAFilename(list);
|
||||||
|
|
||||||
|
const certificate_bodies = list.clientcas
|
||||||
|
.filter((clientca) => {
|
||||||
|
return typeof clientca.certificate.meta !== 'undefined';
|
||||||
|
})
|
||||||
|
.map((clientca) => {
|
||||||
|
return clientca.certificate.meta.certificate;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Unlink the original file (nginx retains file handle till reload)
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(clientca_file);
|
||||||
|
} catch (err) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the new file in one shot
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(clientca_file, certificate_bodies.join('\n'), {encoding: 'utf8'});
|
||||||
|
logger.success('Built Client CA file #' + list.id + ' for: ' + list.name);
|
||||||
|
resolve(clientca_file);
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute both promises concurrently
|
||||||
|
return Promise.all([htPasswdBuild, caCertificateBuild]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ mkdir -p \
|
|||||||
/data/custom_ssl \
|
/data/custom_ssl \
|
||||||
/data/logs \
|
/data/logs \
|
||||||
/data/access \
|
/data/access \
|
||||||
|
/data/clientca \
|
||||||
/data/nginx/default_host \
|
/data/nginx/default_host \
|
||||||
/data/nginx/default_www \
|
/data/nginx/default_www \
|
||||||
/data/nginx/proxy_host \
|
/data/nginx/proxy_host \
|
||||||
|
@ -73,7 +73,7 @@ module.exports = Mn.View.extend({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let query = this.ui.query.val();
|
let query = this.ui.query.val();
|
||||||
|
|
||||||
this.fetch(['owner', 'items', 'clients', 'clientcas'], query)
|
this.fetch(['owner', 'items', 'clients', 'clientcas.certificate'], query)
|
||||||
.then(response => this.showData(response))
|
.then(response => this.showData(response))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.showError(err);
|
this.showError(err);
|
||||||
@ -88,7 +88,7 @@ module.exports = Mn.View.extend({
|
|||||||
onRender: function () {
|
onRender: function () {
|
||||||
let view = this;
|
let view = this;
|
||||||
|
|
||||||
view.fetch(['owner', 'items', 'clients', 'clientcas'])
|
view.fetch(['owner', 'items', 'clients', 'clientcas.certificate'])
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!view.isDestroyed()) {
|
if (!view.isDestroyed()) {
|
||||||
if (response && response.length) {
|
if (response && response.length) {
|
||||||
|
Reference in New Issue
Block a user