2026-04-13 12:40:49 +03:00
<!DOCTYPE html>
< html lang = "ru" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2026-04-15 11:38:26 +03:00
< title > Файрвол — NanoRouter< / title >
2026-04-13 12:40:49 +03:00
< 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 >
2026-04-15 11:38:26 +03:00
< h1 > NanoRouter< / h1 >
2026-04-13 12:40:49 +03:00
< / div >
< div class = "header-right" >
< / div >
< / header >
< nav class = "tab-nav" >
2026-04-15 11:38:26 +03:00
< a href = "/home.html" class = "tab-link" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" width = "15" height = "15" >
< path d = "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" / >
< polyline points = "9 22 9 12 15 12 15 22" / >
< / svg >
Главная
< / a >
< a href = "/ifaces.html" class = "tab-link" >
2026-04-13 12:40:49 +03:00
< 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 >
2026-04-15 11:38:26 +03:00
< a href = "/profile.html" class = "tab-link" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" width = "15" height = "15" >
< path d = "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" / >
< circle cx = "12" cy = "7" r = "4" / >
< / svg >
Профиль
< / a >
2026-04-13 12:40:49 +03:00
< / 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 >