Compare commits

...

3 Commits

Author SHA1 Message Date
da0c5008df docs: gitea mis-route fix + robust WAF route propagation (#609)
Some checks are pending
License Headers / check (push) Waiting to run
2026-06-15 18:27:27 +02:00
CyberMind
994b48f39d
Merge pull request #610 from CyberMind-FR/feature/609-waf-robust-route-propagation-dir-bind-mo
fix(waf): robust route propagation — dir bind-mount + live-reload
2026-06-15 18:26:18 +02:00
e13cf925f1 fix(waf): robust route propagation — dir bind-mount + addon live-reload (closes #609)
The #603 FILE bind-mount of haproxy-routes.json binds an inode; route tools
edit via jq>tmp&&mv (new inode) so changes went stale until a container
restart (surfaced fixing git.maegia.tv mis-route). Now: wafctl uses a
DIRECTORY bind-mount (host /srv/mitmproxy -> /var/lib/secubox-waf-routes ro)
+ symlink, and the addon (both synced copies) live-reloads haproxy-routes.json
on mtime change (throttled 10s) in requestheaders -> route edits apply with
NO restart. Verified live: jq+mv add -> live-reloaded 256 routes, 0 restart.
2026-06-15 18:25:56 +02:00
6 changed files with 98 additions and 5 deletions

View File

@ -3,6 +3,22 @@
---
## 2026-06-15 — gitea mis-route fix + robust WAF route propagation
- **gitea (`git.maegia.tv`) 404 → 200.** Pure routing-table error: its WAF
route pointed at `192.168.1.200:8000` (unrelated nginx) instead of the gitea
LXC `10.100.0.40:3000`. Corrected the route; gitea container was healthy
throughout. (`gitea.gk2`→nginx:9080 and `git.gk2`→gitea:3000 were already OK.)
- **Robust route propagation (#609/PR #610, mitmproxy 1.0.8 + waf 1.2.6).**
Fixing gitea surfaced that the #603 *file* bind-mount binds an inode, so route
tools (`jq > tmp && mv` = new inode) didn't reach the addon until a container
restart. Now: **directory** bind-mount (host `/srv/mitmproxy`
`/var/lib/secubox-waf-routes`, ro) + symlink, and the addon **live-reloads**
`haproxy-routes.json` on mtime change (10 s throttle, in `requestheaders`).
Verified live: `jq+mv` add → `[routes] live-reloaded 256 routes`, **0
restart**. Ported to source (both synced `secubox_waf.py` copies + wafctl) +
rebuilt into apt.secubox.in.
## 2026-06-15 — WAF hardening + perf: close open-proxy, behind-WAF media cache
Follow-up to the WAF restoration. Three findings investigated; two fixed.

View File

@ -688,6 +688,8 @@ ERROR_503_PAGE = b"""<!DOCTYPE html>
class SecuBoxWAF:
def __init__(self):
self.routes = {}
self._routes_mtime = 0.0
self._last_route_check = 0.0
self.compiled_patterns = {}
self.stats = {"requests": 0, "warnings": 0, "blocked": 0, "errors": 0}
self.threat_counts = defaultdict(list) # IP -> list of timestamps
@ -925,7 +927,29 @@ class SecuBoxWAF:
except Exception:
ctx.log.warn(f"BAN FAILED for {ip} ({reason}) : LAPI off + cscli unavailable")
def _maybe_reload_routes(self):
# #609 — live-reload haproxy-routes.json when it changes (throttled
# 10 s) so haproxyctl route edits take effect with NO restart. Pairs
# with the directory bind-mount that makes mv-replaced files visible.
import os as _o, time as _t
now = _t.time()
if now - getattr(self, "_last_route_check", 0) < 10:
return
self._last_route_check = now
try:
m = _o.path.getmtime(str(ROUTES_FILE))
except OSError:
return
if m != getattr(self, "_routes_mtime", 0):
self._routes_mtime = m
self.load_routes()
try:
ctx.log.info(f"[routes] live-reloaded {len(self.routes)} routes")
except Exception:
pass
def requestheaders(self, flow: http.HTTPFlow):
self._maybe_reload_routes()
# #605 — mitmproxy 11 opens the upstream connection before request(),
# so routing must happen here. ALSO: in --mode regular mitmproxy is a
# forward proxy that would relay ANY Host, so internet scanners abused

View File

@ -1,3 +1,15 @@
secubox-mitmproxy (1.0.8-1~bookworm1) bookworm; urgency=medium
* fix(waf): live-reload haproxy-routes.json on change (#609). The addon now
re-reads the routes file when its mtime changes (throttled 10 s, in the
requestheaders hook), so haproxyctl route edits take effect with NO
restart. Pairs with the directory bind-mount in wafctl that replaced the
fragile file bind-mount (a file mount binds one inode → went stale when
route tools edit via `jq > tmp && mv`). Verified live: jq+mv add →
addon live-reloaded, 0 restart.
-- Gerald KERMA <devel@cybermind.fr> Mon, 15 Jun 2026 18:00:00 +0200
secubox-mitmproxy (1.0.7-1~bookworm1) bookworm; urgency=medium
* feat(waf): behind-WAF media cache (#607). New media_cache.py addon caches

View File

@ -1,3 +1,14 @@
secubox-waf (1.2.6-1~bookworm1) bookworm; urgency=medium
* fix(waf): robust route propagation (#609). wafctl now uses a DIRECTORY
bind-mount (host /srv/mitmproxy → /var/lib/secubox-waf-routes, ro) + a
symlink /data/mitmproxy/haproxy-routes.json → it, replacing the #603 file
bind-mount (which bound one inode and went stale on `mv`). With the addon
live-reload (synced secubox_waf.py copies), haproxyctl route edits apply
with no restart. Fixes the class of bug that left git.maegia.tv mis-routed.
-- Gerald KERMA <devel@cybermind.fr> Mon, 15 Jun 2026 18:00:00 +0200
secubox-waf (1.2.5-1~bookworm1) bookworm; urgency=medium
* feat(waf): behind-WAF media cache (#607) — ship media_cache.py addon copy,

View File

@ -570,6 +570,8 @@ ERROR_503_PAGE = b"""<!DOCTYPE html>
class SecuBoxWAF:
def __init__(self):
self.routes = {}
self._routes_mtime = 0.0
self._last_route_check = 0.0
self.compiled_patterns = {}
self.stats = {"requests": 0, "warnings": 0, "blocked": 0, "errors": 0}
self.threat_counts = defaultdict(list) # IP -> list of timestamps
@ -781,7 +783,29 @@ class SecuBoxWAF:
except Exception:
ctx.log.warn(f"BAN FAILED for {ip} ({reason})")
def _maybe_reload_routes(self):
# #609 — live-reload haproxy-routes.json when it changes (throttled
# 10 s) so haproxyctl route edits take effect with NO restart. Pairs
# with the directory bind-mount that makes mv-replaced files visible.
import os as _o, time as _t
now = _t.time()
if now - getattr(self, "_last_route_check", 0) < 10:
return
self._last_route_check = now
try:
m = _o.path.getmtime(str(ROUTES_FILE))
except OSError:
return
if m != getattr(self, "_routes_mtime", 0):
self._routes_mtime = m
self.load_routes()
try:
ctx.log.info(f"[routes] live-reloaded {len(self.routes)} routes")
except Exception:
pass
def requestheaders(self, flow: http.HTTPFlow):
self._maybe_reload_routes()
# #605 — mitmproxy 11 opens the upstream connection before request(),
# so routing must happen here. ALSO: in --mode regular mitmproxy is a
# forward proxy that would relay ANY Host, so internet scanners abused

View File

@ -99,11 +99,13 @@ lxc.net.0.flags = up
lxc.net.0.ipv4.address = $LXC_IP/24
lxc.net.0.ipv4.gateway = 10.100.0.1
# Routes: bind-mount the host-maintained haproxy routes into the path the WAF
# addon reads. Without this the addon loaded an empty/stale in-LXC copy
# (routes_count: 0) so no upstream routing happened and every inspected vhost
# went to its public DNS IP. (#603)
lxc.mount.entry = /srv/mitmproxy/haproxy-routes.json data/mitmproxy/haproxy-routes.json none bind,create=file 0 0
# Routes: bind-mount the host-maintained haproxy routes DIRECTORY into the
# container; the addon reads /data/mitmproxy/haproxy-routes.json via a symlink
# into it (created below). A *directory* mount (not a file mount) is required
# so route tools that edit via `jq > tmp && mv` (new inode) stay visible — a
# file mount binds one inode and goes stale on mv (#609, was #603). Combined
# with the addon's mtime live-reload, route edits apply with no restart.
lxc.mount.entry = /srv/mitmproxy var/lib/secubox-waf-routes none bind,ro,create=dir 0 0
# Autostart
lxc.start.auto = 1
@ -121,6 +123,10 @@ CONF
lxc-attach -n "$LXC_NAME" -- mkdir -p /opt/mitmproxy /data/mitmproxy /var/log/mitmproxy \
/data/mitmproxy/cache/media /data/mitmproxy/logs # #607 media cache + stats
# #609 — addon reads /data/mitmproxy/haproxy-routes.json; point it at the
# routes dir bind-mount so mv-replaced files stay visible + live-reload.
lxc-attach -n "$LXC_NAME" -- ln -sfn /var/lib/secubox-waf-routes/haproxy-routes.json \
/data/mitmproxy/haproxy-routes.json
lxc-attach -n "$LXC_NAME" -- python3 -m venv /opt/mitmproxy
lxc-attach -n "$LXC_NAME" -- /opt/mitmproxy/bin/pip install mitmproxy