Add enable/disable toggle for the global firewall allow/block lists

New firewall.DisableGlobal() removes the wireguard_ui_global nftables
table without touching stored IP list entries, and firewall.IsGlobalEnabled()
reports whether it's currently loaded. New GET /firewall-lists/status and
POST /firewall-lists/disable endpoints (admin-only), plus a status badge
and "Toggle enable/disable" button on the Global Firewall Lists page -
one click to turn the whole thing off without losing the list contents,
and back on again (re-applies the current ruleset).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-07-12 20:52:02 +02:00
co-authored by Claude Sonnet 5
parent 00d084a188
commit 5a7709bc6e
4 changed files with 119 additions and 0 deletions
+65
View File
@@ -21,6 +21,9 @@ Global Firewall Lists
<div class="card card-warning">
<div class="card-header">
<h3 class="card-title">Host-wide Allow / Block Lists</h3>
<div class="card-tools">
<span id="_global_fw_status" class="badge">checking...</span>
</div>
</div>
<div class="card-body">
<p class="text-muted">
@@ -29,6 +32,7 @@ Global Firewall Lists
firewall table (nftables priority -10). Allow entries always win over block entries.
Nothing is applied until you press "Apply now (live)".
</p>
<button type="button" class="btn btn-outline-secondary btn-sm mb-2" id="btn_toggle_global_firewall">Toggle enable/disable</button>
<table class="table table-sm" id="_iplist_table">
<thead>
@@ -126,9 +130,69 @@ Global Firewall Lists
});
}
function refreshGlobalStatus() {
$.ajax({
cache: false,
method: 'GET',
url: '{{.basePath}}/firewall-lists/status',
dataType: 'json',
success: function (data) {
const badge = $("#_global_fw_status");
if (data.enabled) {
badge.removeClass('badge-secondary').addClass('badge-success').text('ENABLED');
} else {
badge.removeClass('badge-success').addClass('badge-secondary').text('DISABLED');
}
},
error: function () {
$("#_global_fw_status").removeClass('badge-success').addClass('badge-secondary').text('unknown');
}
});
}
$(document).ready(function () {
loadIPList();
refreshGlobalPreview();
refreshGlobalStatus();
$("#btn_toggle_global_firewall").click(function () {
$.ajax({
cache: false,
method: 'GET',
url: '{{.basePath}}/firewall-lists/status',
dataType: 'json',
success: function (data) {
if (data.enabled) {
if (!confirm("Disable the global allow/block list firewall now?\nStored entries are kept, only the live nftables table is removed.")) return;
$.ajax({
method: 'POST', url: '{{.basePath}}/firewall-lists/disable',
dataType: 'json', contentType: "application/json",
success: function (r) { toastr.success(r.message); refreshGlobalStatus(); },
error: function (jqXHR) {
const rj = jQuery.parseJSON(jqXHR.responseText);
toastr.error(rj['message'] || "Failed to disable");
}
});
} else {
if (!confirm("Enable the global allow/block list firewall now?\nThis applies the current ruleset live via 'nft -f'.")) return;
$.ajax({
method: 'POST', url: '{{.basePath}}/firewall-lists/apply',
dataType: 'json', contentType: "application/json",
success: function (r) {
toastr.success(r.message);
if (r.output) { $("#_iplist_preview_text").text(r.output); }
refreshGlobalStatus();
},
error: function (jqXHR) {
const rj = jQuery.parseJSON(jqXHR.responseText);
toastr.error(rj['message'] || "Failed to enable");
if (rj['output']) { $("#_iplist_preview_text").text(rj['output']); }
}
});
}
}
});
});
$("#frm_iplist_entry").on('submit', function (e) {
e.preventDefault();
@@ -226,6 +290,7 @@ Global Firewall Lists
success: function (data) {
toastr.success(data.message);
if (data.output) { $("#_iplist_preview_text").text(data.output); }
refreshGlobalStatus();
},
error: function (jqXHR) {
const responseJson = jQuery.parseJSON(jqXHR.responseText);