mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-06-29 10:08:36 +00:00
Compare commits
22 Commits
20dfad720c
...
39d7002b7a
| Author | SHA1 | Date | |
|---|---|---|---|
| 39d7002b7a | |||
| b985db14ff | |||
| 1001d24180 | |||
| 039824dcfa | |||
| 4674b6023a | |||
| 1f21c59c19 | |||
| b41032107a | |||
| 6739e19fea | |||
| b2891ff4d2 | |||
| dba06bc47a | |||
| b24d5bbbe0 | |||
| bb499b7fd5 | |||
| 845683c9c9 | |||
| 04064a9fb7 | |||
| a273cb570a | |||
| 73414e7550 | |||
| 6c55a21df5 | |||
| 6e4ef4d557 | |||
| 87a31fba45 | |||
| c8d0e7d352 | |||
|
|
3fcdb8bd9a | ||
| cc478872ae |
|
|
@ -6595,3 +6595,19 @@ CONFIG_USB_NET_RNDIS_HOST=y
|
||||||
- LAN interfaces scanned: lan0, lan1, lan2, lan3, br0, br-lan, eth0, eth1
|
- LAN interfaces scanned: lan0, lan1, lan2, lan3, br0, br-lan, eth0, eth1
|
||||||
- ARP states mapped to online: REACHABLE, DELAY, PROBE, PERMANENT = online
|
- ARP states mapped to online: REACHABLE, DELAY, PROBE, PERMANENT = online
|
||||||
- STALE, FAILED = offline
|
- STALE, FAILED = offline
|
||||||
|
|
||||||
|
## 2026-06-24 — build+deploy T0 fixes (#494/#519/#53/#421) + dirs-guard /run self-heal
|
||||||
|
|
||||||
|
- Merged #121/#53/#65; cherry-picked #494 onto master (versions re-bumped above
|
||||||
|
master's advanced core 1.1.8/hub 1.4.6 → core 1.1.9, hub 1.4.7).
|
||||||
|
- Discovered #494 was systemic (7 pkgs chowning /run/secubox parent) AND that
|
||||||
|
91 services declare `RuntimeDirectory=secubox` → systemd re-chowns the parent
|
||||||
|
to secubox:secubox 0755 on each start (#421). Central fix: extended
|
||||||
|
secubox-dirs-guard to re-assert /run/secubox 1777 root:root every minute
|
||||||
|
(core 1.1.10) instead of editing 91 units.
|
||||||
|
- Built + deployed to gk2 (8 pkgs): core 1.1.10, hub 1.4.7, eye-remote 1.0.1,
|
||||||
|
metablogizer 1.2.2, metrics 1.0.4, p2p 1.7.1, wazuh 1.0.1, toolbox 2.7.18.
|
||||||
|
First deploy ssh was timeout-killed mid-toolbox-postinst → recovered with
|
||||||
|
dpkg --configure -a (cleared stale lock). Verified: /run/secubox=1777 root:root
|
||||||
|
holds, 0 half-configured, all services + R3 workers active, webui/portal 200,
|
||||||
|
toolbox blacklist-sync (#519) carried.
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,78 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🎯 Backlog priorisé — revue 2026-06-24 (64 issues ouvertes)
|
||||||
|
|
||||||
|
> Index d'autorité du triage. Les sections « Phase X » plus bas sont historiques :
|
||||||
|
> plusieurs portent « ✅ COMPLETE » alors que l'issue est restée **ouverte** (livré
|
||||||
|
> mais jamais fermé) → marquées **[vérifier→fermer]** ci-dessous.
|
||||||
|
|
||||||
|
### 🔴 T0 — Régressions & bugs sécurité (petits, débloquants, CSPN priv-sep)
|
||||||
|
- #494 secubox-core ExecStart écrase tmpfiles.d `/run/secubox` *(worktree actif)*
|
||||||
|
- #468 `/etc/secubox` parent 0750 casse la traversée non-secubox *(régression récurrente)*
|
||||||
|
- #471 secubox-mesh postinst écrase perms `/run/secubox` *(régression)*
|
||||||
|
- #421 sockets `/run/secubox` cachés en mount-ns privé (RuntimeDirectory)
|
||||||
|
- #447 kiosk : mot de passe admin semé par le CI (users.json shippe un hash) **← fuite**
|
||||||
|
- #91 haproxyctl régénère haproxy.cfg avec `waf_inspector` inexistant *(intégrité WAF)*
|
||||||
|
- #65 nginx : routes API manquantes dans webui.conf
|
||||||
|
- #53 Wazuh uvicorn 100% CPU spin
|
||||||
|
- #121 metablog ingest : dirs en `secubox:secubox`
|
||||||
|
|
||||||
|
### 🟠 T1 — Plan d'enforcement sécurité (mission CSPN ; détection→action)
|
||||||
|
- #498 Phase 7 — WAF active enforcement (mitm→CrowdSec→nft drop) *(worktree actif)*
|
||||||
|
- ✅ #519 Phase 13 — enforcement plane **FERMÉ 2026-06-22** (livré + réparé :
|
||||||
|
blacklist-sync avortait sur NXDOMAIN + timeout unit → fix `|| true` +
|
||||||
|
TimeoutStartSec 600 ; vérifié live, default-off). Inclut 13.B #522.
|
||||||
|
- #455 secubox-egress — détection egress + corrélation RDS multi-signaux
|
||||||
|
- #500 Phase 8 — Utiq operator-grade tracking (detect/alert/bypass)
|
||||||
|
- #514 Phase 12 — plateforme anti-human-detection (parent ; sous-tracks fermés)
|
||||||
|
- ✅ #515 Phase 12.A CDN cache detection — **FERMÉ** (live, `social_host_meta.cdn_vendor`)
|
||||||
|
- ✅ #516 Phase 12.B anti-bot detection — **FERMÉ** (live via #564/#565, `social_antibot`)
|
||||||
|
- #525 Phase 14 — plan de déception (idée future, parké)
|
||||||
|
- ⬜ Suivi #519 perf (non bloquant) : DNS-guard ne résout que les 2000 premiers
|
||||||
|
domaines/cycle (5523 en base) → couverture partielle ; résolution séquentielle
|
||||||
|
lourde sur board saturé. Option : résolution parallèle bornée + rotation du cap.
|
||||||
|
|
||||||
|
### 🟡 T2 — UX / Hub / conscommateurs report (worktrees actifs + polish)
|
||||||
|
- #615 security-posture dans la sidebar Hub *(worktree actif)*
|
||||||
|
- #655 webext content-script banner CSP-immune *(worktree actif)*
|
||||||
|
- #485 toolbox SOC scoring *(worktree actif)*
|
||||||
|
- #513 ToolBox WebUI : sous-onglets + retrait UI /admin redondante
|
||||||
|
- #69 diagramme flux trafic responsive
|
||||||
|
- #67 cache history-aware glances/netdata
|
||||||
|
- #68 health checks + dépendances services au démarrage
|
||||||
|
|
||||||
|
### 🟢 T3 — Backlog feature (valeur, non bloquant)
|
||||||
|
- #685 APK 'corrupt' — CI signe avec clé éphémère *(plan APK verrouillé)*
|
||||||
|
- #686 android-toolbox flux non-root cassé *(plan APK verrouillé)*
|
||||||
|
- #429 nextcloud dashboard : API stubs au lieu de la vraie instance *(bug)*
|
||||||
|
- #430 nextcloud — fédération OCM (doc/outillage)
|
||||||
|
- #472 nextcloud — Gondwana Desktop (canvas + widgets)
|
||||||
|
- #592 secubox-webmail-hub (Gmail OAuth2 + Gandi + OVH)
|
||||||
|
- #66 auth Google OAuth
|
||||||
|
- #70 Health Banner System *(preplanned)*
|
||||||
|
- #71 CDN proxy injection *(preplanned)*
|
||||||
|
- #393 source-home des scripts health prober
|
||||||
|
|
||||||
|
### 🔵 T4 — Hardware-gated (dépend de pièces ; piste parallèle ; pas de spare EP06)
|
||||||
|
- Modem/PCIe : #254 modules kernel LTE · #255 pins mPCIe modem · #460 DTS cp0_pcie2 ·
|
||||||
|
#467 U-Boot comphy5 SerDes · #462 pivot HW AR9271/MT
|
||||||
|
- Mesh/BLE : #449 WiFi 802.11s · #452 BT mesh · #453 QR multi-canaux · #454 sourcing BLE 5.x
|
||||||
|
- GSM : #347 sentinelle-gsm
|
||||||
|
- Smart-Strip : #33 module HMI · #42 sous-repo · #379 packaging
|
||||||
|
- Eye-remote : #41 sous-repo · #79 buildroot · #127 variante square · #138 radar_concentric ·
|
||||||
|
#155 collision link-rename *(bug)* · #158 multi-gadget L3 · #478 métriques live Round Eye
|
||||||
|
- VILLAGE3B : #480 dossier presse · #497 poster grand public
|
||||||
|
|
||||||
|
### ⚪ T5 — Images / OS variants (basse urgence)
|
||||||
|
- #446 Full Traveller OS multi-mode/arch · #125 build-live-usb +virtualbox · #422 vm-x64 cascade
|
||||||
|
|
||||||
|
### ⚫ T6 — Docs / housekeeping
|
||||||
|
- #81 headers SPDX CMSD-1.0 partout · #243 clarifier scope secubox-zkp-auth *(question)*
|
||||||
|
- #474 ToolBoX (epic parent — garder comme tracker)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🔥 P0 — Immediate (in flight)
|
## 🔥 P0 — Immediate (in flight)
|
||||||
|
|
||||||
### kbin Tor endpoint — anonymized quick-switch surfing (#683)
|
### kbin Tor endpoint — anonymized quick-switch surfing (#683)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,59 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🗂️ 2026-06-22 : triage issues (30 ouvertes → revue obsolètes)
|
||||||
|
|
||||||
|
- **Fermées (user-validé 2026-06-22)** : #722 (nDPId — décidé contre, reverté) ·
|
||||||
|
#475 ToolBoX Phase 1 (live 2.7.x) · #502/#507/#508 Social mapping (carto +
|
||||||
|
/social/me + report PDF live) · #495 Phase 5 mitm-LXC (superseded par #662 Go
|
||||||
|
sbxmitm host) · #531 APK one-tap (superseded par #685/#686 non-root) ·
|
||||||
|
#486 geoip/ASN+flags+catégories dans rapports (livré master : geo.py + dpi_class.py +
|
||||||
|
report wiring ; complémentaire de #718 ASN collector ; worktree stale nettoyé) ·
|
||||||
|
#515 CDN detection (live `social_host_meta.cdn_vendor`) · #516 anti-bot detection
|
||||||
|
(live via #564/#565) · #519 enforcement plane (livré + **réparé** : blacklist-sync
|
||||||
|
avortait NXDOMAIN + timeout unit → fix `|| true` + TimeoutStartSec 600, vérifié live,
|
||||||
|
default-off ; inclut #522). Toolbox source bumpé 2.7.18 (fix live-patché sur gk2) ·
|
||||||
|
#468 /etc/secubox traversal (source+live = 0755, secrets/CA enfants restent 0750).
|
||||||
|
- **Actives (worktrees en cours)** : #655 webext banner · #615 security-posture ·
|
||||||
|
#494 secubox-core ExecStart · #498 Phase 7 WAF enforcement · #485 SOC scoring.
|
||||||
|
|
||||||
|
### 🔎 Reco T0 — recon live gk2 2026-06-24 (avant fix)
|
||||||
|
- ✅ **#494** : **FIX SYSTÉMIQUE poussé** (`fix/494-…`). Pas que core : 7 units re-chownaient
|
||||||
|
le parent partagé `/run/secubox` (core+hub services, eye-remote/eye-square/metablogizer/
|
||||||
|
metrics/p2p postinsts ; eye-square chownait aussi /var/log/secubox = pire). Tous nettoyés
|
||||||
|
(mkdir fallback only ; logs modules en sous-dossier propre ; orphan /etc/tmpfiles.d nettoyé).
|
||||||
|
**Vérifié live** : /run/secubox 1777 **root:root** stable après restart core ET hub ; webui 200.
|
||||||
|
Bumps core 1.1.7/hub 1.4.4/eye-remote 1.0.1/eye-square 1.0.4/metablog 1.2.2/metrics 1.0.4/p2p 1.7.1.
|
||||||
|
- ✅ **#471** (mesh /run/secubox) : déjà résolu (changelog mesh "drop install -d /run/secubox") → verify-close.
|
||||||
|
- ⬜ **#421** : sockets cachés en mount-ns privé (RuntimeDirectory) — mécanisme distinct, non traité.
|
||||||
|
- 🆕 Suivi (classe #511) : mesh/toolbox/admin font `install -d -o <module> /var/log/secubox`
|
||||||
|
(propriétaire du parent partagé = user module) → autres daemons ne peuvent créer leurs logs.
|
||||||
|
Séparé de #494, à traiter (sous-dossiers propres comme fait pour eye-square/p2p).
|
||||||
|
- **#447** : pas une fuite — `password_hash=null` → lockout kiosk + user CI parasite ;
|
||||||
|
**CI-image-gated** (rpi400, pas gk2).
|
||||||
|
- **#91** : `haproxy.cfg` active valide ; backup `*.broken-by-haproxyctl-*` prouve le bug
|
||||||
|
passé ; drift-guard #627 rattrape. Root cause = generate `haproxyctl` (api/main.py l.846/896).
|
||||||
|
- ✅ **#53** : **FIX poussé** (`fix/53-…`) — gate `ConditionPathExists=/var/ossec/etc/ossec.conf`
|
||||||
|
+ `RestartSec=5` ; module conservé (SIEM opt-in). Vérifié gk2 (/var/ossec absent). Bump 1.0.1.
|
||||||
|
- ✅ **#65** : déjà résolu en prod (webui.conf déployé inclut `secubox-routes.d/*.conf`,
|
||||||
|
163 snippets). Template `common/nginx/webui.conf` (stale) synchronisé sur `feature/65-…`.
|
||||||
|
Reco fermer. Convention : `secubox-routes.d/`=actif, `secubox.d/`=legacy.
|
||||||
|
- ✅ **#121** : **FIX poussé** (`fix/121-…`) — helper `fix_perms` chown -R secubox:secubox
|
||||||
|
le site dir après chaque ingest .git (metablog-ingest-site.sh). Script dev, pas de deploy.
|
||||||
|
- ⬜ Restent : **#91** (deploy WAF risqué) · **#65** (refactor include, risque 502) ·
|
||||||
|
**#447** (CI kiosk) · **#494/#471/#421** (worktree fix/494). Build+deploy toolbox 2.7.18 (#519) en attente.
|
||||||
|
- **Backlog/future** : #685/#686 APK non-root (plan verrouillé) · #592 webmail-hub ·
|
||||||
|
#514/#515/#516/#519/#522/#525 Phase 12-14 (#515 CDN / #516 anti-bot partiellement
|
||||||
|
couverts par antibot_sites/opgrade_sites du social graph) · #500 Utiq · #497/#480/
|
||||||
|
#478 VILLAGE3B Eye/poster · #472/#430/#429 Nextcloud · #471/#468/#421 perms (à
|
||||||
|
vérifier si déjà corrigées) · #467/#462/#460/#255/#254 hardware/kernel · #455 egress ·
|
||||||
|
#454/#453/#452/#449 mesh/BLE · #448/#447/#446/#434 kiosk · #422 vm cascade ·
|
||||||
|
#393/#379/#347 packaging · #513 WebUI sub-tabs.
|
||||||
|
- ⚠️ Fermeture finale = **user only** (sauf issues créées en session) ; les
|
||||||
|
recommandations ci-dessus sont commentées sur chaque issue.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ✅ 2026-06-22 : DPI exfil + Netrunner report + sbxmitm fixes (tous mergés, live gk2)
|
## ✅ 2026-06-22 : DPI exfil + Netrunner report + sbxmitm fixes (tous mergés, live gk2)
|
||||||
|
|
||||||
Session livrée intégralement sur master + déployée. Détail dans HISTORY 2026-06-22.
|
Session livrée intégralement sur master + déployée. Détail dans HISTORY 2026-06-22.
|
||||||
|
|
|
||||||
|
|
@ -55,4 +55,13 @@ server {
|
||||||
proxy_pass http://unix:/run/secubox/system.sock:/;
|
proxy_pass http://unix:/run/secubox/system.sock:/;
|
||||||
include /etc/nginx/snippets/secubox-proxy.conf;
|
include /etc/nginx/snippets/secubox-proxy.conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# #65: per-module routes self-register here. Every module package drops a
|
||||||
|
# /etc/nginx/secubox-routes.d/<module>.conf (location-only snippet) at
|
||||||
|
# install time, so a newly added module's /<module>/ + /api/v1/<module>/
|
||||||
|
# routes are picked up automatically — no more hand-editing this file per
|
||||||
|
# module. This is the ACTIVE include (matches the deployed webui.conf).
|
||||||
|
# The crowdsec/waf/system blocks above stay hardcoded: those core packages
|
||||||
|
# only ship the legacy secubox.d/ snippet, so they would NOT duplicate here.
|
||||||
|
include /etc/nginx/secubox-routes.d/*.conf;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,38 @@
|
||||||
|
secubox-core (1.1.10-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #421/#494: secubox-dirs-guard now also re-asserts /run/secubox to
|
||||||
|
1777 root:root every minute. 90+ services declare RuntimeDirectory=secubox,
|
||||||
|
so systemd re-chowns the shared socket parent to secubox:secubox 0755 on
|
||||||
|
each (re)start — which locks out services that create their socket as a
|
||||||
|
non-secubox user (e.g. secubox-blacklist-attrib "Permission denied").
|
||||||
|
Removing the explicit chowns (#494) was necessary but not sufficient against
|
||||||
|
the RuntimeDirectory churn; the guard now self-heals it centrally instead of
|
||||||
|
patching 90+ unit files.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 11:15:00 +0000
|
||||||
|
|
||||||
|
secubox-core (1.1.9-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #494 fix: secubox-core.service no longer chowns /run/secubox to
|
||||||
|
secubox:secubox + chmod 775 in ExecStart. Running After=network.target,
|
||||||
|
it fired LAST and clobbered the canonical tmpfiles rule
|
||||||
|
(`d /run/secubox 1777 root root`) that secubox-runtime.service had just
|
||||||
|
re-applied — leaving the dir secubox:secubox so non-secubox daemons lost
|
||||||
|
parent +x and their sockets 502'd. /run/secubox lifecycle is now owned
|
||||||
|
solely by tmpfiles.d (1777 root root) + secubox-runtime.service; this unit
|
||||||
|
only manages the www-data group membership. Complements the #623 sweep
|
||||||
|
(which covered /var/lib,/var/log,/var/cache,/etc,/usr/share — not /run).
|
||||||
|
* Defensive: postinst removes a stale, non-dpkg-owned
|
||||||
|
/etc/tmpfiles.d/secubox.conf if it declares /run/secubox as
|
||||||
|
0775 secubox secubox (leftover from an old manual fix; /etc overrides
|
||||||
|
/usr/lib so it contradicted the canonical rule). The shipped canonical
|
||||||
|
file is /usr/lib/tmpfiles.d/secubox.conf.
|
||||||
|
* Also ensure /var/log/suricata exists (0755) so services with
|
||||||
|
ReadWritePaths=/var/log/suricata (secubox-threats/suricata) don't
|
||||||
|
NAMESPACE-fail at start.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:30:00 +0000
|
||||||
|
|
||||||
secubox-core (1.1.8-1~bookworm1) bookworm; urgency=medium
|
secubox-core (1.1.8-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* fix(#623): tmpfiles.d now declares all shared secubox parents
|
* fix(#623): tmpfiles.d now declares all shared secubox parents
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,16 @@ case "$1" in
|
||||||
install -d -o secubox -g secubox -m 755 /usr/share/secubox/www
|
install -d -o secubox -g secubox -m 755 /usr/share/secubox/www
|
||||||
|
|
||||||
# ── tmpfiles.d for persistent /run/secubox ──
|
# ── tmpfiles.d for persistent /run/secubox ──
|
||||||
|
# #494: drop a stale, non-dpkg-owned /etc/tmpfiles.d/secubox.conf left over
|
||||||
|
# from an old manual fix — it declares /run/secubox as 0775 secubox secubox
|
||||||
|
# and, because /etc overrides /usr/lib, it contradicts the canonical rule
|
||||||
|
# (/usr/lib/tmpfiles.d/secubox.conf: 1777 root root). Only remove it if it
|
||||||
|
# is NOT owned by any package and carries that bad declaration.
|
||||||
|
if [ -f /etc/tmpfiles.d/secubox.conf ] \
|
||||||
|
&& ! dpkg -S /etc/tmpfiles.d/secubox.conf >/dev/null 2>&1 \
|
||||||
|
&& grep -qE '/run/secubox[[:space:]]+0?775[[:space:]]+secubox' /etc/tmpfiles.d/secubox.conf; then
|
||||||
|
rm -f /etc/tmpfiles.d/secubox.conf
|
||||||
|
fi
|
||||||
systemd-tmpfiles --create 2>/dev/null || true
|
systemd-tmpfiles --create 2>/dev/null || true
|
||||||
|
|
||||||
# ── Config initiale si absente ──
|
# ── Config initiale si absente ──
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,26 @@ After=network.target
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
ExecStart=/bin/mkdir -p /run/secubox
|
# Phase 3 (#494) : /run/secubox is owned by tmpfiles.d at 1777 root:root.
|
||||||
ExecStart=/bin/chown secubox:secubox /run/secubox
|
# The previous ExecStart sequence (mkdir + chown secubox:secubox + chmod 775)
|
||||||
ExecStart=/bin/chmod 775 /run/secubox
|
# ran AFTER tmpfiles.d and silently regressed the dir to 775, breaking cross-
|
||||||
# Add www-data to secubox group if not already (for nginx access)
|
# user traversal for daemons running as users other than secubox (e.g.
|
||||||
|
# secubox-threats which 226/NAMESPACE-failed because ReadWritePaths bind
|
||||||
|
# couldn't cross 775 secubox:secubox + missing /var/log/suricata). See
|
||||||
|
# secubox-core/debian/changelog 1.1.7 for the failure post-mortem.
|
||||||
|
#
|
||||||
|
# /run/secubox lifecycle is now :
|
||||||
|
# 1. tmpfiles.d/secubox.conf creates 1777 root:root
|
||||||
|
# 2. this service is a no-op for /run/secubox (NOT touched)
|
||||||
|
# 3. each module's postinst chmods its own socket via UMask if needed
|
||||||
|
|
||||||
|
# /var/log/suricata must exist for any service whose ReadWritePaths references
|
||||||
|
# it (secubox-threats, secubox-suricata). Create idempotently.
|
||||||
|
ExecStart=/bin/mkdir -p /var/log/suricata
|
||||||
|
ExecStart=/bin/sh -c "chown secubox:secubox /var/log/suricata 2>/dev/null || true"
|
||||||
|
ExecStart=/bin/chmod 0755 /var/log/suricata
|
||||||
|
|
||||||
|
# Add www-data to secubox group (for nginx static-file access).
|
||||||
ExecStart=/bin/sh -c "id -nG www-data | grep -q secubox || usermod -aG secubox www-data"
|
ExecStart=/bin/sh -c "id -nG www-data | grep -q secubox || usermod -aG secubox www-data"
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,17 @@ for d in /var/lib/secubox /var/log/secubox /var/cache/secubox /etc/secubox /usr/
|
||||||
[ -d "$d" ] || continue
|
[ -d "$d" ] || continue
|
||||||
[ "$(stat -c %a "$d" 2>/dev/null)" = "755" ] || chmod 0755 "$d" 2>/dev/null || true
|
[ "$(stat -c %a "$d" 2>/dev/null)" = "755" ] || chmod 0755 "$d" 2>/dev/null || true
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# /run/secubox is the world-writable socket dir and MUST stay 1777 root:root so
|
||||||
|
# a service running as ANY user can create its own socket. 90+ services declare
|
||||||
|
# `RuntimeDirectory=secubox`, which makes systemd re-chown the parent to
|
||||||
|
# secubox:secubox 0755 on every (re)start — locking out non-secubox socket
|
||||||
|
# creators (e.g. blacklist-attrib: "Permission denied") (#421/#494). Re-assert
|
||||||
|
# the canonical perms here so that flapping self-heals each minute.
|
||||||
|
if [ -d /run/secubox ]; then
|
||||||
|
[ "$(stat -c '%a %U %G' /run/secubox 2>/dev/null)" = "1777 root root" ] || {
|
||||||
|
chown root:root /run/secubox 2>/dev/null || true
|
||||||
|
chmod 1777 /run/secubox 2>/dev/null || true
|
||||||
|
}
|
||||||
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
secubox-eye-remote (1.0.1-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #494: postinst no longer chowns the shared /run/secubox parent to
|
||||||
|
secubox:secubox (tmpfiles.d owns it 1777 root root).
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:45:00 +0000
|
||||||
|
|
||||||
secubox-eye-remote (1.0.0-1) bookworm; urgency=medium
|
secubox-eye-remote (1.0.0-1) bookworm; urgency=medium
|
||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@ case "$1" in
|
||||||
# Reload udev rules
|
# Reload udev rules
|
||||||
udevadm control --reload-rules || true
|
udevadm control --reload-rules || true
|
||||||
|
|
||||||
# Create runtime directory
|
# Create runtime directory (fallback). #494: never chown the shared
|
||||||
|
# parent — it is owned by tmpfiles.d (1777 root root); chowning it to
|
||||||
|
# secubox:secubox breaks cross-user socket traversal.
|
||||||
mkdir -p /run/secubox
|
mkdir -p /run/secubox
|
||||||
chown secubox:secubox /run/secubox 2>/dev/null || true
|
|
||||||
|
|
||||||
# Ensure config dirs exist with correct ownership.
|
# Ensure config dirs exist with correct ownership.
|
||||||
# 0755 on eye-remote/ so the dnsmasq daemon (running as dnsmasq:nogroup
|
# 0755 on eye-remote/ so the dnsmasq daemon (running as dnsmasq:nogroup
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
|
secubox-eye-square (1.0.4-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #494/#511: postinst no longer chowns the shared parents /run/secubox and
|
||||||
|
/var/log/secubox to secubox-eye-square (it stripped every other daemon's
|
||||||
|
traversal/log access). Logs go to an own subdir /var/log/secubox/eye-square.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:45:00 +0000
|
||||||
|
|
||||||
secubox-eye-square (1.0.3-1~bookworm1) bookworm; urgency=medium
|
secubox-eye-square (1.0.3-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* Relocate firstboot.sh from /usr/local/sbin/ to /usr/lib/secubox/
|
* Relocate firstboot.sh from /usr/local/sbin/ to /usr/lib/secubox/
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,15 @@ case "$1" in
|
||||||
useradd --system --no-create-home --shell /usr/sbin/nologin secubox-eye-square
|
useradd --system --no-create-home --shell /usr/sbin/nologin secubox-eye-square
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure runtime + audit dirs
|
# Ensure runtime + audit dirs.
|
||||||
|
# #494/#511: NEVER chown the shared parents (/run/secubox,
|
||||||
|
# /var/log/secubox) to a module user — that strips other daemons of
|
||||||
|
# traversal and 502s the whole board. The parents are owned by
|
||||||
|
# tmpfiles.d/secubox-core (1777 root root / 0755). This module creates
|
||||||
|
# its socket inside the 1777 sticky dir and logs into its OWN subdir.
|
||||||
mkdir -p /run/secubox /var/log/secubox
|
mkdir -p /run/secubox /var/log/secubox
|
||||||
chown secubox-eye-square:secubox-eye-square /run/secubox /var/log/secubox
|
install -d -o secubox-eye-square -g secubox-eye-square -m 0750 \
|
||||||
|
/var/log/secubox/eye-square
|
||||||
|
|
||||||
# Activate AppArmor profile
|
# Activate AppArmor profile
|
||||||
if command -v apparmor_parser >/dev/null 2>&1; then
|
if command -v apparmor_parser >/dev/null 2>&1; then
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
|
secubox-hub (1.4.7-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #494: drop the ExecStartPre chown/chmod of the shared /run/secubox parent.
|
||||||
|
It ran on every hub start and clobbered the canonical tmpfiles rule
|
||||||
|
(1777 root root) back to 775 secubox:secubox, 502'ing cross-user socket
|
||||||
|
traversal. The 1777 sticky parent already lets the hub create its socket.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:45:00 +0000
|
||||||
|
|
||||||
secubox-hub (1.4.6-1~bookworm1) bookworm; urgency=medium
|
secubox-hub (1.4.6-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* perf(#644): dashboard/status/modules/alerts + public/health-batch now served
|
* perf(#644): dashboard/status/modules/alerts + public/health-batch now served
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,13 @@ User=secubox
|
||||||
Group=secubox
|
Group=secubox
|
||||||
WorkingDirectory=/usr/lib/secubox/hub
|
WorkingDirectory=/usr/lib/secubox/hub
|
||||||
# Ensure /run/secubox exists before starting (fallback if secubox-core didn't run)
|
# Ensure /run/secubox exists before starting (fallback if secubox-core didn't run)
|
||||||
# + prefix runs as root regardless of User= setting
|
# + prefix runs as root regardless of User= setting.
|
||||||
|
# #494: do NOT chown/chmod the parent here — it is owned by tmpfiles.d
|
||||||
|
# (1777 root root) + secubox-runtime.service. This ExecStartPre ran on every
|
||||||
|
# hub (re)start and clobbered it back to 775 secubox:secubox, breaking
|
||||||
|
# cross-user socket traversal (nginx/www-data 502). The sticky 1777 parent
|
||||||
|
# already lets the hub create its own socket; only mkdir as a fallback.
|
||||||
ExecStartPre=+/bin/mkdir -p /run/secubox
|
ExecStartPre=+/bin/mkdir -p /run/secubox
|
||||||
ExecStartPre=+/bin/chown secubox:secubox /run/secubox
|
|
||||||
ExecStartPre=+/bin/chmod 775 /run/secubox
|
|
||||||
# TCP binding for VM compatibility (Unix socket has issues in some VMs)
|
# TCP binding for VM compatibility (Unix socket has issues in some VMs)
|
||||||
ExecStart=/usr/bin/python3 -m uvicorn api.main:app \
|
ExecStart=/usr/bin/python3 -m uvicorn api.main:app \
|
||||||
--host 127.0.0.1 --port 8001 \
|
--host 127.0.0.1 --port 8001 \
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
secubox-metablogizer (1.2.2-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #494: postinst no longer chowns the shared /run/secubox parent; only the
|
||||||
|
module's own /srv/metablogizer/sites is owned (see also #121).
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:45:00 +0000
|
||||||
|
|
||||||
secubox-metablogizer (1.2.1-1~bookworm1) bookworm; urgency=medium
|
secubox-metablogizer (1.2.1-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* debian/secubox-metablogizer.service: lower uvicorn --log-level from
|
* debian/secubox-metablogizer.service: lower uvicorn --log-level from
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
mkdir -p /run/secubox /srv/metablogizer/sites
|
mkdir -p /run/secubox /srv/metablogizer/sites
|
||||||
chown secubox:secubox /run/secubox /srv/metablogizer/sites 2>/dev/null || true
|
# #494: do NOT chown the shared parent /run/secubox (owned by tmpfiles.d at
|
||||||
|
# 1777 root root). Only own this module's own data dir.
|
||||||
|
chown secubox:secubox /srv/metablogizer/sites 2>/dev/null || true
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable secubox-metablogizer 2>/dev/null || true
|
systemctl enable secubox-metablogizer 2>/dev/null || true
|
||||||
systemctl start secubox-metablogizer 2>/dev/null || true
|
systemctl start secubox-metablogizer 2>/dev/null || true
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
secubox-metrics (1.0.4-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #494: postinst no longer chowns the shared /run/secubox parent
|
||||||
|
(tmpfiles.d owns it 1777 root root).
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:45:00 +0000
|
||||||
|
|
||||||
secubox-metrics (1.0.3-1~bookworm1) bookworm; urgency=medium
|
secubox-metrics (1.0.3-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* VisitorOrigin: add AmbientCapabilities=CAP_NET_ADMIN to the systemd
|
* VisitorOrigin: add AmbientCapabilities=CAP_NET_ADMIN to the systemd
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ case "$1" in
|
||||||
useradd -r -s /usr/sbin/nologin -d /var/lib/secubox secubox
|
useradd -r -s /usr/sbin/nologin -d /var/lib/secubox secubox
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create runtime directory
|
# Create runtime directory (fallback). #494: never chown the shared
|
||||||
|
# parent /run/secubox — it is owned by tmpfiles.d (1777 root root).
|
||||||
mkdir -p /run/secubox
|
mkdir -p /run/secubox
|
||||||
chown secubox:secubox /run/secubox
|
|
||||||
|
|
||||||
# Cache directory — was /tmp/secubox until #149. Now /var/cache/secubox,
|
# Cache directory — was /tmp/secubox until #149. Now /var/cache/secubox,
|
||||||
# auto-managed by systemd's CacheDirectory=secubox at unit start.
|
# auto-managed by systemd's CacheDirectory=secubox at unit start.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
secubox-p2p (1.7.1-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #494/#511: postinst no longer chowns the shared parents /run/secubox and
|
||||||
|
/var/log/secubox; logs go to an own subdir /var/log/secubox/p2p.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:45:00 +0000
|
||||||
|
|
||||||
secubox-p2p (1.7.0-1~bookworm1) bookworm; urgency=medium
|
secubox-p2p (1.7.0-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* Absorb the master-link role from secubox-master-link (closes #384)
|
* Absorb the master-link role from secubox-master-link (closes #384)
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ case "$1" in
|
||||||
adduser --system --group --home /var/lib/secubox --no-create-home secubox
|
adduser --system --group --home /var/lib/secubox --no-create-home secubox
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create runtime directory
|
# Create runtime directory (fallback). #494: never chown/chmod the
|
||||||
|
# shared parent /run/secubox — tmpfiles.d owns it (1777 root root);
|
||||||
|
# overriding it breaks cross-user socket traversal.
|
||||||
mkdir -p /run/secubox
|
mkdir -p /run/secubox
|
||||||
chown secubox:secubox /run/secubox
|
|
||||||
chmod 755 /run/secubox
|
|
||||||
|
|
||||||
# Create log directory
|
# Log dir: own a subdir, do NOT chown the shared /var/log/secubox
|
||||||
|
# parent (#494/#511 — that strips other modules' log access).
|
||||||
mkdir -p /var/log/secubox
|
mkdir -p /var/log/secubox
|
||||||
chown secubox:secubox /var/log/secubox
|
install -d -o secubox -g secubox -m 0750 /var/log/secubox/p2p
|
||||||
chmod 755 /var/log/secubox
|
|
||||||
|
|
||||||
# Enable and start the service
|
# Enable and start the service
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,10 @@ func injectInlineBanner(body []byte, scriptBody string) []byte {
|
||||||
// match would never fire. Mirrors the Python request() p.startswith(...) checks.
|
// match would never fire. Mirrors the Python request() p.startswith(...) checks.
|
||||||
func isToolboxAssetPath(path string) bool {
|
func isToolboxAssetPath(path string) bool {
|
||||||
return strings.HasPrefix(path, "/__toolbox/loader.js") ||
|
return strings.HasPrefix(path, "/__toolbox/loader.js") ||
|
||||||
strings.HasPrefix(path, "/__toolbox/bundle")
|
strings.HasPrefix(path, "/__toolbox/bundle") ||
|
||||||
|
// #724 — banner R0..R3 level switch: same-origin GET from the page,
|
||||||
|
// reverse-proxied to the portal /__toolbox/set-level.
|
||||||
|
strings.HasPrefix(path, "/__toolbox/set-level")
|
||||||
}
|
}
|
||||||
|
|
||||||
// portalTargetURL builds the absolute portal URL for an intercepted asset
|
// portalTargetURL builds the absolute portal URL for an intercepted asset
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
secubox-toolbox-ng (0.1.15-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #724 — intercept /__toolbox/set-level (banner level switch) and reverse-proxy
|
||||||
|
it to the portal, same as /__toolbox/loader.js + /__toolbox/bundle.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Mon, 22 Jun 2026 15:10:00 +0000
|
||||||
|
|
||||||
secubox-toolbox-ng (0.1.14-1~bookworm1) bookworm; urgency=medium
|
secubox-toolbox-ng (0.1.14-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* quic/banner: strip Alt-Svc response header so browsers stop learning/preferring
|
* quic/banner: strip Alt-Svc response header so browsers stop learning/preferring
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,31 @@
|
||||||
|
secubox-toolbox (2.7.18-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #519/#522 fix(blacklist-sync): the DNS-guard domain loop aborted the whole
|
||||||
|
enforcement sync on the first unresolvable blocklisted domain — getent
|
||||||
|
returns exit 2 on NXDOMAIN and, under set -euo pipefail, the
|
||||||
|
`ips=$(getent ... | awk | sort)` assignment propagated that 2 (status=2,
|
||||||
|
INVALIDARGUMENT under systemd). Blocklisted domains are overwhelmingly
|
||||||
|
dead/sinkholed, so the oneshot failed every run → the nft blacklist_v4/v6
|
||||||
|
sets were never populated and the protection enforcement plane was inert.
|
||||||
|
Guard the substitution with `|| true` so a dead domain is skipped, not fatal.
|
||||||
|
* #519/#522 fix(blacklist-sync): a full DNS-guard sweep (~700 live resolutions)
|
||||||
|
runs ~3min on a loaded board but the unit's TimeoutStartSec was 120s →
|
||||||
|
systemd SIGTERM'd the oneshot before it loaded the sets. Raise to 600s and
|
||||||
|
drop the per-lookup timeout default 2s→1s so a sweep finishes well within it.
|
||||||
|
Verified live on gk2: sets populate (blacklist_v4=1675, blacklist_v6=207).
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 09:30:00 +0000
|
||||||
|
|
||||||
|
secubox-toolbox (2.7.17-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #724 banner: in-banner R0..R3 level switch — the injected transparency
|
||||||
|
banner now shows the real client level AND lets the client change it inline
|
||||||
|
(GET /__toolbox/set-level, reverse-proxied by sbxmitm like /__toolbox/bundle;
|
||||||
|
store.set_client_level by hash; bundle cache invalidated so it reflects at
|
||||||
|
once). r2 gated by config, r3 by wg server.pubkey.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Mon, 22 Jun 2026 15:10:00 +0000
|
||||||
|
|
||||||
secubox-toolbox (2.7.16-1~bookworm1) bookworm; urgency=medium
|
secubox-toolbox (2.7.16-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* fix: restore banner on heavy sites (leparisien.fr). The #685 stream_large_bodies=1m
|
* fix: restore banner on heavy sites (leparisien.fr). The #685 stream_large_bodies=1m
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,18 @@ fi
|
||||||
# Bounded : cap on domains/cycle + per-lookup timeout so the sync never
|
# Bounded : cap on domains/cycle + per-lookup timeout so the sync never
|
||||||
# hangs on a dead resolver.
|
# hangs on a dead resolver.
|
||||||
DOMAIN_CAP="${SECUBOX_BL_DOMAIN_CAP:-2000}"
|
DOMAIN_CAP="${SECUBOX_BL_DOMAIN_CAP:-2000}"
|
||||||
RESOLVE_TIMEOUT="${SECUBOX_BL_RESOLVE_TIMEOUT:-2}"
|
RESOLVE_TIMEOUT="${SECUBOX_BL_RESOLVE_TIMEOUT:-1}"
|
||||||
resolved_domains=0
|
resolved_domains=0
|
||||||
if [ -r "$TOOLBOX_DB" ] && command -v sqlite3 >/dev/null 2>&1; then
|
if [ -r "$TOOLBOX_DB" ] && command -v sqlite3 >/dev/null 2>&1; then
|
||||||
while IFS= read -r dom; do
|
while IFS= read -r dom; do
|
||||||
[ -n "$dom" ] || continue
|
[ -n "$dom" ] || continue
|
||||||
# getent ahosts returns both A + AAAA ; timeout guards a dead lookup.
|
# getent ahosts returns both A + AAAA ; timeout guards a dead lookup.
|
||||||
|
# NXDOMAIN makes getent exit 2 → with pipefail+set -e the assignment
|
||||||
|
# would abort the whole sync on the first dead blocklisted domain
|
||||||
|
# (and blocklisted domains are overwhelmingly dead/sinkholed). Guard
|
||||||
|
# the substitution so an unresolvable domain is simply skipped.
|
||||||
ips=$(timeout "$RESOLVE_TIMEOUT" getent ahosts "$dom" 2>/dev/null \
|
ips=$(timeout "$RESOLVE_TIMEOUT" getent ahosts "$dom" 2>/dev/null \
|
||||||
| awk '{print $1}' | sort -u)
|
| awk '{print $1}' | sort -u || true)
|
||||||
if [ -n "$ips" ]; then
|
if [ -n "$ips" ]; then
|
||||||
printf '%s\n' "$ips" >> "$TMP4.raw"
|
printf '%s\n' "$ips" >> "$TMP4.raw"
|
||||||
resolved_domains=$((resolved_domains + 1))
|
resolved_domains=$((resolved_domains + 1))
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,39 @@ async def toolbox_bundle(mh: str = Query(default=""), wg: int = Query(default=0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/__toolbox/set-level")
|
||||||
|
async def toolbox_set_level(mh: str = Query(default=""), level: str = Query(default="")) -> JSONResponse:
|
||||||
|
"""#724 — banner self-service level switch. Reverse-proxied to the portal by
|
||||||
|
sbxmitm (same-origin from the page) like /__toolbox/bundle, so it carries no
|
||||||
|
cookies/CSRF and is identified by the client's baked ``mh`` hash. Persists the
|
||||||
|
analysis tier for that hash (R3 wg peers have no captive MAC, so this is the
|
||||||
|
by-hash setter, not the nft-based /change-level)."""
|
||||||
|
mh = (mh or "").strip().lower()
|
||||||
|
level = (level or "").strip().lower()
|
||||||
|
if not (mh and all(c in "0123456789abcdef" for c in mh) and 8 <= len(mh) <= 64):
|
||||||
|
return JSONResponse({"ok": False, "error": "bad mh"}, status_code=400,
|
||||||
|
headers={"Cache-Control": "no-store"})
|
||||||
|
if level not in ("r0", "r1", "r2", "r3"):
|
||||||
|
return JSONResponse({"ok": False, "error": "bad level"}, status_code=400,
|
||||||
|
headers={"Cache-Control": "no-store"})
|
||||||
|
# honour the same gates as /change-level
|
||||||
|
try:
|
||||||
|
cfg = _get_cfg()
|
||||||
|
if level == "r2" and not cfg.r2.enabled:
|
||||||
|
level = "r1"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if level == "r3" and not Path("/etc/secubox/toolbox/wg/server.pubkey").exists():
|
||||||
|
level = "r1"
|
||||||
|
try:
|
||||||
|
store.set_client_level(mh, level)
|
||||||
|
bundlemod.invalidate(mh) # drop cached bundle so the new level shows at once
|
||||||
|
except Exception as e: # pragma: no cover
|
||||||
|
return JSONResponse({"ok": False, "error": str(e)}, status_code=500,
|
||||||
|
headers={"Cache-Control": "no-store"})
|
||||||
|
return JSONResponse({"ok": True, "level": level}, headers={"Cache-Control": "no-store"})
|
||||||
|
|
||||||
|
|
||||||
@router.get("/__toolbox/inline")
|
@router.get("/__toolbox/inline")
|
||||||
async def toolbox_inline(
|
async def toolbox_inline(
|
||||||
mh: str = Query(default=""),
|
mh: str = Query(default=""),
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,14 @@ def build_bundle(client_id: str, is_wg: bool = False) -> dict:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def invalidate(client_id: str) -> None:
|
||||||
|
"""#724 — drop a client's cached bundle (both wg variants) so a level switch
|
||||||
|
is reflected on the next banner render without waiting for the TTL."""
|
||||||
|
cid = client_id or ""
|
||||||
|
for k in ((cid, True), (cid, False)):
|
||||||
|
_cache.pop(k, None)
|
||||||
|
|
||||||
|
|
||||||
def get_bundle(client_id: str, is_wg: bool = False) -> dict:
|
def get_bundle(client_id: str, is_wg: bool = False) -> dict:
|
||||||
"""Return the cached bundle for a client, rebuilding past the TTL. Fail-open."""
|
"""Return the cached bundle for a client, rebuilding past the TTL. Fail-open."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -163,6 +171,35 @@ _BANNER_CORE = r"""
|
||||||
var c = document.getElementById("sbx-ck");
|
var c = document.getElementById("sbx-ck");
|
||||||
if (c) c.textContent = "🍪 " + countCookies() + " cookies";
|
if (c) c.textContent = "🍪 " + countCookies() + " cookies";
|
||||||
}
|
}
|
||||||
|
// #724 — inline R0..R3 level switch. Shows the real current level (highlighted)
|
||||||
|
// and lets the client change it: GET /__toolbox/set-level (same-origin, the Go
|
||||||
|
// engine reverse-proxies it to the portal), then reload so the new tier applies.
|
||||||
|
function lvlSwitch(b){
|
||||||
|
var cur = String(b.level || "r1").toLowerCase();
|
||||||
|
var lv = ["r0","r1","r2","r3"], out = "<span id=\"sbx-lvl\" title=\"Niveau d'analyse — clique pour changer\">";
|
||||||
|
for (var i=0;i<lv.length;i++){ var on = lv[i]===cur;
|
||||||
|
out += "<button data-lvl=\"" + lv[i] + "\" class=\"sbx-lvl\" style=\"background:"
|
||||||
|
+ (on?"#148C66":"transparent") + ";color:" + (on?"#0A0E14":"#8A9AA8")
|
||||||
|
+ ";border:1px solid #148C66;border-radius:3px;padding:0 5px;margin:0 1px;"
|
||||||
|
+ "font:inherit;font-size:11px;cursor:pointer\">" + lv[i].toUpperCase() + "</button>";
|
||||||
|
}
|
||||||
|
return out + "</span>";
|
||||||
|
}
|
||||||
|
function wireLevels(bar, b){
|
||||||
|
var els = bar.querySelectorAll(".sbx-lvl");
|
||||||
|
for (var i=0;i<els.length;i++){ (function(el){
|
||||||
|
el.onclick = function(){
|
||||||
|
var lvl = el.getAttribute("data-lvl");
|
||||||
|
var who = (typeof mh !== "undefined" && mh) ? mh : (b.client_id || "");
|
||||||
|
if (!who) return;
|
||||||
|
el.textContent = "…";
|
||||||
|
fetch("/__toolbox/set-level?mh=" + encodeURIComponent(who) + "&level=" + lvl,
|
||||||
|
{credentials:"omit", cache:"no-store"})
|
||||||
|
.then(function(r){ if (r && r.ok) { location.reload(); } else { el.textContent = lvl.toUpperCase(); } })
|
||||||
|
.catch(function(){ el.textContent = lvl.toUpperCase(); });
|
||||||
|
};
|
||||||
|
})(els[i]); }
|
||||||
|
}
|
||||||
function render(b){
|
function render(b){
|
||||||
if (dismissed) return;
|
if (dismissed) return;
|
||||||
if (document.getElementById("sbx-banner")) return;
|
if (document.getElementById("sbx-banner")) return;
|
||||||
|
|
@ -184,7 +221,7 @@ _BANNER_CORE = r"""
|
||||||
bar.innerHTML = "<b style=\"color:#148C66\">SecuBox</b>"
|
bar.innerHTML = "<b style=\"color:#148C66\">SecuBox</b>"
|
||||||
+ cspProof
|
+ cspProof
|
||||||
+ tor
|
+ tor
|
||||||
+ "<span>" + esc((b.level || "r1").toUpperCase()) + "</span>"
|
+ lvlSwitch(b)
|
||||||
+ "<span id=\"sbx-trk\">🛰️ " + trk + " trackers</span>"
|
+ "<span id=\"sbx-trk\">🛰️ " + trk + " trackers</span>"
|
||||||
+ "<span id=\"sbx-ck\">🍪 " + ck + " cookies</span>"
|
+ "<span id=\"sbx-ck\">🍪 " + ck + " cookies</span>"
|
||||||
+ pin
|
+ pin
|
||||||
|
|
@ -192,7 +229,8 @@ _BANNER_CORE = r"""
|
||||||
+ "<button aria-label=\"dismiss\" style=\"background:none;border:0;color:#8A9AA8;cursor:pointer;font-size:14px\">✕</button>";
|
+ "<button aria-label=\"dismiss\" style=\"background:none;border:0;color:#8A9AA8;cursor:pointer;font-size:14px\">✕</button>";
|
||||||
document.body.appendChild(bar);
|
document.body.appendChild(bar);
|
||||||
try { document.body.style.paddingTop = (bar.offsetHeight || 34) + "px"; } catch (_) {}
|
try { document.body.style.paddingTop = (bar.offsetHeight || 34) + "px"; } catch (_) {}
|
||||||
var btn = bar.querySelector("button");
|
wireLevels(bar, b);
|
||||||
|
var btn = bar.querySelector("button[aria-label=\"dismiss\"]");
|
||||||
if (btn) btn.onclick = function(){ dismissed = true; try { document.body.style.paddingTop = ""; } catch (_) {} bar.remove(); };
|
if (btn) btn.onclick = function(){ dismissed = true; try { document.body.style.paddingTop = ""; } catch (_) {} bar.remove(); };
|
||||||
}
|
}
|
||||||
// ensure(): (re)render the banner if it's absent and the bundle is loaded and
|
// ensure(): (re)render the banner if it's absent and the bundle is loaded and
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,27 @@ def upsert_client(mac_hash: str, ip: str, level: str = "r1") -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_client_level(mac_hash: str, level: str) -> None:
|
||||||
|
"""#724 — set a client's level by hash only (no ip needed), for the banner
|
||||||
|
self-service switch on R3 wg peers (which have no captive MAC/ip). Updates the
|
||||||
|
existing row; inserts a minimal row if the client is unknown."""
|
||||||
|
now = int(time.time())
|
||||||
|
with _conn() as c:
|
||||||
|
try:
|
||||||
|
c.execute("ALTER TABLE clients ADD COLUMN level TEXT NOT NULL DEFAULT 'r1'")
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
pass
|
||||||
|
cur = c.execute("UPDATE clients SET level=?, last_seen=? WHERE mac_hash=?",
|
||||||
|
(level, now, mac_hash))
|
||||||
|
if cur.rowcount == 0:
|
||||||
|
c.execute(
|
||||||
|
"INSERT INTO clients(mac_hash, ip, level, first_seen, last_seen) "
|
||||||
|
"VALUES (?,?,?,?,?) ON CONFLICT(mac_hash) DO UPDATE SET "
|
||||||
|
"level=excluded.level, last_seen=excluded.last_seen",
|
||||||
|
(mac_hash, "?", level, now, now),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_client_level(mac_hash: str) -> str:
|
def get_client_level(mac_hash: str) -> str:
|
||||||
"""Returns 'r0' | 'r1' | 'r2'. Default 'r1' if not found."""
|
"""Returns 'r0' | 'r1' | 'r2'. Default 'r1' if not found."""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,11 @@ ExecStart=/usr/sbin/secubox-blacklist-sync
|
||||||
User=root
|
User=root
|
||||||
Nice=10
|
Nice=10
|
||||||
IOSchedulingClass=idle
|
IOSchedulingClass=idle
|
||||||
TimeoutStartSec=120
|
# DNS-guard resolves up to DOMAIN_CAP blocklisted domains sequentially; on a
|
||||||
|
# loaded board that can run a few minutes. 120s was shorter than a full sweep
|
||||||
|
# (~3min for ~700 live resolutions) → systemd SIGTERM'd the oneshot before it
|
||||||
|
# loaded the sets. Give it headroom (#519/#522).
|
||||||
|
TimeoutStartSec=600
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
secubox-wazuh (1.0.1-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* #53 fix: stop the 100% CPU spin on boards without Wazuh. The uvicorn worker
|
||||||
|
was started unconditionally even though SecuBox's IDS/IPS stack is
|
||||||
|
Suricata + CrowdSec and /var/ossec is absent, so it busy-looped for nothing.
|
||||||
|
Gate the unit with ConditionPathExists=/var/ossec/etc/ossec.conf — systemd
|
||||||
|
now reports "inactive (condition failed)" and never starts it unless a local
|
||||||
|
Wazuh agent/manager is installed. Add RestartSec=5 as a hot-respawn guard.
|
||||||
|
Module kept (opt-in SIEM integration), not removed.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Wed, 24 Jun 2026 10:00:00 +0000
|
||||||
|
|
||||||
secubox-wazuh (1.0.0-1) stable; urgency=low
|
secubox-wazuh (1.0.0-1) stable; urgency=low
|
||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=SecuBox Wazuh API
|
Description=SecuBox Wazuh API
|
||||||
After=network.target secubox-core.service
|
After=network.target secubox-core.service
|
||||||
|
# #53: this API only does anything when a local Wazuh agent/manager is present
|
||||||
|
# (/var/ossec, port 55000). SecuBox's documented IDS/IPS stack is Suricata +
|
||||||
|
# CrowdSec, so on every normal board Wazuh is absent and the uvicorn worker just
|
||||||
|
# burned 100% CPU for nothing. Gate the unit on Wazuh actually being installed:
|
||||||
|
# systemd reports "inactive (condition failed)" and never starts it otherwise.
|
||||||
|
# Remote-API-only integrations (no local ossec) should drop this condition.
|
||||||
|
ConditionPathExists=/var/ossec/etc/ossec.conf
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|
@ -9,6 +16,8 @@ WorkingDirectory=/usr/lib/secubox/wazuh
|
||||||
ExecStart=/usr/bin/python3 -m uvicorn api.main:app --uds /run/secubox/wazuh.sock --log-level warning
|
ExecStart=/usr/bin/python3 -m uvicorn api.main:app --uds /run/secubox/wazuh.sock --log-level warning
|
||||||
UMask=0117
|
UMask=0117
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
# Defensive: never hot-respawn (the default ~100ms churns CPU on its own).
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ fi
|
||||||
|
|
||||||
cd "\$SITE"
|
cd "\$SITE"
|
||||||
|
|
||||||
|
# #121: every git op below runs as root (ssh root@host), so a freshly created
|
||||||
|
# .git ends up root:root — but metablogizer runs as 'secubox' and must be able
|
||||||
|
# to write the repo (webhook deploys, sub-E #113). Re-own the site dir after any
|
||||||
|
# ingest that touches .git. Matches the chown pattern in module postinsts.
|
||||||
|
fix_perms() { chown -R secubox:secubox "\$SITE" 2>/dev/null || true; }
|
||||||
|
|
||||||
# Determine remote HEAD (might fail if repo doesn't exist yet — that's OK)
|
# Determine remote HEAD (might fail if repo doesn't exist yet — that's OK)
|
||||||
remote_head=\$(git ls-remote "\$REPO_URL" main 2>/dev/null | awk '{print \$1}' || true)
|
remote_head=\$(git ls-remote "\$REPO_URL" main 2>/dev/null | awk '{print \$1}' || true)
|
||||||
|
|
||||||
|
|
@ -76,6 +82,7 @@ if [[ -d .git ]]; then
|
||||||
fi
|
fi
|
||||||
git tag -f v1.0.0
|
git tag -f v1.0.0
|
||||||
git push --quiet --force origin v1.0.0
|
git push --quiet --force origin v1.0.0
|
||||||
|
fix_perms
|
||||||
echo "ingested-with-history"
|
echo "ingested-with-history"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
@ -95,6 +102,7 @@ if [[ -d .git ]]; then
|
||||||
git push --quiet origin main
|
git push --quiet origin main
|
||||||
git tag v1.0.0
|
git tag v1.0.0
|
||||||
git push --quiet origin v1.0.0
|
git push --quiet origin v1.0.0
|
||||||
|
fix_perms
|
||||||
echo "ingested-fresh"
|
echo "ingested-fresh"
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
|
|
@ -113,6 +121,7 @@ else
|
||||||
git push --quiet origin main
|
git push --quiet origin main
|
||||||
git tag v1.0.0
|
git tag v1.0.0
|
||||||
git push --quiet origin v1.0.0
|
git push --quiet origin v1.0.0
|
||||||
|
fix_perms
|
||||||
echo "ingested-fresh"
|
echo "ingested-fresh"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user