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:
@@ -0,0 +1,127 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}WireGuard - Vacuum Wall{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1>WireGuard</h1>
|
||||
<div class="subtitle">VPN tunnel management</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn {{ 'btn-outline' if (wg_status is defined and wg_status.get('state') == 'up') else 'btn-primary' }}"
|
||||
hx-post="/api/wireguard/down"
|
||||
hx-swap="none"
|
||||
class="htmx-on-success"
|
||||
data-success="Tunnel stopped"
|
||||
onsuccess="setTimeout(function(){ location.reload(); }, 500);">
|
||||
Stop Tunnel
|
||||
</button>
|
||||
<button class="btn {{ 'btn-outline' if (wg_status is not defined or wg_status.get('state') != 'up') else 'btn-primary' }}"
|
||||
hx-post="/api/wireguard/up"
|
||||
hx-swap="none"
|
||||
class="htmx-on-success"
|
||||
data-success="Tunnel started"
|
||||
onsuccess="setTimeout(function(){ location.reload(); }, 500);">
|
||||
Start Tunnel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tunnel Status -->
|
||||
<div class="card mb-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3>
|
||||
<span class="status-dot {{ 'status-up' if (wg_status is defined and wg_status.get('state') == 'up') else 'status-down' }}"></span>
|
||||
Tunnel State: <strong>{{ 'UP' if (wg_status is defined and wg_status.get('state') == 'up') else 'DOWN' }}</strong>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="text-sm text-muted">
|
||||
{% if config %}
|
||||
Listen Port: <strong>{{ config.get('listen_port', 'N/A') }}</strong> |
|
||||
Public Key: <strong>{{ config.get('public_key', 'N/A')[:12] if config.get('public_key') else 'N/A' }}...</strong> |
|
||||
Address: <strong>{{ config.get('address', 'N/A') }}</strong>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Peer Form -->
|
||||
<div class="section-title">Peers</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<h3>Add Peer</h3>
|
||||
<form hx-post="/api/wireguard/peers" hx-swap="none" class="htmx-on-success" data-success="Peer added" onsuccess="setTimeout(function(){ location.reload(); }, 300);">
|
||||
<div class="inline-form">
|
||||
<div class="form-group">
|
||||
<label for="peer-name">Name</label>
|
||||
<input type="text" id="peer-name" name="name" placeholder="client-1" required style="width:140px;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="peer-pubkey">Public Key</label>
|
||||
<input type="text" id="peer-pubkey" name="public_key" placeholder="Base64 public key (48 chars)" required style="width:260px;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="peer-allowed">Allowed IPs</label>
|
||||
<input type="text" id="peer-allowed" name="allowed_ips" placeholder="10.8.0.2/32" value="10.8.0.{% set next = (peers|length + 2) %}{{ next }}/32" required style="width:160px;">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Add Peer</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Peers Table -->
|
||||
<div class="card">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Public Key</th>
|
||||
<th>Allowed IPs</th>
|
||||
<th>Endpoint</th>
|
||||
<th>Latest Handshake</th>
|
||||
<th>Transfer</th>
|
||||
<th style="width:160px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for peer in (peers or []) %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="status-dot {{ 'status-up' if peer.get('latest_handshake') else 'status-down' }}"></span>
|
||||
<strong>{{ peer.get('name', 'unnamed') }}</strong>
|
||||
</td>
|
||||
<td style="font-family:monospace;font-size:11px;">{{ peer.get('public_key', 'N/A')[:20] }}...</td>
|
||||
<td class="text-sm">{{ peer.get('allowed_ips', '-') }}</td>
|
||||
<td class="text-sm">{{ peer.get('endpoint', '-') }}</td>
|
||||
<td class="text-sm">{{ peer.get('latest_handshake', 'Never') or 'Never' }}</td>
|
||||
<td class="text-sm">
|
||||
<div>Recv: {{ peer.get('transfer_recv', '0') or '0' }}</div>
|
||||
<div>Sent: {{ peer.get('transfer_sent', '0') or '0' }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-sm btn-outline" onclick="downloadPeerConfig('{{ peer.get('name', '') }}')">Config</button>
|
||||
<form hx-delete="/api/wireguard/peers/{{ peer.get('name', '') | urlencode }}" hx-swap="none" class="htmx-on-success" data-success="Peer removed" onsuccess="setTimeout(function(){ location.reload(); }, 300);">
|
||||
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Remove peer {{ peer.name }}?')">Remove</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if not (peers or []) %}
|
||||
<tr>
|
||||
<td colspan="7" class="text-muted text-sm">No peers configured. Add a peer above.</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function downloadPeerConfig(peerName) {
|
||||
var url = '/api/wireguard/peers/' + encodeURIComponent(peerName) + '/config';
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user