125 lines
5.9 KiB
HTML
125 lines
5.9 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Порты · {{.Agent.Name}} - LPRA</title>
|
|
<link rel="stylesheet" href="/static/app.css">
|
|
<style>
|
|
.frow { display:flex; align-items:flex-end; gap:14px; flex-wrap:wrap; }
|
|
.frow label { margin:0 0 6px; }
|
|
.frow .fld { display:flex; flex-direction:column; }
|
|
.frow select, .frow input, .frow button {
|
|
height:40px; box-sizing:border-box; font-size:14px; line-height:1; margin:0;
|
|
}
|
|
label.check { display:inline-flex; align-items:center; gap:8px; margin:14px 0 0; color:var(--text); font-size:14px; cursor:pointer; }
|
|
.frow select {
|
|
padding:0 32px 0 12px; border-radius:8px; border:1px solid var(--border);
|
|
background:#0c0f13; color:var(--text);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header class="topbar">
|
|
<div class="brand">LPRA · Порты - {{.Agent.Name}} <span class="muted">({{.Agent.Hostname}})</span></div>
|
|
<nav>
|
|
<a href="/">← Машины</a>
|
|
<a href="/agents/{{.Agent.ID}}/terminal">Терминал</a>
|
|
<a href="/agents/{{.Agent.ID}}/files">Файлы</a>
|
|
<a href="/agents/{{.Agent.ID}}/status">Статус</a>
|
|
<a href="/agents/{{.Agent.ID}}/edit">Изменить</a>
|
|
</nav>
|
|
</header>
|
|
<div class="wrap">
|
|
<h1>Проброс портов</h1>
|
|
<p class="muted">Порт сервиса на машине-клиенте становится доступен на порту хоста.
|
|
Клиент за NAT - трафик идёт исходящим подключением агента.</p>
|
|
{{if .Error}}<div class="err">{{.Error}}</div>{{end}}
|
|
|
|
<div class="card">
|
|
<h2>Правила</h2>
|
|
{{if .Rules}}
|
|
<table>
|
|
<thead><tr><th>Тип</th><th>Хост (порт)</th><th>→ Клиент (порт)</th><th>Слушать на</th><th></th></tr></thead>
|
|
<tbody>
|
|
{{range .Rules}}
|
|
<tr>
|
|
<td><code>{{.Proto}}</code></td>
|
|
<td class="muted">:{{.HostPort}}</td>
|
|
<td class="muted">127.0.0.1:{{.ClientPort}}</td>
|
|
<td class="muted">{{.ListenAddr}}{{if and .NoUfw $.UfwEnabled}} · без ufw{{end}}</td>
|
|
<td style="text-align:right">
|
|
<form method="post" action="/forwards/{{.ID}}/delete" style="display:inline"
|
|
onsubmit="return confirm('Удалить правило?')">
|
|
<input type="hidden" name="csrf" value="{{$.CSRF}}">
|
|
<button class="btn danger small" type="submit">Удалить</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<p class="muted">Правил пока нет.</p>
|
|
{{end}}
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Новое правило</h2>
|
|
<form method="post" action="/agents/{{.Agent.ID}}/forwards/add">
|
|
<input type="hidden" name="csrf" value="{{.CSRF}}">
|
|
<div class="frow">
|
|
<div class="fld">
|
|
<label>Тип</label>
|
|
<select name="proto" id="proto">
|
|
<option value="http">http (long-poll, работает везде)</option>
|
|
<option value="tcp">tcp (прямой, низкая задержка, порт {{.TunnelPort}})</option>
|
|
<option value="udp">udp (прямой, порт {{.TunnelPort}})</option>
|
|
</select>
|
|
</div>
|
|
<div class="fld">
|
|
<label>Порт на клиенте</label>
|
|
<input type="text" name="client_port" placeholder="напр. 22" style="max-width:140px" required>
|
|
</div>
|
|
<div class="fld">
|
|
<label>Порт на хосте</label>
|
|
<input type="text" name="host_port" placeholder="напр. 2222" style="max-width:140px" required>
|
|
</div>
|
|
<div class="fld">
|
|
<label>Слушать на хосте</label>
|
|
<input type="text" name="listen_addr" value="0.0.0.0" style="max-width:160px">
|
|
</div>
|
|
<button type="submit">Создать</button>
|
|
</div>
|
|
{{if .UfwEnabled}}
|
|
<label class="check" title="Порт не будет открыт в ufw, и при удалении правила в ufw ничего не изменится">
|
|
<input type="checkbox" name="no_ufw" value="1"> Без ufw правила
|
|
</label>
|
|
{{end}}
|
|
</form>
|
|
<p class="muted" id="note" style="font-size:13px; margin-top:12px"></p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Режимы</h2>
|
|
<ul class="muted" style="font-size:14px; line-height:1.7">
|
|
<li><b>http</b> - туннель поверх HTTP long-poll (через адрес панели, порт {{.PanelPort}}). Работает везде, где доступна панель, но задержка выше.</li>
|
|
<li><b>tcp</b> - прямой TCP-мультиплексор через порт <b>{{.TunnelPort}}</b> хоста. Минимальная задержка, keep-alive, TCP_NODELAY. Нужно открыть {{.TunnelPort}}/tcp на хосте.</li>
|
|
<li><b>udp</b> - прямой UDP через порт <b>{{.TunnelPort}}</b> хоста. Нужно открыть {{.TunnelPort}}/udp.</li>
|
|
</ul>
|
|
<p class="muted" style="font-size:13px">Для tcp/udp агент держит исходящее соединение к <code>host:{{.TunnelPort}}</code>; несколько клиентов и портов мультиплексируются на нём.</p>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
var notes = {
|
|
http: 'Трафик идёт через порт панели {{.PanelPort}} (long-poll). Порт {{.TunnelPort}} не требуется.',
|
|
tcp: 'Требуется открытый порт {{.TunnelPort}}/tcp на хосте. Самая низкая задержка.',
|
|
udp: 'Требуется открытый порт {{.TunnelPort}}/udp на хосте.'
|
|
};
|
|
var sel = document.getElementById('proto'), note = document.getElementById('note');
|
|
function upd(){ note.textContent = notes[sel.value] || ''; }
|
|
sel.onchange = upd; upd();
|
|
</script>
|
|
</body>
|
|
</html>
|