diff --git a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/Api/MaintenanceController.php b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/Api/MaintenanceController.php index ce3977eca..c727e6017 100644 --- a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/Api/MaintenanceController.php +++ b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/Api/MaintenanceController.php @@ -47,6 +47,9 @@ class MaintenanceController extends ApiControllerBase */ public function searchCertificateDiffAction() { + $backend = new Backend(); + $backend->configdRun('template reload OPNsense/HAProxy'); + return $this->getData( ["cert_diff_list"], ["rowCount", "current", "searchPhrase", "sort"] @@ -59,6 +62,9 @@ class MaintenanceController extends ApiControllerBase */ public function searchServerAction() { + $backend = new Backend(); + $backend->configdRun('template reload OPNsense/HAProxy'); + return $this->getData( ["server_status_list"], ["rowCount", "current", "searchPhrase", "sort"] @@ -71,18 +77,38 @@ class MaintenanceController extends ApiControllerBase */ public function certSyncAction() { + $backend = new Backend(); + $backend->configdRun('template reload OPNsense/HAProxy'); + return $this->syncCerts( ["cert_sync"], ["frontend_ids"] ); } + /** + * sync certificate for frontends + * @return array|mixed + */ + public function certSyncBulkAction() + { + $backend = new Backend(); + $backend->configdRun('template reload OPNsense/HAProxy'); + + return $this->syncCerts( + ["cert_sync_bulk"] + ); + } + /** * show certificate diff for frontends * @return array|mixed */ public function certDiffAction() { + $backend = new Backend(); + $backend->configdRun('template reload OPNsense/HAProxy'); + return $this->getData( ["cert_diff"], ["frontend_ids"] @@ -95,6 +121,9 @@ class MaintenanceController extends ApiControllerBase */ public function certActionsAction() { + $backend = new Backend(); + $backend->configdRun('template reload OPNsense/HAProxy'); + return $this->getData( ["cert_actions"], ["frontend_ids"] diff --git a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt index 7422f22a6..a70204a4d 100644 --- a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt +++ b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt @@ -48,8 +48,10 @@ POSSIBILITY OF SUCH DAMAGE. message += `{{ lang._('REMOVE:') }} ${remove.cert}:
`; message += `
${remove.messages.join("
")}
`; }); + message += `
`; }); + message += `{{ lang._('CERTIFICATES:') }}

`; deleted.forEach(function(del) { message += `{{ lang._('DELETE:') }} ${del.cert}:
`; message += `
${del.messages.join("
")}
`; @@ -58,6 +60,53 @@ POSSIBILITY OF SUCH DAMAGE. return message; } + function showDiffDialog(payload) { + $.post('/api/haproxy/maintenance/certDiff', payload, function(data) { + BootstrapDialog.show({ + type: BootstrapDialog.TYPE_INFO, + title: "{{ lang._('Diff between configured and remote ssl certificates') }}", + message: `
${data}
`, + buttons: [{ + label: '{{ lang._('Close') }}', + action: function(dialog){ + dialog.close(); + } + }] + }); + }); + } + + function applyDiffDialog(payload, requested_count) { + $.post('/api/haproxy/maintenance/certActions', payload, function(data_actions) { + question = '' + question += `
${data_actions}
`; + question += '{{ lang._('Apply ssl certificates to HaProxy?') }}

'; + + stdDialogConfirm('{{ lang._('Confirmation Required') }}', + question, + '{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() { + $.post('/api/haproxy/maintenance/certSync', payload, function(data) { + modified_count = data.result.add_count + data.result.remove_count + data.result.update_count; + + if (requested_count != modified_count) { + var error_msg = syncErrorMessage(data.result.modified, data.result.deleted); + BootstrapDialog.show({ + type: BootstrapDialog.TYPE_DANGER, + title: "{{ lang._('Error applying ssl certificates to HAProxy') }}", + message: error_msg, + buttons: [{ + label: '{{ lang._('Close') }}', + action: function(dialog){ + dialog.close(); + } + }] + }); + } + $("#grid-certificates").bootgrid("reload"); + }); + }); + }); + } $("#grid-certificates").bootgrid('destroy'); var grid_certificates = $("#grid-certificates").UIBootgrid({ @@ -88,21 +137,7 @@ POSSIBILITY OF SUCH DAMAGE. var payload = { 'frontend_ids': frontend_ids, }; - - $.post('/api/haproxy/maintenance/certDiff', payload, function(data) { - BootstrapDialog.show({ - type: BootstrapDialog.TYPE_INFO, - title: "{{ lang._('Diff between configured and remote ssl certificates') }}", - message: `
${data}
`, - buttons: [{ - label: '{{ lang._('Close') }}', - action: function(dialog){ - dialog.close(); - } - }] - }); - }); - + showDiffDialog(payload); }); grid_certificates.find("*[data-action=applyDiff]").off().on("click", function(e) { @@ -111,57 +146,76 @@ POSSIBILITY OF SUCH DAMAGE. var row = rows.filter(function(row) { return row.id == row_id; })[0]; - var requested_count = row.total_count; var frontend_ids = row.id var payload = { 'frontend_ids': frontend_ids, }; - $.post('/api/haproxy/maintenance/certActions', payload, function(data_actions) { - question = '' - question += `
${data_actions}
`; - question += '{{ lang._('Apply ssl certificates to HaProxy?') }}

'; - - stdDialogConfirm('{{ lang._('Confirmation Required') }}', - question, - '{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() { - $.post('/api/haproxy/maintenance/certSync', payload, function(data) { - modified_count = data.result.add_count + data.result.remove_count + data.result.update_count; - if (requested_count != modified_count) { - var error_msg = syncErrorMessage(data.result.modified, data.result.deleted); - BootstrapDialog.show({ - type: BootstrapDialog.TYPE_DANGER, - title: "{{ lang._('Error applying ssl certificates to HAProxy') }}", - message: error_msg, - buttons: [{ - label: '{{ lang._('Close') }}', - action: function(dialog){ - dialog.close(); - } - }] - }); - } - $("#grid-certificates").bootgrid("reload"); - }); - }); - }); - + applyDiffDialog(payload, requested_count); }); grid_certificates.find("*[data-action=showDiffBulk]").off().on("click", function(e) { var rows = $("#grid-certificates").bootgrid("getSelectedRows"); - console.log('Show diff for multi') + var payload = { + 'frontend_ids': rows.join() + }; + if (rows != undefined && rows.length > 0) { + showDiffDialog(payload); + } }); grid_certificates.find("*[data-action=applyDiffBulk]").off().on("click", function(e) { var rows = $("#grid-certificates").bootgrid("getSelectedRows"); - console.log('Apply diff for multi') + var frontend_ids = rows.join(); + var all_rows = $("#grid-certificates").bootgrid("getCurrentRows"); + var requested_count = 0; + all_rows.forEach(function(row) { + if (rows.indexOf(row.id) != -1) { + requested_count = requested_count + row.total_count; + } + }); + var payload = { + 'frontend_ids': frontend_ids, + }; + if (rows != undefined && rows.length > 0) { + applyDiffDialog(payload, requested_count); + } }); - - }); + // Apply all changes + $("*[data-action=applyDiffAll]").off().on("click", function(e) { + $('[id*="applyDiffAll_progress"]').each(function(){ + $(this).addClass("fa fa-spinner fa-pulse"); + }); + var all_rows = $("#grid-certificates").bootgrid("getCurrentRows"); + var requested_count = 0; + all_rows.forEach(function(row) { + requested_count = requested_count + row.total_count; + }); + + var payload = {}; + $.post('/api/haproxy/maintenance/certSyncBulk', payload, function(data) { + modified_count = data.result.add_count + data.result.remove_count + data.result.update_count; + if (requested_count != modified_count) { + var error_msg = syncErrorMessage(data.result.modified, data.result.deleted); + BootstrapDialog.show({ + type: BootstrapDialog.TYPE_DANGER, + title: "{{ lang._('Error applying ssl certificates to HAProxy') }}", + message: error_msg, + buttons: [{ + label: '{{ lang._('Close') }}', + action: function(dialog){ + dialog.close(); + } + }] + }); + } + $("#grid-certificates").bootgrid("reload"); + $("#applyDiffAll_progress").removeClass("fa fa-spinner fa-pulse"); + }); + }); // grid-status $("#grid-status").bootgrid('destroy'); @@ -436,12 +490,18 @@ POSSIBILITY OF SUCH DAMAGE. - - + + +
+
+ +
+
+
diff --git a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/syncCerts.py b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/syncCerts.py index 31484670e..41df2cbba 100755 --- a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/syncCerts.py +++ b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/syncCerts.py @@ -170,22 +170,24 @@ class Diff(SyncWithTarget): print(json.dumps(self.status)) if self.output_format == 'raw': - print("## STATUS ##") for frontend_id, crt_list in self.status.items(): - print(f"CRT_LIST: {crt_list['path']}") - print(f" FRONTEND NAME: {crt_list['frontend_name']}") - print(f" FRONTEND ID: {frontend_id}") - print(f" LOCAL CERTS: {crt_list['local_certs']}") - print(f" REMOTE CERTS: {crt_list['remote_certs']}") - print(f" LOCAL DEFAULT: {crt_list['local_default']}") - print(f" REMOTE DEFAULT: {crt_list['remote_default']}") - + print(f"FRONTEND NAME: {crt_list['frontend_name']}") + print(f" CONFIG:") for cert_id, cert in crt_list['certs'].items(): - print() - print(f" CERT: {cert['path']}") - print(f" LOCAL: {cert['local']}") - print(f" REMOTE: {cert['remote']}") - print() + if cert['path'] == crt_list['local_default']: + print(f" CERT (Default):") + else: + print(f" CERT:") + print(f" Serial: {cert['local']['Serial']}") + print(f" Issuer: {cert['local']['Issuer']}") + print(f" Subject: {cert['local']['Subject']}") + print(f" ACTIVE:") + for cert in crt_list['remote_certs']: + meta = self._execute_remote_cmd(cmds.showSslCert, certfile=cert.split(":")[0]) + print(f" CERT:") + print(f" Serial: {meta['Serial']}") + print(f" Issuer: {meta['Issuer']}") + print(f" Subject: {meta['Subject']}") def show_actions(self): """ Shows what will be synced to target """ @@ -198,18 +200,22 @@ class Diff(SyncWithTarget): if self.output_format == 'raw': for diff in self.diff: print(f"FRONTEND: {diff['frontend_name']}") + + print(f" CRT-LIST: {diff['path']}") for update in diff['update']: print(f" CERT NEW / UPDATE:") + print(f" Cert: {update['certfile']}") print(f" Serial: {update['meta'].get('Serial', None)}") print(f" Issuer: {update['meta'].get('Issuer', None)}") print(f" Subject: {update['meta'].get('Subject', None)}") print() else: if not diff['update']: - print(f" CERT UPDATE: []") + print(f" CERT NEW / UPDATE: []") for add in diff['add']: print(f" CERT ADD:") + print(f" Cert: {add['certfile']}") print(f" Serial: {add['meta'].get('Serial', None)}") print(f" Issuer: {add['meta'].get('Issuer', None)}") print(f" Subject: {add['meta'].get('Subject', None)}") @@ -220,6 +226,7 @@ class Diff(SyncWithTarget): for remove in diff['remove']: print(f" CERT REMOVE:") + print(f" Cert: {remove['certfile']}") print(f" Serial: {remove['meta'].get('Serial', None)}") print(f" Issuer: {remove['meta'].get('Issuer', None)}") print(f" Subject: {remove['meta'].get('Subject', None)}") @@ -227,6 +234,7 @@ class Diff(SyncWithTarget): else: if not diff['remove']: print(f" CERT REMOVE: []") + print() def show_transactions(self): @@ -308,11 +316,10 @@ class Diff(SyncWithTarget): 'messages': messages }) - if "Success!" in output: + if "deleted in crtlist" in output: sync['remove_count'] += 1 sync_item['remove_count'] += 1 - #modified_items = sync_item['update_count'] + sync_item['add_count'] + sync_item['remove_count'] sync['modified'].append(sync_item) # delete unused certs operation - haproxy does not allow to delete certs in use diff --git a/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf b/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf index 7d2630221..bb0ab73f6 100644 --- a/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf +++ b/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf @@ -97,7 +97,7 @@ type:script_output message:Show diff between configured ssl certificates and certs from HAProxy memory for multiple frontends [cert_sync] -command:/usr/local/opnsense/scripts/OPNsense/HAProxy/syncCerts.py +command:configctl template reload OPNsense/HAProxy 2 > /dev/null; /usr/local/opnsense/scripts/OPNsense/HAProxy/syncCerts.py parameters: sync --frontend-ids %s --output json type:script_output message:Sync ssl certificates into HAProxy memory for multiple frontends @@ -109,7 +109,7 @@ type:script_output message:Show diff between configured ssl certificates and certs from HAProxy memory for all frontends [cert_sync_bulk] -command:/usr/local/opnsense/scripts/OPNsense/HAProxy/syncCerts.py sync --output json +command:configctl template reload OPNsense/HAProxy 2 > /dev/null; /usr/local/opnsense/scripts/OPNsense/HAProxy/syncCerts.py sync --output json parameters: type:script_output message:Sync ssl certificates into HAProxy memory for all frontends