Skip to content

Commit e0b9da7

Browse files
committed
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.
1 parent a132763 commit e0b9da7

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

backend/internal/access-list.js

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const internalAccessList = {
8686
// re-fetch with expansions
8787
return internalAccessList.get(access, {
8888
id: data.id,
89-
expand: ['owner', 'items', 'clients', 'clientcas', 'proxy_hosts.access_list.[clientcas.certificate,clients,items]']
89+
expand: ['owner', 'items', 'clients', 'clientcas.certificate', 'proxy_hosts.access_list.[clientcas,clients,items]']
9090
}, true /* <- skip masking */);
9191
})
9292
.then((row) => {
@@ -261,10 +261,11 @@ const internalAccessList = {
261261
// re-fetch with expansions
262262
return internalAccessList.get(access, {
263263
id: data.id,
264-
expand: ['owner', 'items', 'clients', 'clientcas', 'proxy_hosts.[certificate,access_list.[clientcas.certificate,clients,items]]']
264+
expand: ['owner', 'items', 'clients', 'clientcas.certificate', 'proxy_hosts.[certificate,access_list.[clientcas.certificate,clients,items]]']
265265
}, true /* <- skip masking */);
266266
})
267267
.then((row) => {
268+
console.log(row);
268269
return internalAccessList.build(row)
269270
.then(() => {
270271
if (row.proxy_host_count) {
@@ -299,7 +300,7 @@ const internalAccessList = {
299300
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
300301
.where('access_list.is_deleted', 0)
301302
.andWhere('access_list.id', data.id)
302-
.withGraphFetched('[owner,items,clients,clientcas,proxy_hosts.[certificate,access_list.[clientcas.certificate,clients,items]]]')
303+
.allowGraph('[owner,items,clients,clientcas.certificate,proxy_hosts.[certificate,access_list.[clientcas.certificate,clients,items]]]')
303304
.first();
304305

305306
if (access_data.permission_visibility !== 'all') {
@@ -420,7 +421,7 @@ const internalAccessList = {
420421
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
421422
.where('access_list.is_deleted', 0)
422423
.groupBy('access_list.id')
423-
.withGraphFetched('[owner,items,clients,clientcas.certificate]')
424+
.allowGraph('[owner,items,clients,clientcas.certificate]')
424425
.orderBy('access_list.name', 'ASC');
425426

426427
if (access_data.permission_visibility !== 'all') {
@@ -508,17 +509,27 @@ const internalAccessList = {
508509
return '/data/access/' + list.id;
509510
},
510511

512+
/**
513+
* @param {Object} list
514+
* @param {Integer} list.id
515+
* @returns {String}
516+
*/
517+
getClientCAFilename: (list) => {
518+
return '/data/clientca/' + list.id;
519+
},
520+
511521
/**
512522
* @param {Object} list
513523
* @param {Integer} list.id
514524
* @param {String} list.name
515525
* @param {Array} list.items
526+
* @param {Array} list.clientcas
516527
* @returns {Promise}
517528
*/
518529
build: (list) => {
519-
logger.info('Building Access file #' + list.id + ' for: ' + list.name);
520530

521-
return new Promise((resolve, reject) => {
531+
const htPasswdBuild = new Promise((resolve, reject) => {
532+
logger.info('Building Access file #' + list.id + ' for: ' + list.name);
522533
let htpasswd_file = internalAccessList.getFilename(list);
523534

524535
// 1. remove any existing access file
@@ -566,6 +577,39 @@ const internalAccessList = {
566577
});
567578
}
568579
});
580+
581+
const caCertificateBuild = new Promise((resolve, reject) => {
582+
// TODO: we need to ensure this rebuild is run if any certificates change
583+
logger.info('Building Client CA file #' + list.id + ' for: ' + list.name);
584+
let clientca_file = internalAccessList.getClientCAFilename(list);
585+
586+
const certificate_bodies = list.clientcas
587+
.filter((clientca) => {
588+
return clientca.certificate.meta === undefined;
589+
})
590+
.map((clientca) => {
591+
return clientca.certificate.meta.certificate;
592+
});
593+
594+
// Unlink the original file (nginx retains file handle till reload)
595+
try {
596+
fs.unlinkSync(clientca_file);
597+
} catch (err) {
598+
// do nothing
599+
}
600+
601+
// Write the new file in one shot
602+
try {
603+
fs.writeFileSync(clientca_file, certificate_bodies.join('\n'), {encoding: 'utf8'});
604+
logger.success('Built Client CA file #' + list.id + ' for: ' + list.name);
605+
resolve(clientca_file);
606+
} catch (err) {
607+
reject(err);
608+
}
609+
});
610+
611+
// Execute both promises concurrently
612+
return Promise.all([htPasswdBuild, caCertificateBuild]);
569613
}
570614
};
571615

docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mkdir -p \
2020
/data/custom_ssl \
2121
/data/logs \
2222
/data/access \
23+
/data/clientca \
2324
/data/nginx/default_host \
2425
/data/nginx/default_www \
2526
/data/nginx/proxy_host \

frontend/js/app/nginx/access/main.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ module.exports = Mn.View.extend({
7373
e.preventDefault();
7474
let query = this.ui.query.val();
7575

76-
this.fetch(['owner', 'items', 'clients', 'clientcas'], query)
76+
this.fetch(['owner', 'items', 'clients', 'clientcas.certificate'], query)
7777
.then(response => this.showData(response))
7878
.catch(err => {
7979
this.showError(err);
@@ -88,7 +88,7 @@ module.exports = Mn.View.extend({
8888
onRender: function () {
8989
let view = this;
9090

91-
view.fetch(['owner', 'items', 'clients', 'clientcas'])
91+
view.fetch(['owner', 'items', 'clients', 'clientcas.certificate'])
9292
.then(response => {
9393
if (!view.isDestroyed()) {
9494
if (response && response.length) {

0 commit comments

Comments
 (0)