secubox-core.service is a Type=oneshot (mkdir+chown) that RemainAfterExit=yes. A hard
Requires= on ~108 units cascade-stops them all if core is restarted/fails (e.g. a
secubox-core package upgrade) — a thundering-herd outage. After= keeps the ordering;
Wants= keeps the soft dependency without the cascade. Prereq for mass native apply
(Phase 3). Scaffolds (new-module.sh/new-package.sh) updated so future units use Wants=.
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
Add /shared/hybrid-dark.css: a shared overlay that remaps the design tokens
(consumed by crt-light.css, sidebar-light.css and every module's inline styles)
to the cyan hybrid-dark palette from /certs/ + /wireguard/. body.hybrid-dark
(specificity 0,1,1) beats :root and inline :root, so one token remap flips the
whole tree — no per-panel rewrite. Each panel gains the overlay <link> + the
hybrid-dark body class (2-line additive swap; crt-light.css kept as base).
See .claude/WEBUI-PANEL-GUIDELINES.md.
Panels with fully bespoke styles (certs/wireguard/users/nac already hybrid;
~14 non-crt-light custom panels) are untouched.
- secubox-toolbox-autolearn now mirrors each splice-learned host into
mitm-bypass-dynamic.conf (the Filtres MITM '🔍 learned' source) as bypass
regex, so autolearned splice hosts are visible + editable in the webui and
honoured on the ignore_hosts path. Merge (never clobbers operator entries).
- secubox-mitmproxy filters.html: 'WAF Filters' → 'Filtres MITM' (title/h1/desc);
API path /api/v1/mitmproxy/waf unchanged.
secubox-waf-watchdog timer checks inspector :8080 every 60s and auto-recovers
the mitmproxy LXC after 3 consecutive failures (rate-limited once/10min) — an
inspector crash becomes a ~3min auto-recovery instead of a multi-hour 503.
Shipped in secubox-mitmproxy; enabled in postinst, disabled in prerm. Makes the
live hotfix from the #624 incident durable across reflash.
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.
New media_cache.py addon (both synced copies) caches cacheable GET
media/static (image/video/audio/font/css/js) from our vhosts on disk
(URL key, 16MB/obj, 2GB LRU, TTL from max-age) and serves repeats from
cache. NOT a bypass: requests still pass secubox_waf inspection; only the
response body is served from a WAF-populated cache. Loaded in the LXC
mitmproxy.service; wafctl creates the cache dir. Toggle via
/data/mitmproxy/media-cache.json (default on). Verified live: HIT.
In --mode regular the addon relayed any Host; HAProxy default_backend made
the WAF an open forward proxy abused by scanners (~72% error churn + 11
loop-508s/hr). requestheaders now serves ONLY our vhosts (routes / our
domains via routes-derived local_suffixes -> nginx 9080 / SELF_HOSTS) and
returns 421 otherwise with no upstream connect. Applied to both synced
secubox_waf.py copies. Verified live: 0 external server-connects, 0 loops,
apt/admin/kbin 200, scanners 421.
- requestheaders hook in both synced secubox_waf.py copies: mitmproxy 11
opens the upstream connection before the request hook, so the in-request
redirect was too late and routed vhosts hit their public IP. Set
flow.server_conn.address in requestheaders instead.
- wafctl: bind-mount host /srv/mitmproxy/haproxy-routes.json into the LXC at
the addon's read path /data/mitmproxy/haproxy-routes.json (they had drifted
→ routes_count 0 → no routing); ensure the host file exists at provision.
- mitmproxy.service: warn any ExecStart drop-in must keep --set confdir
(a dropped confdir crash-looped the WAF via ProtectHome).
Ported from the live gk2 fixes (HISTORY 2026-06-15).
User : 'baisse rapide des performances du tunnel' followed by 'd\'abord les
performances'. Phase 6.N (dynamic whitelist) closed the retry-storm cause.
Phase 6.O squeezes more perf from the kernel/network stack.
## Live verification (before → after on gk2)
load avg : 8.08 → 4.75 (-41% in ~5 min)
run queue : 12 → 5 (-58%)
CPU idle : 5% → 21% (4x more breathing room)
swap activity: 0 (already healthy, just better protection now)
## What changed
### /etc/sysctl.d/95-secubox-perf.conf (NEW)
Memory :
vm.swappiness = 10 (default 60 → keep working set in RAM)
vm.vfs_cache_pressure = 50 (retain dentry/inode cache longer)
vm.dirty_background_ratio = 5 (earlier background writeback)
vm.dirty_ratio = 15 (lower sync flush threshold — less latency spike)
Network :
net.core.rmem_max / wmem_max = 16 MiB (was 200 KiB — WG burst friendly)
net.core.rmem_default / wmem_default = 1 MiB
net.core.netdev_max_backlog = 8000 (8x default — scheduling jitter)
TCP :
net.ipv4.tcp_fastopen = 3 (client + server : -1 RTT on repeat conn)
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_tw_reuse = 1 (~500 TIME_WAIT idle currently)
### /etc/tmpfiles.d/secubox-thp.conf (NEW)
Switches Transparent Huge Pages from 'always' to 'madvise'. On Armada
under memory pressure, 'always' causes compaction stalls (multi-ms
pauses in scheduling). 'madvise' lets apps opt-in (none here do),
which avoids the stalls completely.
### Not applied (kernel constraint)
BBR congestion control + fq qdisc were planned but the Marvell custom
kernel (6.12.85) wasn't built with CONFIG_TCP_CONG_BBR / NET_SCH_FQ.
Would need a kernel rebuild for those — kept in comment in the file.
Cost of staying on cubic + pfifo_fast : ~3-5% less throughput.
## Files
packages/secubox-mitmproxy/sysctl/95-secubox-perf.conf (NEW)
packages/secubox-mitmproxy/tmpfiles.d/secubox-thp.conf (NEW)
packages/secubox-mitmproxy/debian/rules (install wires)
## Diagnostics that mattered
vmstat 1 3 : si/so = 0 (no active swap thrashing — relief)
ps state : 12 procs in run queue (scheduling bottleneck)
cpu freq : already at max (1.4 GHz, schedutil governor OK)
cpu gov : schedutil (modern, no need to change)
THP : 'always' (changed to madvise — gain on this hw)
## Not pursued
- eBPF/XDP : Marvell DSA driver limits XDP to generic mode (no native
hook in driver). Gain would be ~2-3x, not 200x. Postponed.
- LXC pruning : memory available is fine (2.98 GB avail). No need
to stop yacy/horde/rustdesk.
User : 'plan for later a waf with dropping result of attackants...
threats accumulate looks very high'. Phase 7.A delivers the foundation :
WAF detections become real nft drops within seconds.
## E2E live-verified on gk2
login : 200 JWT 160 chars
alert : 201 ← decision id 13244
cscli : Ip:192.0.2.99 ban origin=secubox-waf 1h
nft : 192.0.2.99 in table ip crowdsec (kernel-level drop)
Full chain (mitm LXC → host LAPI → bouncer → nft) round-trip in ~12s.
## Architecture
[secubox_waf.py in mitm LXC]
│ detects threat via pattern match
│ if count >= BAN_THRESHOLD
│ ↓
│ ban_ip() → _ban_via_crowdsec()
│ ↓
│ HTTP POST /v1/alerts (urllib stdlib, no httpx dep)
│ ↓
[socat 10.100.0.1:8080 → 127.0.0.1:8080] ← Phase 7.A.1 bridge
│ ↓
[CrowdSec LAPI on host 127.0.0.1:8080]
│ ↓
[crowdsec-firewall-bouncer (existing)]
│ ↓
[nft add element ip crowdsec ...]
│ ↓
[attacker IP dropped at netdev/forward chain]
## Files
### addons/secubox_waf.py
New constants CROWDSEC_CFG_FILE, _CS_JWT, _CS_JWT_TTL.
New _load_crowdsec_cfg() reads TOML, validates 4 required fields.
New _cs_jwt(cfg) caches JWT for 25 min (LAPI default 30 min expiry).
New _ban_via_crowdsec() POSTs Alert+Decision to /v1/alerts with
Bearer JWT. urllib stdlib only (mitm venv lacks httpx). On 401,
forces JWT refresh next call. Stats : bans_pushed, bans_failed.
Modified ban_ip() : tries HTTP bridge first, falls back to cscli
subprocess if bridge disabled. Old subprocess path kept for hosts
where WAF runs natively (cscli available).
### bin/secubox-waf-cs-bridge-setup
Installs socat forwarder unit secubox-cs-bridge.service so the mitm
LXC can reach the host loopback LAPI without changing CrowdSec config.
Registers secubox-waf as a MACHINE (watcher) via cscli machines add
with -f - so the local agent's credentials file stays untouched.
Writes /tmp/crowdsec.toml with machine_id + machine_password.
Operator copies into LXC at /etc/secubox/waf/crowdsec.toml.
### crowdsec/crowdsec.toml.example
Updated to document machine_id / machine_password (not the old
bouncer api_key — bouncers can only PULL decisions, not push them).
## Why machine auth (not bouncer)
CrowdSec LAPI splits routes by auth type :
- X-Api-Key (bouncer) → GET /v1/decisions/stream (read-only)
- JWT (watcher) → POST /v1/alerts (write)
- JWT (watcher) → DELETE /v1/decisions
To PUSH a ban, the WAF must auth as a watcher. We register a dedicated
machine 'secubox-waf' so its decisions show up in the dashboard tagged
with origin=secubox-waf (vs origin=crowdsec for scenario-driven bans
and origin=cscli for manual ones).
## Socat bridge rationale
Default CrowdSec install binds LAPI to 127.0.0.1:8080. Direct patching
of listen_uri to 10.100.0.1 breaks the local agent + bouncer (they hard-
code 127.0.0.1 in their credential files). A 30-line socat forwarder
service exposes the same LAPI on the LXC bridge gateway, gated by the
host firewall (only LXCs can reach 10.100.0.1). Zero CrowdSec config
change.
## Acceptance criteria (issue #498 Phase 7.A target)
1. Attacker dropped at nft within 30s of crossing BAN_THRESHOLD ✓
2. cscli decisions list shows origin=secubox-waf entries ✓
3. nft list table ip crowdsec shows the dropped IPs ✓
4. threat_counts dict growth bounded ✓ (still
local memory, but attacker IPs now drop before refilling it)
## Phase 7.A.2+ (followups in TODO.md)
- Backport into packages/secubox-waf/mitmproxy/secubox_waf.py
- debian/postinst : invoke setup script + bind-mount config into LXC
- WAF dashboard tab in admin webui showing bans_pushed counter
- Tune BAN_THRESHOLD per category (XSS=2, SQLi=1, scanner pattern=5)
- Phase 7.B : nft rate-limit pre-mitm to catch TCP-only scanners
Tracking files updated to reflect 2026-06-05 work :
* .claude/HISTORY.md — full Phase 6 R3 release entry (R3 architecture,
first-class identity, banner enrichment, splash detection, landing polish,
multi-OS install, WAF leak fix, 8 bugs resolved)
* .claude/WIP.md — Done/Next/Follow-ups for Phase 6
* .claude/TODO.md — Phase 6 P0 checked + 3 follow-ups added
## Source backport : mitm WAF leak fix
packages/secubox-mitmproxy/addons/secubox_waf.py SecuBoxWAF.request adds :
flow.request.headers["Connection"] = "close"
Live verification (after live LXC patch + this source backport) :
Before : FDs 1513, scur=812 (8h growth), HTTP 504 sur /toolbox/
After : FDs 3, scur=87 (stable post-restart), 31ms response
Root cause was mitmproxy 11 keeping idle upstream HTTP/1.1 keep-alive
sockets in the per-server pool indefinitely. Connection: close on the
outbound request forces nginx to close TCP after the response, mitm
releases the socket → no pool buildup. Cost : ~1ms per request to
upstream (loopback TCP handshake), well worth the saved FDs.
This commit is the source-of-truth backport. The live LXC at
/var/lib/lxc/mitmproxy/rootfs/srv/mitmproxy/secubox_waf.py was patched
inline first (urgent prod fix) then synced back here.
Note : packages/secubox-waf/mitmproxy/secubox_waf.py (756 lines, older
version) still pending backport — tracked in TODO.md follow-ups.
The mitmproxy WAF whitelist was an exact-match set of two IPs, so a LAN
operator whose request matched a rule (e.g. a logout) hit the 3-strikes ban
with no bypass. Make the whitelist CIDR-aware and trust loopback + RFC1918
(LAN + internal LXC bridge). The WAF still inspects external traffic. Already
applied live in the mitmproxy LXC.
v1.0.3 added a last-resort 508 Loop Detected response when the
X-Forwarded-For chain showed mitmproxy was bouncing through HAProxy's
default_backend in a loop. That stopped the CPU runaway but the user
saw a 508 page when they typed http://192.168.1.200/ (a literal local
IP) since that Host is not in haproxy-routes.json — mitmproxy would
forward to the IP, HAProxy on the same IP would receive and route to
mitmproxy again, repeat.
This commit catches the case earlier. The new SELF_HOSTS set
enumerates the box's listening IPs (127.0.0.1, 192.168.1.200,
192.168.255.1, 10.100.0.1, 10.55.0.1). When the Host header matches
one of those AND has no explicit route, the pre-route guard in
request() rewrites the target to 192.168.1.200:9080 (host nginx) and
leaves the Host header intact so nginx can pick a vhost. The user
now lands on the canonical hub catch-all instead of a 508 page.
The 508 path remains as the absolute last resort and now logs the
offending Host + XFF self-reference count so operators can spot any
new Host that needs to be added to haproxy-routes.json or SELF_HOSTS.
Live verified on gk2: GET http://{192.168.1.200,10.100.0.1,10.55.0.1,
192.168.255.1}/ all return 503 (nginx default vhost) instead of the
WAF's 508 loop-detected page.
Adds an XFF self-reference check at the top of SecuBoxWaf.request(): when
the X-Forwarded-For chain already contains the LXC's own IP (10.100.0.60)
more than twice, the WAF returns 508 Loop Detected instead of opening
another upstream connection.
Observed failure mode on gk2: Pi Zero / Eye Remote (10.55.0.2) was
calling http://10.55.0.1/api/v1/eye-remote/api/system/info. Host header
'10.55.0.1' had no entry in haproxy-routes.json, so mitmproxy forwarded
to 10.55.0.1:80 (HAProxy itself) → default_backend mitmproxy_inspector
→ back into mitmproxy → loop. Each hop appended another 10.100.0.60 to
XFF; sustained ~50 hops per request kept mitmdump at 84% CPU.
The guard breaks the loop after at most 3 hops regardless of which Host
caused it — belt-and-braces in case another stray literal-IP appears.
The site-specific runtime fix (mapping 10.55.0.1 → 192.168.1.200:9080 in
haproxy-routes.json) lives on the board and is not in source: routes are
deployment-local state, not packaged.
The hub's `_compute_menu_sync()` drops menu entries lacking an `id`
field. 6+ packages (lyrion, yacy, zigbee, rustdesk, grafana, authelia)
shipped a different schema (`title/url/section/module`) so they NEVER
appeared in the navbar. Plus the existing CATEGORY_META declared 12
sections (dashboard/security/network/system/core/users/services/
privacy/monitoring/publishing/apps/admin), not the 6 SecuBox charter
modules.
Changes:
* All 121 packages/secubox-*/menu.d/*.json normalised to the canonical
schema (id, name, path, category, icon, order, description). Legacy
schema aliases (title/url/module/section) preserved as fallbacks then
dropped from the file.
* Each menu entry's category remapped to one of the 6 charter modules:
AUTH (auth/users/identity/zkp/nac/openclaw),
WALL (crowdsec/waf/mitmproxy/hardening/threat-* /cve-triage/...),
BOOT (kernel-build/eye-remote/master-link/droplet/cloner/backup/...),
MIND (ai-gateway/mcp-server/grafana/ndpid/netifyd/glances/...),
ROOT (system/hub/portal/console/admin/vault/vm/rtty/...),
MESH (wireguard/dns/tor/matrix/gitea/nextcloud/mail/lyrion/yacy/
zigbee/dns-provider/rustdesk/... — all network + comms apps).
* secubox-hub v1.4.0 — CATEGORY_META rewritten with the 6 charter
modules carrying their official color from DESIGN-CHARTER.md and
the complementary-pair order. DEFAULT_MENU fallback remapped too.
* secubox-zigbee v2.5.3 — www/zigbee/index.html rewritten with the
canonical SecuBox scaffold (body display:flex, sidebar 220px fixed,
.main reserving 48px for the global-menu-bar). MESH palette.
Browser side: operators need to clear localStorage sbx_menu_cache (or
hard-refresh after the 1h TTL) to see the new sections after deploy.
Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
PR #169 moved login.html out of secubox-hub to /usr/share/secubox/www/
under secubox-portal's ownership, but 67 source files across the
package set still redirected to /portal/login.html which no longer
exists on disk. nginx fell back to /index.html (hub root) on every
hit, whose checkAuth() bounced back to /portal/login.html, producing
an infinite "Redirecting to login..." loop with no way for any user
to authenticate.
sed -i 's|/portal/login\.html|/login.html|g' across all packages/*/www
sources (82 occurrences). The two prior partial fixes that triggered
this investigation (bcc6a781, 464e3379) only covered a subset and
missed the kiosk root checkAuth in secubox-hub/www/index.html plus
65 module index.html files.
Verified: grep -rl '/portal/login\.html' packages/ | grep -v /debian/
returns empty.
Discovered while testing the firstboot v2-schema+argon2 chain end-to-end
in a VBox amd64 boot of CI run 26082003883 (the #218 image build fix)
— firstboot now works, users.json gets a valid argon2 hash for
admin/secubox, the portal API runs, but the login UI is unreachable
because of this dangling URL.
Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
mitmproxyctl is SecuBox's third routing verb, parallel to:
haproxyctl vhost add/remove (routing layer)
giteactl user add/remove (identity layer)
mitmproxyctl route add/remove (interception layer) ← new
Without this verb, every WAF route change required hand-editing
/srv/mitmproxy/haproxy-routes.json + manual restart. The interception
organ existed but could not be commanded.
Changes:
* Defaults now match board reality:
CONTAINER_NAME : "mitmproxy-waf" -> "mitmproxy"
LXC_PATH : "/var/lib/lxc" -> "/data/lxc"
All overridable via [container] in /etc/secubox/mitmproxy.toml.
* Added --config flag to load any TOML.
* New `route` subcommand:
mitmproxyctl route list
mitmproxyctl route add HOST IP PORT [--no-restart]
mitmproxyctl route remove HOST [--no-restart]
Atomic writes via .tmp + replace, mirror to the LXC-rootfs copy of
haproxy-routes.json automatically, restart prefers in-LXC systemd
`mitmproxy.service` reload over full container bounce.
* `restart` is now reload-friendly when an in-LXC systemd unit exists
(the common case on the live board); falls back to stop+start otherwise.
* `logs` reads via `journalctl -u mitmproxy` in-LXC (was `tail` on a
file path that didn't exist in practice).
After this lands, the gitea WAF unbypass (done by hand today during the
#156 cascade) reduces to:
haproxyctl vhost add gitea.gk2.secubox.in mitmproxy_inspector ssl
mitmproxyctl route add gitea.gk2.secubox.in 192.168.1.200 9080
Three verbs, three layers, one operation. The grammar closes.
Out of scope (separate ticket): the package's debian/postinst still
assumes host-installed mitmproxy under /srv/mitmproxy-waf/, while the
modern board runs the LXC topology this ctl now targets. That refactor
will follow as a postinst -> LXC-bootstrap migration similar to mailctl.
Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ship two post-deploy artifacts requested by #156:
- /etc/apparmor.d/abstractions/secubox-cookie-audit
Operators with an enforced usr.bin.mitmdump AppArmor profile can
#include this abstraction to grant the addon rw on the ledger paths
(/var/log/secubox/cookie-audit/** + /var/lib/secubox/cookie-audit/**).
No-op on boards without AppArmor enforcement.
- /etc/logrotate.d/secubox-cookie-audit
Daily rotation of /var/log/secubox/cookie-audit/server.jsonl,
14 days kept, 50M maxsize, copytruncate so the running addon keeps
its fd open across rotations (no SIGUSR1 dance needed).
Both files installed by debian/rules into the existing package layout —
no new dependencies, no postinst changes.
Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(mitmproxy): cookie_audit addon — Set-Cookie ledger for RGPD (ref #156)
New mitmproxy addon that hooks the response flow and appends every Set-Cookie
header to /var/log/secubox/cookie-audit/server.jsonl. Cookie values are
sha256-hashed at the addon — the raw value never leaves the process.
Companion to the upcoming browser-side cookie-inventory.js + the
CookieAuditAggregator that will reconcile both streams for RGPD/ePrivacy
audit on operator-owned vhosts.
Also adds the implementation plan under docs/superpowers/plans/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(hub): cookie-inventory.js browser snapshotter (ref #156)
Vanilla JS module that snapshots document.cookie at DOMContentLoaded, +2s,
and on visibilitychange. Names + sha256(value) are POSTed to
/api/v1/cookie-audit/ingest with credentials:'omit'. Hard-capped at 8
snapshots per page to avoid amplification.
Loaded into every HTML response via the WAF banner injection (next commit).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(waf): inject cookie-inventory.js alongside health banner (ref #156)
Extends the existing CDN banner injection block to also append a script tag
loading shared/cookie-inventory.js. Adds two new CDN-config keys:
- cookie_inventory_url (default: admin.gk2.secubox.in/shared/...)
- cookie_audit_ingest_url (default: admin.gk2.secubox.in/api/v1/...)
Both health banner and cookie inventory now load from the same injected
guard block, so a single </body> replacement covers both.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(metrics): CookieAuditAggregator + Classifier (ref #156)
Aggregator class mirroring CertStatusAggregator: tails the mitmproxy JSONL
Set-Cookie ledger and the browser-snapshot ingest dir, then reconciles per
(vhost, cookie-name).
Per-cookie source verdict:
* http — server only
* js — browser only -> RGPD violation unless strictly_necessary
* both — server + browser
Classifier loads regex rules from config, checks categories in order
(strictly_necessary > functional > analytics > marketing), first match wins.
Default cache file: /var/cache/secubox/metrics/cookie-audit.json.
9 unit tests covering parsing, reconciliation, persistence and malformed
input.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(metrics): wire /api/v1/cookie-audit/{ingest,report,summary} (ref #156)
Imports + initializes CookieAuditAggregator, schedules it in the FastAPI
lifespan alongside the other aggregators, opens up CORS to POST (was GET
only) for the ingest endpoint, and adds three routes:
POST /api/v1/cookie-audit/ingest — browser snapshot ingest (rate-bounded,
credentials: omit, returns refused if
audit disabled in config)
GET /api/v1/cookie-audit/report?host=… — per-vhost or global report
GET /api/v1/cookie-audit/summary — global rollup
Hard caps: 200 cookies/snapshot, 128B names, 128B hashes, 512B UA.
All 34 existing metrics tests + 9 new cookie-audit tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(core): get_cookie_audit_config + RGPD classifier baseline (ref #156)
New config helper get_cookie_audit_config() that returns the merged
[cookie_audit] section with built-in RGPD/CNIL classifier patterns.
Categories (eval order — first match wins):
strictly_necessary PHPSESSID, csrftoken, cart, remember_token, ...
functional lang, locale, theme, cookie_consent, euconsent, ...
analytics _ga*, _gid, _gat*, _pk_*, _hjid, _clck, _matomo*, ...
marketing _fbp, _fbc, __utm*, _gcl_*, _uet*, IDE, MUID, NID
Operator patterns from [cookie_audit.classifier] are MERGED on top of the
baseline by default. Set classifier_override = true to replace it entirely
(opt-out for sites that need a different taxonomy).
secubox.conf.example documents the section with commented-out examples.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: cookie-audit API + addon (ref #156)
- secubox-metrics README: new "Cookie Audit" section documenting the
three endpoints, the source verdict semantics (http/js/both), and the
rgpd_violation rule.
- secubox-mitmproxy README: new "Addons" section listing both
secubox_waf and cookie_audit + the dual -s mitmdump invocation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(claude): record cookie-audit pipeline in HISTORY+WIP (ref #156)
HISTORY 2026-05-16 entry captures scope (server ledger + browser snapshot
reconciler), the 8 implementation tasks landed in the worktree, and the
two follow-ups left (AppArmor + logrotate).
WIP gets a top entry pointing at the worktree/branch with the test
totals and "awaiting validation, no PR opened" status.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(banner): add Cookie Audit live tile + summary URL injection (ref #156)
Adds a "CookieAudit" section to the health-banner live panel alongside
VisitorOrigin / LiveHosts / CertStatus. It polls /api/v1/cookie-audit/summary
every 30s and renders:
- host count
- RGPD verdict row (red ⚠ when violation_count > 0, green ✓ otherwise)
- per-category breakdown: strict / func / analytics / marketing
- unclassified row only shown when > 0
The WAF banner-injection block now also sets
window.SECUBOX_COOKIE_AUDIT_SUMMARY (new cookie_audit_summary_url config
key) so the banner can hit the cross-origin summary endpoint when the
host page is on a non-admin vhost.
Banner bumped to v1.4.0. New section hides itself silently when the
aggregator is disabled or empty — zero visual impact on existing
deployments.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add /multigadget skill covering autorun, storage, round UI, tooling
- Add Eye-Remote-Multigadget.md unified wiki hub
- Add Build-System.md to wiki with build documentation
- Add AI-BUILD-PROMPT.md for GPT/Gemini assistance
- Update sidebar and home page with new links
- Update WIP.md with #70 and #71 done
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- mitmproxy WAF injects health-banner.js into all HTML responses
- Configurable via /api/v1/cdn/banner/config endpoint
- Options: inject_domains, exclude_domains, banner_url, banner_api_url
- Health banner v1.1.0 supports cross-origin CDN injection
- SECUBOX_HEALTH_API global for configurable API endpoint
CDN API endpoints:
- GET /banner/config - Get injection config
- POST /banner/config - Update config
- POST /banner/toggle - Enable/disable injection
- POST /banner/exclude - Add domain exclusion
- DELETE /banner/exclude - Remove exclusion
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Parse X-Forwarded-For and X-Real-IP headers
- TRUSTED_PROXIES set for Docker/LXC bridge IPs
- Debug fields added to threat logs for IP tracing
- Updated HISTORY.md for Session 143
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WAF Improvements:
- Add cyberpunk-styled BAN_PAGE with skull, animations, scanlines
- Fix ban_ip() to SSH as mitmproxy user to host CrowdSec
- Add whitelist entries for local IPs (192.168.1.36, 192.168.1.254)
- Autoban now correctly registers in CrowdSec after 3 threats
UI Enhancements:
- Add category emojis to WAF threat list (SQLi💉, XSS🔥, RCE💀, etc.)
- Fix JSON parse error handling in system webui
Session 113: WAF ban page styling + autoban debug
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add GeoMap tab with country attack distribution
- Display country flags emoji in alerts list
- Show attack percentage by country
- Fix WAF addon to use X-Forwarded-For for real attacker IP
- Enable FAT/VFAT built-in and MVPP2 in LED kernel config
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The postinst script references /usr/share/secubox/mitmproxy/crowdsec/
but the crowdsec directory was not being copied during package build.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add nginx config installation to debian/rules:
- Installs mitmproxy.conf to /etc/nginx/secubox.d/
- Enables API proxying to unix socket
- Enables static file serving for WebUI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add filters.html page for managing WAF rule categories:
- Grid layout displaying all 14 WAF categories
- Toggle switches for enable/disable with immediate API calls
- Severity color coding (critical=red, high=orange, medium=yellow)
- Pattern count and hit count display per category
- Category descriptions for all rule types
- Summary bar showing totals (categories, enabled, patterns, hits)
- Follows SecuBox CRT-light styling conventions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add status.html that redirects to index.html for URL consistency.
The existing index.html already provides full WAF status functionality.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Refactor main.py to use modular router structure
- Add background stats cache refresh (60s interval)
- Create status router with LXC container control endpoints
- Include /health endpoint (no auth) and /status (JWT required)
- Add start/stop/restart container actions via mitmproxyctl
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>