Initial commit: SSL proxy / firewall appliance

Flask WebUI behind nginx reverse proxy with zone-based firewall, DHCP,
WireGuard, and ACME certificate management.
This commit is contained in:
2026-05-07 22:24:24 +00:00
commit e2f56b8cc8
56 changed files with 10013 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
{% extends "base.html" %}
{% block title %}Interfaces - Vacuum Wall{% endblock %}
{% block content %}
<div class="page-header">
<div>
<h1>Interfaces</h1>
<div class="subtitle">Network interface to zone bindings</div>
</div>
</div>
<div class="card">
<table>
<thead>
<tr>
<th>Interface</th>
<th>MAC Address</th>
<th>IP Address</th>
<th>State</th>
<th>Zone</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for iface in (interfaces or []) %}
<tr>
<td><strong>{{ iface.get('name', 'unknown') }}</strong></td>
<td class="text-muted">{{ iface.get('mac', 'N/A') }}</td>
<td>
{% for ip in iface.get('ips', []) %}
{{ ip }}{% if not loop.last %}, {% endif %}
{% endfor %}
{% if not iface.get('ips') %}N/A{% endif %}
</td>
<td>
<span class="status-dot {{ 'status-up' if iface.get('state') == 'up' else 'status-down' }}"></span>
{{ 'Up' if iface.get('state') == 'up' else 'Down' }}
</td>
<td>
{% if zones %}
<select
class="htmx-on-success"
data-success="Zone updated for {{ iface.name }}"
hx-post="/api/firewall/zones/__ZONE__/interfaces/{{ iface.name }}"
hx-swap="none"
hx-select-oob="#toast-container *"
onchange="assignInterfaceToZone(this, '{{ iface.name }}', '{{ iface.get('zone', '') }}')"
>
{% for zone in zones %}
<option value="{{ zone.get('name', '') }}" {% if zone.get('name') == iface.get('zone') %}selected{% endif %}>{{ zone.get('name', '') }}</option>
{% endfor %}
</select>
{% else %}
<span class="text-muted">No zones configured</span>
{% endif %}
</td>
<td>
<button class="btn btn-sm btn-outline" onclick="assignInterfaceToZone(this.previousElementSibling, '{{ iface.name }}', null)">Apply</button>
</td>
</tr>
{% endfor %}
{% if not (interfaces or []) %}
<tr>
<td colspan="6" class="text-muted text-sm">No interfaces found</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
<script>
function assignInterfaceToZone(selectEl, ifaceName, currentZone) {
var zoneName = selectEl.value;
var url = '/api/firewall/zones/' + encodeURIComponent(zoneName) + '/interfaces/' + encodeURIComponent(ifaceName);
fetch(url, { method: 'POST' })
.then(function(res) {
if (res.ok) {
showToast('Assigned ' + ifaceName + ' to zone ' + zoneName, 'success');
} else {
return res.text().then(function(txt) { throw new Error(txt); });
}
})
.catch(function(err) {
showToast('Failed to assign: ' + err.message, 'error');
});
}
</script>
{% endblock %}