fix(mitmproxy): Fix wildcard route priority matching
- Support both "*.domain" and ".domain" wildcard formats in haproxy_router.py - Sort wildcards by length (longest first) for correct specificity matching - Add auto-reload: check routes file mtime every 10 requests - Update metablogizerctl to use mitmproxyctl sync-routes Also fix luci-app-wazuh api.js to use baseclass.extend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b1c34021db
commit
9eaa16171d
@ -1,6 +1,6 @@
|
||||
# SecuBox UI & Theme History
|
||||
|
||||
_Last updated: 2026-02-11_
|
||||
_Last updated: 2026-02-14_
|
||||
|
||||
1. **Unified Dashboard Refresh (2025-12-20)**
|
||||
- Dashboard received the "sh-page-header" layout, hero stats, and SecuNav top tabs.
|
||||
@ -1697,3 +1697,18 @@ git checkout HEAD -- index.html
|
||||
- **API wrapper (wazuh/api.js)** with helper functions for alert levels and timestamps
|
||||
- **Fixed jshn segfault issue** - simplified to printf-based JSON output
|
||||
- Tested all RPCD methods via ubus calls
|
||||
|
||||
### 2026-02-14: mitmproxy WAF Wildcard Route Priority Fix
|
||||
- **Fixed wildcard route matching in haproxy_router.py**
|
||||
- Issue: `.gk2.secubox.in` wildcard (port 4000) matched before specific routes like `apr.gk2.secubox.in` (port 8928)
|
||||
- Root cause: Python code expected `*.domain` format but HAProxy generates `.domain` format
|
||||
- Fix: Support both `*.domain` and `.domain` wildcard formats
|
||||
- Fix: Sort wildcards by length (longest/most specific first) to ensure proper priority
|
||||
- **Added auto-reload capability**
|
||||
- Routes file mtime checked every 10 requests
|
||||
- Automatically reloads routes.json if file modified
|
||||
- No container restart needed for route updates
|
||||
- **Updated metablogizerctl integration**
|
||||
- `_emancipate_mitmproxy()` now uses `mitmproxyctl sync-routes` instead of direct file manipulation
|
||||
- Ensures HAProxy and mitmproxy routes stay in sync
|
||||
- MetaBlogizer sites now properly routed through WAF
|
||||
|
||||
@ -64,6 +64,15 @@ _Last updated: 2026-02-14 (WAF architecture configured)_
|
||||
|
||||
### Just Completed (2026-02-14)
|
||||
|
||||
- **mitmproxy WAF Wildcard Route Priority Fix** — DONE (2026-02-14)
|
||||
- Fixed wildcard route matching in `haproxy_router.py`
|
||||
- Issue: `.gk2.secubox.in` wildcard (port 4000) matched before specific routes like `apr.gk2.secubox.in` (port 8928)
|
||||
- Fix: Support both `*.domain` and `.domain` wildcard formats
|
||||
- Fix: Sort wildcards by length (longest/most specific first)
|
||||
- Added auto-reload: Routes file checked every 10 requests, reloads if modified
|
||||
- Updated `metablogizerctl` to use `mitmproxyctl sync-routes` instead of direct file manipulation
|
||||
- MetaBlogizer sites now properly routed through WAF
|
||||
|
||||
- **Wazuh SIEM LuCI Dashboard** — DONE (2026-02-14)
|
||||
- Created `luci-app-wazuh` package for unified Wazuh security monitoring
|
||||
- 4 views: Overview, Alerts, File Integrity, Agents
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require rpc';
|
||||
|
||||
var callWazuh = rpc.declare({
|
||||
@ -75,7 +76,7 @@ var callRestartAgent = rpc.declare({
|
||||
expect: {}
|
||||
});
|
||||
|
||||
return L.Class.extend({
|
||||
return baseclass.extend({
|
||||
getOverview: function() {
|
||||
return callWazuh().then(function(res) {
|
||||
return res.result || res;
|
||||
|
||||
@ -843,19 +843,24 @@ _emancipate_haproxy() {
|
||||
_emancipate_mitmproxy() {
|
||||
local name="$1"
|
||||
local domain="$2"
|
||||
local port=$(uci_get site_${name}.port)
|
||||
local routes_file="/srv/mitmproxy-in/haproxy-routes.json"
|
||||
|
||||
log_info "[WAF] Adding $domain to mitmproxy routes"
|
||||
log_info "[WAF] Syncing mitmproxy routes from HAProxy config"
|
||||
|
||||
# Check if mitmproxy routes file exists
|
||||
if [ ! -f "$routes_file" ]; then
|
||||
log_warn "[WAF] mitmproxy routes file not found, skipping"
|
||||
return 1
|
||||
fi
|
||||
# Use mitmproxyctl to sync routes from HAProxy UCI config
|
||||
# This ensures all routes are in sync and mitmproxy will auto-reload
|
||||
if command -v mitmproxyctl >/dev/null 2>&1; then
|
||||
mitmproxyctl sync-routes 2>&1 | while read -r line; do
|
||||
log_info "[WAF] $line"
|
||||
done
|
||||
log_info "[WAF] Routes synced - mitmproxy will auto-reload on next request"
|
||||
else
|
||||
log_warn "[WAF] mitmproxyctl not found, manually adding route"
|
||||
# Fallback: directly add route to the routes file
|
||||
local port=$(uci_get site_${name}.port)
|
||||
local routes_file="/srv/mitmproxy-in/haproxy-routes.json"
|
||||
|
||||
# Add domain to mitmproxy routes using Python
|
||||
python3 -c "
|
||||
if [ -f "$routes_file" ]; then
|
||||
python3 -c "
|
||||
import json
|
||||
try:
|
||||
with open('$routes_file') as f:
|
||||
@ -867,15 +872,7 @@ try:
|
||||
except Exception as e:
|
||||
print(f'[WAF] Error: {e}')
|
||||
" 2>/dev/null
|
||||
|
||||
# Restart mitmproxy-in container to reload routes
|
||||
if command -v lxc-stop >/dev/null 2>&1; then
|
||||
log_info "[WAF] Restarting mitmproxy-in container..."
|
||||
lxc-stop -n mitmproxy-in 2>/dev/null
|
||||
sleep 1
|
||||
lxc-start -n mitmproxy-in 2>/dev/null
|
||||
sleep 2
|
||||
log_info "[WAF] mitmproxy-in restarted"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,9 @@ DEFAULT_BACKEND = ("127.0.0.1", 8081) # LuCI fallback
|
||||
class HaproxyRouter:
|
||||
def __init__(self):
|
||||
self.routes = {}
|
||||
self._routes_mtime = 0
|
||||
self._check_interval = 10 # Check file every N requests
|
||||
self._request_count = 0
|
||||
self._load_routes()
|
||||
ctx.log.info(f"HAProxy Router loaded with {len(self.routes)} routes")
|
||||
|
||||
@ -26,6 +29,7 @@ class HaproxyRouter:
|
||||
"""Load routing table from JSON file"""
|
||||
if os.path.exists(ROUTES_FILE):
|
||||
try:
|
||||
self._routes_mtime = os.path.getmtime(ROUTES_FILE)
|
||||
with open(ROUTES_FILE, 'r') as f:
|
||||
self.routes = json.load(f)
|
||||
ctx.log.info(f"Loaded routes: {list(self.routes.keys())}")
|
||||
@ -36,6 +40,17 @@ class HaproxyRouter:
|
||||
ctx.log.warn(f"Routes file not found: {ROUTES_FILE}")
|
||||
self._generate_default_routes()
|
||||
|
||||
def _check_reload_routes(self):
|
||||
"""Check if routes file has changed and reload if needed"""
|
||||
try:
|
||||
if os.path.exists(ROUTES_FILE):
|
||||
mtime = os.path.getmtime(ROUTES_FILE)
|
||||
if mtime > self._routes_mtime:
|
||||
ctx.log.info("Routes file changed, reloading...")
|
||||
self._load_routes()
|
||||
except Exception as e:
|
||||
ctx.log.error(f"Error checking routes file: {e}")
|
||||
|
||||
def _generate_default_routes(self):
|
||||
"""Generate default routes from UCI if available"""
|
||||
self.routes = {
|
||||
@ -62,22 +77,42 @@ class HaproxyRouter:
|
||||
# Remove port from host if present
|
||||
hostname = host.split(':')[0].lower()
|
||||
|
||||
# 1. Try exact match first
|
||||
if hostname in self.routes:
|
||||
backend = self.routes[hostname]
|
||||
return (backend[0], backend[1])
|
||||
|
||||
# Try wildcard matching
|
||||
# 2. Try wildcard matching - collect all wildcard patterns
|
||||
# Support both "*.domain" and ".domain" formats
|
||||
wildcards = []
|
||||
for pattern, backend in self.routes.items():
|
||||
if pattern.startswith('*.'):
|
||||
suffix = pattern[2:]
|
||||
if hostname.endswith(suffix):
|
||||
return (backend[0], backend[1])
|
||||
# Standard wildcard: *.gk2.secubox.in
|
||||
suffix = pattern[1:] # Keep the dot: .gk2.secubox.in
|
||||
wildcards.append((suffix, backend))
|
||||
elif pattern.startswith('.'):
|
||||
# HAProxy-style wildcard: .gk2.secubox.in
|
||||
suffix = pattern # Already has dot: .gk2.secubox.in
|
||||
wildcards.append((suffix, backend))
|
||||
|
||||
# Sort by suffix length descending - longest (most specific) first
|
||||
wildcards.sort(key=lambda x: len(x[0]), reverse=True)
|
||||
|
||||
for suffix, backend in wildcards:
|
||||
if hostname.endswith(suffix):
|
||||
return (backend[0], backend[1])
|
||||
|
||||
ctx.log.warn(f"No route for {hostname}, using default")
|
||||
return DEFAULT_BACKEND
|
||||
|
||||
def request(self, flow: http.HTTPFlow):
|
||||
"""Route request to appropriate backend"""
|
||||
# Periodically check if routes file has changed
|
||||
self._request_count += 1
|
||||
if self._request_count >= self._check_interval:
|
||||
self._request_count = 0
|
||||
self._check_reload_routes()
|
||||
|
||||
host = flow.request.host_header or flow.request.host
|
||||
backend = self._get_backend(host)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user