183 строки
6.7 KiB
HTML
183 строки
6.7 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Сеть — {{ app_name }}</title>
|
|
<link rel="stylesheet" href="/static/app.css">
|
|
</head>
|
|
<body>
|
|
<div class="shell">
|
|
<header class="topbar">
|
|
<div>
|
|
<div class="brand">Сеть</div>
|
|
<div class="muted">NAT Router для VPS и проброс портов к VM</div>
|
|
</div>
|
|
<div class="top-meta">
|
|
<span>user: {{ user }}</span>
|
|
<a class="link-pill accent" href="/vm/create">+ Создать VM</a>
|
|
<a class="link-pill" href="/">← Дашборд</a>
|
|
<form method="post" action="/logout" class="logout-form"><button class="ghost">Выйти</button></form>
|
|
</div>
|
|
</header>
|
|
|
|
{% if error %}
|
|
<div class="alert danger">{{ error }}</div>
|
|
{% endif %}
|
|
|
|
<section class="grid stats">
|
|
<article class="card stat">
|
|
<div class="label">NAT network</div>
|
|
<div class="value small">{{ ctx.nat.name }}</div>
|
|
<span class="status {{ 'ok' if ctx.nat.exists else 'warn' }}">{{ 'ready' if ctx.nat.exists else 'not created' }}</span>
|
|
</article>
|
|
<article class="card stat">
|
|
<div class="label">Subnet</div>
|
|
<div class="value small">{{ ctx.nat.subnet }}</div>
|
|
</article>
|
|
<article class="card stat">
|
|
<div class="label">External interface</div>
|
|
<div class="value small">{{ ctx.external_interface }}</div>
|
|
</article>
|
|
<article class="card stat">
|
|
<div class="label">IPv4 forward</div>
|
|
<div class="value small">{{ ctx.ip_forward }}</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="grid two">
|
|
<article class="card">
|
|
<div class="card-head">
|
|
<h2>Virtuality NAT Router</h2>
|
|
<span class="pill">{{ ctx.nat.bridge }}</span>
|
|
</div>
|
|
<div class="meta-grid">
|
|
<div><span>Gateway</span><strong>{{ ctx.nat.gateway }}</strong></div>
|
|
<div><span>DHCP</span><strong>{{ ctx.nat.dhcp }}</strong></div>
|
|
<div><span>Bridge</span><strong>{{ ctx.nat.bridge }}</strong></div>
|
|
</div>
|
|
<form method="post" action="/network/nat/setup" class="inline-form top-space">
|
|
<button class="primary">Создать / починить NAT-сеть</button>
|
|
</form>
|
|
<pre>{{ ctx.nat.info }}</pre>
|
|
</article>
|
|
|
|
<article class="card">
|
|
<div class="card-head">
|
|
<h2>DHCP leases</h2>
|
|
<span class="pill">virsh net-dhcp-leases</span>
|
|
</div>
|
|
<pre>{{ ctx.nat.leases }}</pre>
|
|
<div class="notice top-space">Если IP VM не виден — внутри гостевой системы нужен qemu-guest-agent или задай статический IP в диапазоне 192.168.100.x.</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="grid two">
|
|
<article class="card">
|
|
<div class="card-head">
|
|
<h2>Добавить проброс порта</h2>
|
|
<span class="pill">DNAT</span>
|
|
</div>
|
|
<form method="post" action="/network/forward/add" class="form-grid">
|
|
<label>
|
|
<span>VM</span>
|
|
<select name="vm_name" required>
|
|
{% for vm in vms %}
|
|
<option value="{{ vm.name }}">{{ vm.name }} — {{ vm.state }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</label>
|
|
<label>
|
|
<span>Внутренний IP VM</span>
|
|
<input type="text" name="guest_ip" placeholder="192.168.100.50" required>
|
|
</label>
|
|
<div class="form-row">
|
|
<label>
|
|
<span>Внешний порт VPS</span>
|
|
<input type="number" name="external_port" min="1" max="65535" placeholder="2222" required>
|
|
</label>
|
|
<label>
|
|
<span>Порт внутри VM</span>
|
|
<input type="number" name="guest_port" min="1" max="65535" placeholder="22" required>
|
|
</label>
|
|
</div>
|
|
<div class="form-row">
|
|
<label>
|
|
<span>Протокол</span>
|
|
<select name="protocol">
|
|
<option value="tcp">tcp</option>
|
|
<option value="udp">udp</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
<span>Заметка</span>
|
|
<input type="text" name="note" placeholder="SSH / HTTP / RDP">
|
|
</label>
|
|
</div>
|
|
<button class="primary wide">Добавить и применить</button>
|
|
</form>
|
|
</article>
|
|
|
|
<article class="card">
|
|
<div class="card-head">
|
|
<h2>Libvirt networks</h2>
|
|
<span class="pill">virsh net-list --all</span>
|
|
</div>
|
|
<table>
|
|
<thead><tr><th>Name</th><th>State</th><th>Autostart</th></tr></thead>
|
|
<tbody>
|
|
{% for net in ctx.networks %}
|
|
<tr>
|
|
<td>{{ net.name }}</td>
|
|
<td><span class="status {{ 'ok' if net.state == 'active' else 'warn' }}">{{ net.state }}</span></td>
|
|
<td>{{ net.autostart }}</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr><td colspan="3" class="muted">Libvirt-сети не найдены</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<div class="card-head">
|
|
<h2>Пробросы портов</h2>
|
|
<form method="post" action="/network/apply"><button>Применить правила</button></form>
|
|
</div>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Внешний порт</th><th>VM</th><th>Внутренний адрес</th><th>Протокол</th><th>Заметка</th><th></th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in ctx.forwards %}
|
|
<tr>
|
|
<td class="strong">{{ item.external_port }} → {{ item.guest_port }}</td>
|
|
<td>{{ item.vm_name }}</td>
|
|
<td>{{ item.guest_ip }}</td>
|
|
<td><span class="status ok">{{ item.protocol }}</span></td>
|
|
<td>{{ item.note }}</td>
|
|
<td>
|
|
<form method="post" action="/network/forward/{{ item.id }}/delete">
|
|
<button class="danger">Удалить</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr><td colspan="6" class="muted">Пробросов пока нет</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<div class="card-head">
|
|
<h2>nftables preview</h2>
|
|
<span class="pill">/etc/virtuality/nftables/virtuality.nft</span>
|
|
</div>
|
|
<pre>{{ ctx.nft_rules }}</pre>
|
|
</section>
|
|
</div>
|
|
</body>
|
|
</html>
|