2026-05-04 00:00:36 +03:00
{{ define "admin/security.html" }}
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< title > WarpBox Admin Security</ title >
< link rel = "icon" type = "image/png" href = "/static/WarpBoxLogo.png" >
< link rel = "stylesheet" href = "/static/css/app.css" >
< link rel = "stylesheet" href = "/static/css/window.css" >
< link rel = "stylesheet" href = "/static/css/components/buttons.css" >
< link rel = "stylesheet" href = "/static/css/components/toast.css" >
< link rel = "stylesheet" href = "/static/css/admin.css" >
< link rel = "stylesheet" href = "/static/css/security.css" >
</ head >
< body >
< div class = "admin-shell" >
< div class = "admin-frame" >
{{ template "admin/header.html" . }}
< div class = "win98-window admin-workspace-window" role = "main" >
< div class = "win98-titlebar" >
< div class = "win98-titlebar-label" >
< img class = "win98-titlebar-icon" src = "/static/WarpBoxLogo.png" alt = "" aria-hidden = "true" >
< h1 > WarpBox Security</ h1 >
</ div >
< div class = "win98-window-controls" aria-hidden = "true" >
< button class = "win98-control" type = "button" > _</ button >
< button class = "win98-control" type = "button" > □</ button >
< button class = "win98-control" type = "button" > x</ button >
</ div >
</ div >
< nav class = "menu-bar" aria-label = "Security toolbar" >
< div class = "menu-item" >
< button class = "menu-button" type = "button" aria-expanded = "false" > Security</ button >
< div class = "menu-popup" >
< button class = "menu-action" type = "button" data-command = "ban-ip" >< span > B</ span >< span > Ban IP now</ span >< span ></ span ></ button >
< button class = "menu-action" type = "button" data-command = "ban-until" >< span > T</ span >< span > Set ban expiration</ span >< span ></ span ></ button >
< button class = "menu-action" type = "button" data-command = "unban-ip" >< span > U</ span >< span > Unban selected IP</ span >< span ></ span ></ button >
< button class = "menu-action" type = "button" data-command = "bulk-unban" >< span > K</ span >< span > Bulk unban selected</ span >< span ></ span ></ button >
< button class = "menu-action" type = "button" data-command = "unban-all" >< span > A</ span >< span > Unban all</ span >< span ></ span ></ button >
< button class = "menu-action" type = "button" data-command = "refresh" >< span > R</ span >< span > Refresh data</ span >< span > F5</ span ></ button >
</ div >
</ div >
</ nav >
< div class = "admin-workspace-body security-page-body" >
< section class = "security-grid" >
< section class = "security-panel" >
< div class = "security-panel-header" >< strong > Manual controls</ strong >< span > admin actions</ span ></ div >
< div class = "security-panel-body" >
< label class = "security-field" > IP address
< input class = "security-input" id = "security-ip-input" type = "text" placeholder = "203.0.113.12" >
</ label >
< button class = "win98-button security-button" type = "button" data-command = "ban-ip" > Ban IP (temporary)</ button >
< label class = "security-field" > Ban expires (UTC)
< input class = "security-input" id = "security-ban-until" type = "datetime-local" >
</ label >
< button class = "win98-button security-button" type = "button" data-command = "ban-until" > Set ban expiration</ button >
< button class = "win98-button security-button" type = "button" data-command = "unban-ip" > Unban selected IP</ button >
< button class = "win98-button security-button" type = "button" data-command = "bulk-unban" > Bulk unban selected</ button >
< button class = "win98-button security-button security-danger" type = "button" data-command = "unban-all" > Unban all</ button >
< div class = "security-note" > Ban duration, whitelist rules and trusted proxies are managed in Settings - Security.</ div >
</ div >
</ section >
< section class = "security-panel" >
< div class = "security-panel-header" >< strong > Recent alerts</ strong >< span > {{ len .Alerts }} total</ span ></ div >
< div class = "security-panel-body" >
< ul class = "security-list" id = "security-alert-list" ></ ul >
</ div >
</ section >
</ section >
< section class = "security-panel" >
< div class = "security-panel-header" >< strong > Active bans</ strong >< span id = "security-bans-count" > {{ len .Bans }} active bans</ span ></ div >
< div class = "security-panel-body security-ban-grid" >
< div >
< div class = "security-table-toolbar" >
< input id = "security-ban-filter" class = "security-input" type = "text" placeholder = "Filter by IP" >
< select id = "security-ban-sort" class = "security-input" >
< option value = "expiry_asc" > Expiry ↑</ option >
< option value = "expiry_desc" > Expiry ↓</ option >
< option value = "ip_asc" > IP A-Z</ option >
< option value = "ip_desc" > IP Z-A</ option >
</ select >
</ div >
< div class = "security-table-wrap security-bans-wrap" >
< table class = "security-table" >
< thead >
< tr >
< th >< input id = "security-select-all" type = "checkbox" aria-label = "Select all" ></ th >
< th > IP</ th >
< th > Status</ th >
< th > Ban expires (UTC)</ th >
</ tr >
</ thead >
< tbody id = "security-bans-body" ></ tbody >
</ table >
</ div >
</ div >
< div class = "security-ip-detail" >
< h3 id = "security-detail-ip" > No IP selected</ h3 >
< ul >
< li >< strong > Risk:</ strong > < span id = "security-detail-risk" > -</ span ></ li >
< li >< strong > Threat:</ strong > < span id = "security-detail-threat" > -</ span ></ li >
< li >< strong > Geo:</ strong > < span id = "security-detail-geo" > GeoIP not enabled yet</ span ></ li >
< li >< strong > ASN:</ strong > < span id = "security-detail-asn" > GeoIP not enabled yet</ span ></ li >
< li >< strong > Ban until:</ strong > < span id = "security-detail-until" > -</ span ></ li >
< li >< strong > Why banned:</ strong > < span id = "security-detail-why" > -</ span ></ li >
< li >< button id = "security-copy-ip" class = "win98-button security-button" type = "button" > Copy IP</ button ></ li >
< li >< button id = "security-open-activity" class = "win98-button security-button" type = "button" > Search in activity</ button ></ li >
< li >< button id = "security-open-alerts" class = "win98-button security-button" type = "button" > Search in alerts</ button ></ li >
</ ul >
</ div >
</ div >
</ section >
< section class = "security-panel" >
< div class = "security-panel-header" >< strong > Recent security activity</ strong >< span > {{ len .Events }} rows</ span ></ div >
< div class = "security-panel-body" >
< div class = "security-table-wrap" >
< table class = "security-table" >
< thead >
< tr >
< th > Time</ th >
< th > Kind</ th >
< th > Severity</ th >
< th > IP</ th >
< th > Path</ th >
< th > Message</ th >
</ tr >
</ thead >
< tbody id = "security-activity-body" ></ tbody >
</ table >
</ div >
</ div >
</ section >
< section class = "security-panel" >
< div class = "security-panel-header" >< strong > Security Runbook</ strong >< span > ops quick reference</ span ></ div >
< div class = "security-panel-body security-docs" >
< h4 > Reverse Proxy and Trusted CIDRs</ h4 >
2026-05-23 19:07:11 +03:00
< p > Set < code > WARPBOX_TRUSTED_PROXY_CIDRS</ code > to the CIDRs of your proxy nodes only. Without this, all traffic can appear as the proxy or bridge IP, such as < code > 172.30.0.1</ code > .</ p >
2026-05-04 00:00:36 +03:00
< pre > Caddyfile
:443 {
2026-05-23 19:07:11 +03:00
reverse_proxy warpbox:8080 {
2026-05-04 00:00:36 +03:00
header_up X-Forwarded-For {http.request.remote.host}
header_up X-Real-IP {http.request.remote.host}
}
}</ pre >
< h4 > Ban / Unban Safety</ h4 >
< p > Use custom ban durations only for active incidents. Prefer temporary bans. Review the "why banned" detail before unbanning to avoid immediate re-abuse.</ p >
< h4 > Tuning Guidance</ h4 >
< p > Low traffic: lower < code > security_*_max_attempts</ code > . High traffic: increase windows and attempt thresholds gradually, then monitor alerts/activity for false positives.</ p >
< h4 > GeoIP Guide (planned)</ h4 >
< p > For < code > geoip2fast</ code > , keep lookups async-safe with a single loaded database, add a short timeout per lookup, cache by IP with TTL, and degrade gracefully to "unknown" on failures. Start with security detail pane only, then aggregate stats later.</ p >
</ div >
</ section >
</ div >
< footer class = "status-bar admin-dashboard-statusbar" >
< span id = "security-status-left" > Security controls active</ span >
< span id = "security-status-middle" > alerts + activity linked</ span >
< span id = "security-status-right" > admin only</ span >
</ footer >
</ div >
</ div >
</ div >
< div class = "toast" id = "toast" role = "status" aria-live = "polite" ></ div >
< script id = "security-events-data" type = "application/json" >{{ toJSON . Events }}</ script >
< script id = "security-alerts-data" type = "application/json" >{{ toJSON . Alerts }}</ script >
< script id = "security-bans-data" type = "application/json" >{{ toJSON . Bans }}</ script >
< script src = "/static/js/warpbox-ui.js" ></ script >
< script src = "/static/js/admin/security.js" ></ script >
</ body >
</ html >
{{ end }}