Firewall added & some fixes

This commit is contained in:
MoonDev
2026-04-13 12:40:49 +03:00
parent 7eaa9750b0
commit 8c35022483
22 changed files with 1659 additions and 134 deletions

234
public/firewall.html Normal file
View File

@@ -0,0 +1,234 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Файрвол — AlpineRouter</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<div class="header-left">
<svg class="logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
<h1>AlpineRouter</h1>
</div>
<div class="header-right">
<button class="btn btn-ghost" id="refreshBtn" title="Обновить">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
<path d="M3 3v5h5"/>
</svg>
Обновить
</button>
</div>
</header>
<nav class="tab-nav">
<a href="/" class="tab-link">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
Интерфейсы
</a>
<a href="/dhcp.html" class="tab-link">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
DHCP сервер
</a>
<a href="/clients.html" class="tab-link">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
<circle cx="9" cy="7" r="4"/><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2"/>
<path d="M16 3.13a4 4 0 0 1 0 7.75M21 21v-2a4 4 0 0 0-3-3.87"/>
</svg>
Клиенты
</a>
<a href="/firewall.html" class="tab-link active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
<path d="M9 12l2 2 4-4"/>
</svg>
Файрвол
</a>
<a href="/proxy.html" class="tab-link">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15">
<circle cx="12" cy="12" r="3"/>
<path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/>
</svg>
Прокси
</a>
</nav>
<main class="fw-main">
<div id="notInstalledBanner" class="alert alert-error hidden">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
<circle cx="12" cy="12" r="10"/><path d="M12 8v4m0 4h.01"/>
</svg>
<div>
<strong>nftables не установлен.</strong>
Для работы файрвола выполните на роутере:
<code>apk add nftables</code>
</div>
</div>
<!-- Top controls bar -->
<div class="fw-toolbar">
<div class="fw-toolbar-left">
<label class="toggle-label" id="vlanIsolationLabel" title="Запрещает трафик между VLAN-интерфейсами по умолчанию. Явные правила разрешения выше имеют приоритет.">
<input type="checkbox" id="vlanIsolation">
<span class="toggle-slider"></span>
<span>Изоляция VLAN</span>
</label>
<span class="fw-hint">— теговые VLAN не видят друг друга; NAT — только выход в интернет</span>
</div>
<div class="fw-toolbar-right">
<button class="btn btn-ghost btn-sm" id="addRuleBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M12 5v14M5 12h14"/></svg>
Добавить правило
</button>
<button class="btn btn-primary" id="applyBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M5 12l5 5L20 7"/></svg>
Сохранить и применить
</button>
</div>
</div>
<!-- Rules table -->
<div class="fw-card">
<div class="fw-table-wrap">
<table class="fw-table" id="rulesTable">
<thead>
<tr>
<th class="col-drag"></th>
<th class="col-num">#</th>
<th class="col-en">Вкл</th>
<th class="col-action">Действие</th>
<th class="col-proto">Протокол</th>
<th class="col-iface">Вх. интерфейс</th>
<th class="col-iface">Вых. интерфейс</th>
<th class="col-addr">Источник</th>
<th class="col-addr">Назначение</th>
<th class="col-comment">Комментарий</th>
<th class="col-btns"></th>
</tr>
</thead>
<tbody id="rulesTbody">
<tr id="emptyRow">
<td colspan="11" class="fw-empty">
Правил нет. Нажмите «Добавить правило» для создания.
</td>
</tr>
</tbody>
</table>
</div>
<div class="fw-policy-note">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
<circle cx="12" cy="12" r="10"/><path d="M12 8v4m0 4h.01"/>
</svg>
Политика по умолчанию: <strong>DROP</strong> — весь трафик запрещён, если не разрешён явно выше или через NAT.
Трафик established/related всегда разрешается.
</div>
</div>
</main>
<!-- Rule Edit Modal -->
<div id="ruleModal" class="modal hidden" role="dialog" aria-modal="true">
<div class="modal-backdrop" id="ruleModalBackdrop"></div>
<div class="modal-box modal-wide">
<div class="modal-header">
<h2 id="ruleModalTitle">Добавить правило</h2>
<button class="btn-icon" id="closeRuleModal"></button>
</div>
<form id="ruleForm" autocomplete="off">
<div class="form-row">
<label for="rComment">Комментарий</label>
<input type="text" id="rComment" placeholder="Описание правила (необязательно)">
</div>
<div class="form-row">
<label>Действие</label>
<div class="segmented" id="actionSwitch">
<button type="button" class="seg-btn active" data-val="accept">Разрешить</button>
<button type="button" class="seg-btn" data-val="drop">Запретить</button>
<button type="button" class="seg-btn" data-val="reject">Отклонить</button>
</div>
</div>
<div class="form-row">
<label>Протокол</label>
<div class="segmented" id="protoSwitch">
<button type="button" class="seg-btn active" data-val="all">Любой</button>
<button type="button" class="seg-btn" data-val="tcp">TCP</button>
<button type="button" class="seg-btn" data-val="udp">UDP</button>
<button type="button" class="seg-btn" data-val="icmp">ICMP</button>
</div>
</div>
<div class="form-grid-2">
<div class="form-row">
<label for="rInIface">Входящий интерфейс</label>
<input type="text" id="rInIface" placeholder="eth0, eth0.100 … (пусто = любой)" list="ifaceList">
</div>
<div class="form-row">
<label for="rOutIface">Исходящий интерфейс</label>
<input type="text" id="rOutIface" placeholder="eth1 … (пусто = любой)" list="ifaceList">
</div>
</div>
<div class="form-grid-2">
<div class="form-row">
<label for="rSrcAddr">Источник (адрес/подсеть)</label>
<input type="text" id="rSrcAddr" placeholder="192.168.1.0/24 или 10.0.0.5">
</div>
<div class="form-row">
<label for="rDstAddr">Назначение (адрес/подсеть)</label>
<input type="text" id="rDstAddr" placeholder="10.0.0.0/24 или 8.8.8.8">
</div>
</div>
<div class="form-grid-2" id="portFields">
<div class="form-row">
<label for="rSrcPort">Порт источника</label>
<input type="text" id="rSrcPort" placeholder="80 или 1000-2000">
</div>
<div class="form-row">
<label for="rDstPort">Порт назначения</label>
<input type="text" id="rDstPort" placeholder="443 или 8080-8090">
</div>
</div>
<div class="form-row">
<label class="checkbox-label">
<input type="checkbox" id="rEnabled" checked>
<span>Правило активно</span>
</label>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-ghost" id="cancelRuleBtn">Отмена</button>
<button class="btn btn-primary" id="saveRuleBtn">Сохранить</button>
</div>
</div>
</div>
<datalist id="ifaceList"></datalist>
<div id="toast" class="toast hidden"></div>
<script src="firewall.js"></script>
</body>
</html>