Commit Graph

2793 Commits

Author SHA1 Message Date
5b8feb2272 feat(metablogizer): route publishes into the live sbxwaf host file via publishctl
Retires sync_mitmproxy_routes (wrote the retired mitmproxy-LXC file — the zem 421).

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 13:02:02 +02:00
389b2f380b feat(metablogizer): safe zip/html upload extraction (zip-slip guarded)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 12:56:39 +02:00
1338971818 fix(metablogizer): publishctl — valid JSON on all paths + real vhost failure reporting
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 12:54:06 +02:00
22c3260eac feat(metablogizer): secubox-publishctl root helper for privileged publish steps
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 12:48:39 +02:00
2f0106c9b9 docs: implementation plan for MetaBlogizer Publisher Wizard (11 tasks, TDD)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 12:44:58 +02:00
9ea93ae55a docs: design spec for MetaBlogizer Publisher Wizard
Guided publish wizard in secubox-metablogizer: upload zip/html -> gitea
versioning -> WAF routing (sbxwaf host file, via a new secubox-publishctl root
helper, fixing the mitmproxy-LXC route-target drift that 421'd zem) -> cert
(wildcard for *.gk2, certbot for custom) -> portable .sbxsite backup (git
bundle + manifest). Also retires secubox-publish's broken unprivileged
/etc/haproxy writes (delegates to the wizard) and the droplet.toml ownership bug.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 12:36:33 +02:00
3790cf225e feat(portal): full in-browser TOTP flow in login.html (mfa + enrollment + backup codes)
Makes the admin-2FA ON state usable without the API. login.html branches on the
auth response: mfa_required -> code step (/login/mfa, backup codes ok);
enrollment_required -> QR + manual key + confirm (/totp/enroll, /totp/confirm)
then one-time backup codes; access_token -> straight in. Verified live end-to-end
on gk2 (enroll->confirm->re-login->mfa).

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 07:01:39 +02:00
6b9860a91b feat(auth): webui-toggleable admin-TOTP requirement (default off) + per-user 2FA reset
secubox-auth 1.0.4:
- GET/POST /api/v1/auth/settings (admin-gated) for require_admin_totp,
  persisted to /etc/secubox/auth-runtime.json
- login enforcement via _get_require_admin_totp() (runtime > [auth] config >
  default False); fail-open so a config error never locks admins out
- tests realigned to reachability-first contract + /settings roundtrip;
  fixture isolates config + runtime paths

secubox-users 1.4.3:
- Admin 2FA ON/OFF toolbar toggle wired to auth /settings
- per-user Reset 2FA button (POST /user/<u>/totp/disable), enrolled-only

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 06:52:56 +02:00
9b5d0b1719 fix(auth): login failed on undefined _SESSIONS_FILE (dunder vs single-underscore mismatch)
Sessions file defined as __SESSIONS_FILE but referenced as _SESSIONS_FILE in
every handler -> NameError on session save/load, so valid logins failed and
/status 500d. Normalised all refs to _SESSIONS_FILE. Verified: login endpoint
returns proper JSON (401 on bad creds), sessions persist.
2026-07-11 06:05:46 +02:00
f5461575c6 fix(metoblizer): install api + fix ExecStart (was crash-looping 4042x)
Bare 'dh $@' rules never installed api/www/config, so the service could not
chdir to its (nonexistent) WorkingDirectory and crash-looped 4042x; ExecStart
also called /usr/bin/uvicorn which is not present. Add override_dh_auto_install
and use /usr/bin/python3 -m uvicorn. Deployed live: active, 0 restarts, API 200.
2026-07-10 15:08:24 +02:00
c501af01b3 feat(ndpid): bridge dashboard to ndpiReader/dpi collector when nDPId DB is empty
nDPId's zmq distributor is not run on gk2 (perf revert #722/#723), so the
ndpid DB is empty and the panel showed nothing. Add DpiBridge reading
/var/lib/secubox/dpi/{cumulative,state}.json and fall the flows/protocols/
applications/top/stats endpoints back to it (nDPId preferred if it has data).
JA3/JA4 + per-flow risks stay empty (no equivalent). Live: 1.3M flows, TLS/DNS/
BitTorrent top protocols, real top apps.
2026-07-10 15:01:01 +02:00
3866e88b96 fix(ndpid): stop service crash-loop (bad ReadWritePaths /run/ndpid) + add nginx API dropin
secubox-ndpid crash-looped 1111x with 226/NAMESPACE: ReadWritePaths listed
/run/ndpid (nonexistent) — the socket is /run/secubox/ndpid.sock. Removed it.
Added nginx/ndpid.conf routing /api/v1/ndpid/ to the module socket + rules
install. Service now stable; API serves (status/health 200, gated endpoints
401). Data is empty because nDPId's zmq distributor is not run on gk2 (DPI =
ndpiReader/dpi module); the module plumbing is fixed regardless.
2026-07-10 14:46:02 +02:00
dabdc0aea9 feat(nac): LXC Containers zone auto-fed by interface (br-lxc -> lxc)
Add an 'lxc' zone and interface-based auto-classification: a discovered client
is placed by the interface it was seen on (br-lxc -> lxc, configurable via nac
interface_zones), after explicit nft-set moves but before the legacy JSON
fallback. Applied consistently across /clients, /zones, /summary. Frontend
ZONE_COLOR gains lxc=cyan. gk2: 10 containers in lxc, 3 devices in lan.
2026-07-10 14:27:53 +02:00
999bcefa6c fix(nac): Zones tab shows discovered clients grouped by resolved zone (was empty)
/zones reported only raw nftables set members, which are empty for
ARP-discovered clients that default into a zone but were never added to the
nft set, so every zone card showed 0 members. Group discovered clients by
resolved zone (nft set / JSON assignment / fallback) instead.
2026-07-10 14:21:49 +02:00
4ed0758da8 fix(nac): discover clients on br-lxc + config-extendable lan_interfaces (was 0 clients)
_parse_arp() filtered every neighbour not in a hardcoded LAN_INTERFACES set
(lan0-3/br0/br-lan/eth0-1), so on gk2 (clients on br-lxc + eth2) the dashboard
showed nothing but the template. Add br-lxc to the default set and read extra
interfaces from nac config lan_interfaces. gk2 config adds eth2 → 13 clients.
2026-07-10 14:13:54 +02:00
8a90404770 docs: note mass-reinstall incident + reskin bake-in status 2026-07-10 09:37:15 +02:00
238637e09a release(webui): bump 127 arch:all packages for hybrid-dark reskin bake-in 2026-07-10 09:07:42 +02:00
2ced02d042 feat(webui): hybrid-dark overlay on the hub + waf root dashboards (fleet complete) 2026-07-10 08:59:23 +02:00
258a84a99c feat(webui): hybrid-dark overlay on nested mmpm + podcaster/portal sub-panels 2026-07-10 08:58:21 +02:00
caba368a4e feat(webui): give the Frigate placeholder a proper hybrid-dark page (fleet consistency) 2026-07-10 08:57:57 +02:00
94021e982d feat(webui): extend hybrid-dark overlay to remaining custom panels
Apply the /shared/hybrid-dark.css overlay + hybrid-dark body class to the 10
non-crt-light panels the fleet pass skipped (soc, waf, threat-analyst, health,
security-posture, webradio, master-link, metoblizer, proxypac, sentinelle) via
a universal inject-before-</head> + body-class swap. Flagships (certs/wireguard/
users/nac) stay bespoke; frigate is a redirect stub (left as-is).
2026-07-10 08:56:47 +02:00
3197f866e4 feat(webui): fleet reskin — apply hybrid-dark cyan overlay to all 116 crt-light panels
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.
2026-07-10 08:40:10 +02:00
c4d0484971 feat(nac): reskin Client Guardian panel to hybrid-dark cyan (WebUI guidelines)
Convert /nac/ from light crt-light to dark-cyan hybrid-dark (certs/wireguard):
shared #sidebar, Courier Prime, cyan palette, stat grid + tabs (Clients/Zones/
Quarantine/Parental/Alerts/Policy/History) + dense client rows with state
border. Exposes the full NAC API surface (was clients+quarantine only).
Functionality unchanged (same endpoints/methods/bodies). Security: esc() on all
API values, data-act delegation (mac/zone never interpolated into onclick),
sbx_token bearer, 401 toast.
2026-07-10 08:27:13 +02:00
a9c0f10fcc feat(users): reskin panel to hybrid-dark cyan (WebUI guidelines)
Convert /users/ from light crt-light to the dark-cyan hybrid-dark convention
(certs/wireguard): shared #sidebar, Courier Prime, cyan palette, stat grid +
tabs (Users/Sessions) + dense list rows, toast/modal. Functionality unchanged
(same endpoints/methods/bodies). Security: esc() on all API values, data-act
delegation (no username/session-id interpolated into onclick), sbx_token only
(dropped jwt_token loop), 401 toast. Pre-existing /import response-shape
mismatch left untouched (behavior-preserving) — flagged for follow-up.
2026-07-10 08:24:06 +02:00
9eb89fcd9b feat(metablogizer): version uploads into their Gitea repo (auto commit+push per upload)
- git_commit_push(): stage-all + commit + push origin HEAD, best-effort
  (no repo / no changes / push-rejected never raise; commit kept local)
- /site/{name}/upload wires it under the per-site lock, off the event loop
- postinst rewrites the push URL to Gitea's internal LXC endpoint for the
  service user (public host NAT-hairpins → pack transfer stalls); seeds
  known_hosts. Verified live on gk2: upload → commit → push = ok
2026-07-10 08:22:22 +02:00
48f2ab7e72 docs: add WebUI Panel Guidelines (hybrid-dark cyan convention from certs/wireguard) 2026-07-10 08:18:21 +02:00
8135130a4a fix(wireguard): hard self-lockout warning before bringing down admin/mesh tunnels
Bringing wg-admin down from the panel cut a user's own access with no way to
bring it back from a panel they could no longer reach. Warn explicitly on
admin/mesh bring-down that it may sever access to SecuBox.
2026-07-10 08:16:01 +02:00
6321307288 docs: track wg-toolbox reboot surf-blackhole nft fix 2026-07-10 08:14:41 +02:00
bf0460c189 fix(toolbox): wg-toolbox nft drop-in aborted every boot → tunnel surf blackholed
Root cause of 'surf stops when wg-toolbox VPN activated' after a reboot: the
persistent nft drop-in (loaded atomically by nftables.service via
/etc/nftables.conf include) failed to load, so the wg-toolbox DNAT never
existed after boot and all tunnel traffic was blackholed.

Two boot-time aborts, both fixed:
- referenced 'inet filter input' which does not exist on gk2 at load time →
  fold the UDP-51820 handshake accept into the wg-toolbox table's own input
  base chain (self-contained, no external table dependency)
- used iif/oif "wg-toolbox" (resolves an interface index at LOAD time, fails
  when wg-quick@wg-toolbox has not created the iface yet) → iifname/oifname
  (per-packet name match, load-order independent)

nft -c -f /etc/nftables.conf now passes; the DNAT survives reboot.
2026-07-10 08:13:55 +02:00
b644cd409f docs: WIP checkpoint — wireguard 1.0.3 done, users reskin + webui guidelines next 2026-07-10 07:48:39 +02:00
c61e4967d6 docs: track wireguard 1.0.3 connected-only peers + wg-toolbox peer prune 2026-07-10 07:46:05 +02:00
f25806c7d6 feat(wireguard): show connected-only peers on large tunnels (Show-all toggle) instead of suppressing 2026-07-10 07:45:09 +02:00
577a0037a8 docs: track wireguard 1.0.2 webui rewrite + perf avalanche fix 2026-07-10 07:31:42 +02:00
24972969a9 fix(wireguard): address review — close private-key disclosure in _parse_wg_show, delegate webui handlers (no JS-string interp), invalidate cache on mutations
- _parse_wg_show: parse all-dump by field count (5=iface/9=peer), drop private_key
  (sudo route had made it leak 48 bits via /stats,/summary,/peers/status);
  also restores peer parsing (old detection misclassified every peer line)
- webui: data-attr + delegated click handlers; guard peer-drawer loading flicker
- api: clear read cache after up/down/add/remove so mutations show immediately
2026-07-10 07:29:30 +02:00
f3acebdc4a feat(wireguard): rewrite webui as live interface dashboard + fix status/peers O(peers) subprocess avalanche
- webui: interface-card dashboard (roles, live status, lazy peer drawers,
  up/down + add-peer), certs skin + shared sidebar navbar
- wgctl status/peers: single awk / wg show dump passes (was ~8 procs/peer)
- api: single-flight+TTL cache on read endpoints; kill wgctl child on timeout
- service: NoNewPrivileges=false so sudo -n wgctl elevates
2026-07-10 07:21:42 +02:00
f1f6b23600 release(wireguard): sudo-routed wg show 2026-07-10 06:53:38 +02:00
3433a666b2 fix(wireguard): sudo-route wgctl/wg (root needed for wg show dump) so webui lists interfaces+peers; fix(mochabin): clean netplan — WAN=eth2 DHCP metric100, drop stray lan3 IP (boot-wedge) 2026-07-10 06:53:38 +02:00
9884035e02 Merge: NAC online-clients → one-click Tor-VPN toggle + .onion webui discovery cache (tor 0700 workaround)
Some checks failed
License Headers / check (push) Has been cancelled
2026-07-09 15:32:09 +02:00
a319219efb release: NAC→Tor-VPN toggle (tor+toolbox) 2026-07-09 15:26:36 +02:00
d8397b1fdc feat(tor): NAC online-clients → one-click Tor-VPN routing toggle
Add a "LAN clients (from NAC)" subsection to the Tor-VPN selective-routing
panel in both /tor/ and /toolbox/#tor webuis. Lists NAC clients that are
online and not quarantined, cross-referenced against the current
/toolbox/vpn/clients selectors to derive each toggle's on/off state.
Toggling calls the existing POST/DELETE /toolbox/vpn/client {kind:"ip"}
endpoints and refreshes both lists. IPv6 client IPs are filtered out
client-side (VPN backend is v4-only, Phase 1). Existing free-text
add-selector control (MAC/CIDR/off-NAC clients) is unchanged.
2026-07-09 15:25:47 +02:00
a073b42b5d fix(tor/exposure): .onion webui discovery via root-written cache (tor 0700 blocks secubox-user reads) 2026-07-09 15:19:15 +02:00
204129f23f fix(exposure): reconcile makes HS .onion group-readable for webui discovery 2026-07-09 15:14:21 +02:00
0ccee5028b Merge secubox-reality: VLESS+Reality+Vision (Xray) egress module — API+WebUI, fleet skin, navbar (deployed on gk2)
Some checks are pending
License Headers / check (push) Waiting to run
2026-07-09 15:11:54 +02:00
4a808a4cad Merge Tor Phase-1 enhancement: exit-country, per-client Tor-VPN (leak-proof), emancipate .onion, .onion DNS, obfs4 bridges, /tor/+/toolbox/#tor webui (deployed+verified on gk2) 2026-07-09 15:11:53 +02:00
0d5edcab6f fix(exposure): create /var/lib/tor/hidden_services parent (2750 debian-tor) in postinst — tor config broke + egress died without it (live-found) 2026-07-09 15:11:53 +02:00
db64a056e4 release(reality): 0.1.1 fleet skin + navbar 2026-07-09 15:08:40 +02:00
f9ac5289e6 feat(reality): fleet crt-light skin + shared sidebar + navbar menu entry
Swap the standalone MESH-blue charter in reality.html for the fleet
crt-light/P31-phosphor skin (Courier Prime, --p31-* palette,
.main{margin-left:220px}) matching secubox-tor and other webuis, and
replace the bespoke breadcrumb sidebar with the shared sidebar.js/
sidebar-light.css injector so /reality/ appears in the common navbar.
All panels (server list+create, validate-target, apply, clients+link
QR, volume-guard config+stats) and their JS/fetch wiring are unchanged.

Add menu.d/562-reality.json (mesh category) and install it via
debian/rules, mirroring secubox-tor. python3-segno was already
declared in debian/control.
2026-07-09 15:07:58 +02:00
9bf04ad795 release(tor): 1.0.4 arm toggle 2026-07-09 14:56:22 +02:00
ea5a703092 feat(tor): /tor/ Control Panel arms the transparent Tor egress (tor_mode) + shows armed state 2026-07-09 14:56:22 +02:00
5526144e90 feat(reality): debian packaging — secubox-reality 0.1.0-1~bookworm1
control/rules/postinst/prerm/changelog/compat + 4 systemd units:
secubox-reality.service (API, User=secubox, RuntimeDirectory=secubox
RuntimeDirectoryPreserve=yes per #494/RuntimeDirectory-wipe lessons),
secubox-xray@.service (templated per-egress instance: DynamicUser=yes,
ProtectSystem=strict, LoadCredential for the private-key-bearing config,
AmbientCapabilities=CAP_NET_BIND_SERVICE, NoNewPrivileges=yes),
secubox-reality-monitor.service+.timer (5-min volume-guard sweep).
postinst only chowns /etc/secubox/reality and /var/lib/secubox/reality
(never the shared parents). Verified via a full local dpkg-buildpackage
-us -uc -b (builds clean, correct paths, dh_installsystemd wiring
confirmed).
2026-07-09 13:57:17 +02:00