mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-01 13:06:54 +00:00
Compare commits
No commits in common. "72b7eca12eb12afa2377afc13806220cf74e56c1" and "8bb546c689ebb43a6505dc1315a86e67742ac6a0" have entirely different histories.
72b7eca12e
...
8bb546c689
|
|
@ -712,12 +712,6 @@ class SecuBoxWAF:
|
||||||
if ROUTES_FILE.exists():
|
if ROUTES_FILE.exists():
|
||||||
try:
|
try:
|
||||||
self.routes = json.loads(ROUTES_FILE.read_text())
|
self.routes = json.loads(ROUTES_FILE.read_text())
|
||||||
sfx = set()
|
|
||||||
for _h in self.routes:
|
|
||||||
_p = _h.split('.')
|
|
||||||
if len(_p) >= 2 and not _p[-1].isdigit():
|
|
||||||
sfx.add('.'.join(_p[-2:]))
|
|
||||||
self.local_suffixes = sfx
|
|
||||||
ctx.log.info(f"Loaded {len(self.routes)} routes")
|
ctx.log.info(f"Loaded {len(self.routes)} routes")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.log.error(f"Failed to load routes: {e}")
|
ctx.log.error(f"Failed to load routes: {e}")
|
||||||
|
|
@ -926,12 +920,12 @@ class SecuBoxWAF:
|
||||||
ctx.log.warn(f"BAN FAILED for {ip} ({reason}) : LAPI off + cscli unavailable")
|
ctx.log.warn(f"BAN FAILED for {ip} ({reason}) : LAPI off + cscli unavailable")
|
||||||
|
|
||||||
def requestheaders(self, flow: http.HTTPFlow):
|
def requestheaders(self, flow: http.HTTPFlow):
|
||||||
# #605 — mitmproxy 11 opens the upstream connection before request(),
|
# #603 — mitmproxy 11 opens the upstream connection BETWEEN the
|
||||||
# so routing must happen here. ALSO: in --mode regular mitmproxy is a
|
# requestheaders and request hooks, so upstream redirection must
|
||||||
# forward proxy that would relay ANY Host, so internet scanners abused
|
# happen here. Doing it in request() (below) is too late: the
|
||||||
# it as an open proxy (~70% error churn + self-loops). Serve ONLY our
|
# socket is already connected to the original Host, so routed
|
||||||
# own vhosts: mapped (routes), our domains (-> nginx catch-all), or our
|
# vhosts went to their public DNS IP instead of the internal
|
||||||
# own IPs; refuse everything else with 421 and never open an upstream.
|
# backend. Setting flow.server_conn.address here fixes routing.
|
||||||
try:
|
try:
|
||||||
host = flow.request.pretty_host
|
host = flow.request.pretty_host
|
||||||
if host in self.routes:
|
if host in self.routes:
|
||||||
|
|
@ -944,32 +938,9 @@ class SecuBoxWAF:
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
flow.request.headers['Host'] = orig
|
flow.request.headers['Host'] = orig
|
||||||
return
|
|
||||||
if host in SELF_HOSTS or self._is_local_host(host):
|
|
||||||
flow.request.host = '192.168.1.200'
|
|
||||||
flow.request.port = 9080
|
|
||||||
try:
|
|
||||||
flow.server_conn.address = ('192.168.1.200', 9080)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return
|
|
||||||
self.stats['blocked'] = self.stats.get('blocked', 0) + 1
|
|
||||||
flow.response = http.Response.make(
|
|
||||||
421,
|
|
||||||
b'<h1>421 Misdirected Request</h1><p>SecuBox WAF does not proxy this host.</p>',
|
|
||||||
{'Content-Type': 'text/html', 'X-SecuBox-WAF': 'unmapped-host'},
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.log.warn(f'[requestheaders-route] {e}')
|
ctx.log.warn(f'[requestheaders-route] {e}')
|
||||||
|
|
||||||
def _is_local_host(self, host: str) -> bool:
|
|
||||||
# #605 — is `host` one of our own (registrable) domains? Derived from
|
|
||||||
# the routed hosts in load_routes (self.local_suffixes).
|
|
||||||
sfx = getattr(self, 'local_suffixes', None)
|
|
||||||
if not sfx:
|
|
||||||
return False
|
|
||||||
return any(host == s or host.endswith('.' + s) for s in sfx)
|
|
||||||
|
|
||||||
def request(self, flow: http.HTTPFlow):
|
def request(self, flow: http.HTTPFlow):
|
||||||
# Connection close (Phase 6.J leak fix, ref #496) — prevents mitmproxy
|
# Connection close (Phase 6.J leak fix, ref #496) — prevents mitmproxy
|
||||||
# from accumulating idle keep-alive sockets to upstream backends.
|
# from accumulating idle keep-alive sockets to upstream backends.
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,3 @@
|
||||||
secubox-mitmproxy (1.0.6-1~bookworm1) bookworm; urgency=medium
|
|
||||||
|
|
||||||
* fix(waf): refuse unmapped hosts — close the open forward-proxy (#605). In
|
|
||||||
--mode regular the addon relayed any Host, so HAProxy's default_backend
|
|
||||||
made the WAF an open proxy; internet scanners drove ~72% backend-error
|
|
||||||
churn + self-loops. The `requestheaders` hook now serves ONLY our vhosts
|
|
||||||
(mapped, our domains via routes-derived `local_suffixes` → nginx catch-all,
|
|
||||||
or our own IPs) and returns 421 for everything else with no upstream
|
|
||||||
connect. Verified live: 0 external server-connects, 0 loop-508s.
|
|
||||||
|
|
||||||
-- Gerald KERMA <devel@cybermind.fr> Mon, 15 Jun 2026 16:30:00 +0200
|
|
||||||
|
|
||||||
secubox-mitmproxy (1.0.5-1~bookworm1) bookworm; urgency=medium
|
secubox-mitmproxy (1.0.5-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* fix(waf): mitmproxy-11 upstream routing (#603). The addon only redirected
|
* fix(waf): mitmproxy-11 upstream routing (#603). The addon only redirected
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,3 @@
|
||||||
secubox-waf (1.2.4-1~bookworm1) bookworm; urgency=medium
|
|
||||||
|
|
||||||
* fix(waf): refuse unmapped hosts in the addon copy — close the open
|
|
||||||
forward-proxy (#605, synced with secubox-mitmproxy). 421 for any host not
|
|
||||||
in routes / our domains / our IPs; no upstream connect. Kills the ~72%
|
|
||||||
scanner-driven error churn and the self-loop 508s.
|
|
||||||
|
|
||||||
-- Gerald KERMA <devel@cybermind.fr> Mon, 15 Jun 2026 16:30:00 +0200
|
|
||||||
|
|
||||||
secubox-waf (1.2.3-1~bookworm1) bookworm; urgency=medium
|
secubox-waf (1.2.3-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* fix(waf): mitmproxy-11 upstream routing — `requestheaders` hook in the
|
* fix(waf): mitmproxy-11 upstream routing — `requestheaders` hook in the
|
||||||
|
|
|
||||||
|
|
@ -594,12 +594,6 @@ class SecuBoxWAF:
|
||||||
if ROUTES_FILE.exists():
|
if ROUTES_FILE.exists():
|
||||||
try:
|
try:
|
||||||
self.routes = json.loads(ROUTES_FILE.read_text())
|
self.routes = json.loads(ROUTES_FILE.read_text())
|
||||||
sfx = set()
|
|
||||||
for _h in self.routes:
|
|
||||||
_p = _h.split('.')
|
|
||||||
if len(_p) >= 2 and not _p[-1].isdigit():
|
|
||||||
sfx.add('.'.join(_p[-2:]))
|
|
||||||
self.local_suffixes = sfx
|
|
||||||
ctx.log.info(f"Loaded {len(self.routes)} routes")
|
ctx.log.info(f"Loaded {len(self.routes)} routes")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.log.error(f"Failed to load routes: {e}")
|
ctx.log.error(f"Failed to load routes: {e}")
|
||||||
|
|
@ -782,12 +776,12 @@ class SecuBoxWAF:
|
||||||
ctx.log.warn(f"BAN FAILED for {ip} ({reason})")
|
ctx.log.warn(f"BAN FAILED for {ip} ({reason})")
|
||||||
|
|
||||||
def requestheaders(self, flow: http.HTTPFlow):
|
def requestheaders(self, flow: http.HTTPFlow):
|
||||||
# #605 — mitmproxy 11 opens the upstream connection before request(),
|
# #603 — mitmproxy 11 opens the upstream connection BETWEEN the
|
||||||
# so routing must happen here. ALSO: in --mode regular mitmproxy is a
|
# requestheaders and request hooks, so upstream redirection must
|
||||||
# forward proxy that would relay ANY Host, so internet scanners abused
|
# happen here. Doing it in request() (below) is too late: the
|
||||||
# it as an open proxy (~70% error churn + self-loops). Serve ONLY our
|
# socket is already connected to the original Host, so routed
|
||||||
# own vhosts: mapped (routes), our domains (-> nginx catch-all), or our
|
# vhosts went to their public DNS IP instead of the internal
|
||||||
# own IPs; refuse everything else with 421 and never open an upstream.
|
# backend. Setting flow.server_conn.address here fixes routing.
|
||||||
try:
|
try:
|
||||||
host = flow.request.pretty_host
|
host = flow.request.pretty_host
|
||||||
if host in self.routes:
|
if host in self.routes:
|
||||||
|
|
@ -800,32 +794,9 @@ class SecuBoxWAF:
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
flow.request.headers['Host'] = orig
|
flow.request.headers['Host'] = orig
|
||||||
return
|
|
||||||
if host in SELF_HOSTS or self._is_local_host(host):
|
|
||||||
flow.request.host = '192.168.1.200'
|
|
||||||
flow.request.port = 9080
|
|
||||||
try:
|
|
||||||
flow.server_conn.address = ('192.168.1.200', 9080)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return
|
|
||||||
self.stats['blocked'] = self.stats.get('blocked', 0) + 1
|
|
||||||
flow.response = http.Response.make(
|
|
||||||
421,
|
|
||||||
b'<h1>421 Misdirected Request</h1><p>SecuBox WAF does not proxy this host.</p>',
|
|
||||||
{'Content-Type': 'text/html', 'X-SecuBox-WAF': 'unmapped-host'},
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ctx.log.warn(f'[requestheaders-route] {e}')
|
ctx.log.warn(f'[requestheaders-route] {e}')
|
||||||
|
|
||||||
def _is_local_host(self, host: str) -> bool:
|
|
||||||
# #605 — is `host` one of our own (registrable) domains? Derived from
|
|
||||||
# the routed hosts in load_routes (self.local_suffixes).
|
|
||||||
sfx = getattr(self, 'local_suffixes', None)
|
|
||||||
if not sfx:
|
|
||||||
return False
|
|
||||||
return any(host == s or host.endswith('.' + s) for s in sfx)
|
|
||||||
|
|
||||||
def request(self, flow: http.HTTPFlow):
|
def request(self, flow: http.HTTPFlow):
|
||||||
# Connection close (Phase 6.J leak fix, ref #496) — prevents mitmproxy
|
# Connection close (Phase 6.J leak fix, ref #496) — prevents mitmproxy
|
||||||
# from accumulating idle keep-alive sockets to upstream backends.
|
# from accumulating idle keep-alive sockets to upstream backends.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user