Commit Graph

3020 Commits

Author SHA1 Message Date
50712b9e29 Merge remote-tracking branch 'origin/master' into feat/rlevel-per-peer
# Conflicts:
#	.superpowers/sdd/task-7-report.md
#	.superpowers/sdd/task-8-report.md
2026-07-25 07:41:57 +02:00
CyberMind
2ebf8cac46
Merge pull request #900 from CyberMind-FR/feat/proxypac-wpad-autodetect
feat(proxypac): transparent .onion (wg-toolbox+LAN) + WPAD/DHCP autodetect
2026-07-25 07:40:28 +02:00
7b45d320ba release(toolbox): 2.8.8 — R-level webui sans sudo (portail garde NNP=true)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-25 07:29:42 +02:00
015a92e463 fix(toolbox): R-level webui sans sudo — le portail lance le ctl directement (CAP_NET_ADMIN + fichier portal-owned suffisent); portail garde NNP=true (revert dropin + sudoers)
Meilleur que NNP=false: zéro réduction de durcissement sur le portail public.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-25 07:25:58 +02:00
0e4a4b253b fix(toolbox): portail NNP=false (dropin) — le contrôle R-level webui délègue via sudo→sbxmitm-policyctl (sudoers scopé 5 verbes)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-25 07:20:32 +02:00
d6152d7709 fix(toolbox): /rlevel admin — bloque l'écriture depuis le vhost public kbin (_is_public_kbin, comme les 8 routes sœurs)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 16:38:31 +02:00
8dc288db78 build(toolbox): package rlevel (policyctl, sudoers, panel, seed default_mode) + changelog
secubox-toolbox: install sbxmitm-policyctl (0755), the /rlevel panel +
menu.d entry, and a scoped sudoers.d/secubox-toolbox granting the portal
(User=secubox-toolbox) exactly the 5 policyctl subcommands
(set-floor|force|set-chosen|set-default|list) — no blanket ctl grant.
postinst seeds /var/lib/secubox/toolbox/peer-rlevel.json with
defaults.mode="reel" (never overwritten on upgrade, leaf-only chown) to
preserve current fleet-wide MITM behaviour instead of a surprise
fleet-wide downgrade; operators opt individual peers down afterwards.
control: + sudo (transport for the ctl delegation).

secubox-toolbox-ng: changelog bump only — rlevel.go/rlevel_test.go build
into the existing sbxmitm binary (same Go package), no rules/control
change needed. Cross-build verified: GOARCH=arm64 GOOS=linux go build
-mod=vendor produces a valid ARM aarch64 ELF; go test ./... passes in
full.

nftables.d @rlevel_off set + off-bypass rule were already shipped by the
existing execute_after_dh_auto_install block — verified, not touched.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 16:25:08 +02:00
668d920076 test(toolbox): garde XSS panneau rlevel renforcé (whitelist statique, attrape la concaténation, pas seulement ${})
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 16:17:24 +02:00
65116b44b5 feat(toolbox): panneau /rlevel (statut par peer, badges, self-service, délégation événements)
Panneau admin.gk2/rlevel/ consommant le contrat API /rlevel de la tâche 6
(GET /rlevel/peers, POST /rlevel/peer {pubkey,floor?,forced?}, GET/POST
/rlevel/me {chosen}) : table peers avec badges off/passive/active/reel,
contrôles floor + force/unlock par ligne, carte self-service pour son
propre niveau borné par floor/forced. Navbar partagée (sidebar.js +
hybrid-dark), token sbx_token en Bearer + same-origin sur chaque appel.

Sécurité : aucune donnée API (pubkey, label) n'entre dans un handler
inline — délégation d'événements via data-action/data-pubkey + esc()
sur tout texte injecté (leçon proxypac).

menu.d/27-rlevel.json + test_rlevel_panel.py (13 cas, garde XSS incluse).

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 16:11:26 +02:00
5a82eba3bb fix(toolbox): /rlevel — bloque l'escalade peer→admin (403 si source tunnel) + pubkey dans le body (base64 /)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 16:01:27 +02:00
add0fda3f6 feat(toolbox): API /rlevel admin + peer self-service (tunnel-auth, ctl delegation)
Adds GET/POST /rlevel/peers, /rlevel/peer/{pubkey}, /rlevel/me to the toolbox
FastAPI router. Admin routes list every wg-toolbox peer's rlevel status
(chosen/forced/floor/effective + best-effort live handshake) and set
floor/forced. Peer self-service resolves identity from the wg tunnel source
IP (_client_ip -> wg-peers.json), 403s on an unknown IP, and 409s if the
requested chosen mode would drop below the peer's floor or lift an admin
forced mode. Every write delegates to `sudo -n sbxmitm-policyctl`
(set-floor/force/set-chosen) -- no in-process root, mirrors the proxypac
API's _ctl pattern.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 15:50:49 +02:00
e8a6058488 fix(toolbox): off-bypass via named set @rlevel_off dans la chaîne fanout (return same-chain — table séparée ne bloquait pas le DNAT)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 15:42:30 +02:00
78ccae7b58 feat(toolbox): sbxmitm-policyctl (atomic peer-rlevel.json + nft off-bypass regen + audit)
Root-scoped ctl for the per-peer wg-toolbox MITM R-level policy (task 4 of
the rlevel-per-peer plan): set-floor/force/set-chosen/set-default/list.
Shadow-write + atomic rename of peer-rlevel.json (the rename itself is the
hot-reload signal the Go workers poll for), server-side clamp of set-chosen
to the peer's floor, regeneration of the nft off-bypass drop-in from peers
whose effective mode is off (one priority below the toolbox DNAT fanout),
and an append-only JSON audit line per mutation. Invalid mode or unknown
pubkey aborts before any write.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 15:23:03 +02:00
a441fdb50c feat(toolbox-ng): per-peer clamp wired into Decide (decideForPeer, both accept paths)
Both sbxmitm accept paths (CONNECT/handleConnect and transparent/
handleTransparent) now go through a single decideForPeer(clientIP, host, sni)
helper: it calls the existing px.pol.Decide, then clamps the verdict to the
calling peer's R-level via px.rlevel.ModeForIP + clampVerdict when a
PeerPolicy is wired in. Factoring both call-sites onto the same helper means
they can never drift on how the clamp is applied.

Proxy gains an rlevel *PeerPolicy field, loaded in main() from
SECUBOX_PEER_RLEVEL / SECUBOX_WG_PEERS (env-overridable, falling back to
peer-rlevel.json and machash.go's wgPeersPath). A nil rlevel is a total
no-op — every Proxy built without it (existing suite, CONNECT PoC) keeps
today's exact px.pol.Decide behavior.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 15:13:09 +02:00
fd4bcb5c45 feat(toolbox-ng): PeerPolicy loader (peer-rlevel.json + ip→pubkey join, hot-reload, fail-safe passive)
Joins peer-rlevel.json (pubkey -> {chosen,forced,floor}) with wg-peers.json
(pubkey -> ip) into an ip -> RLevel map. ModeForIP resolves an unknown IP to
the file's declared defaults, and any unreadable/corrupt input degrades to
Passive for every peer (fail-closed). Hot-reloads both files on mtime change,
mirroring the reload.Watcher pattern already used by policy.go.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 15:05:47 +02:00
f43c408b5e feat(toolbox-ng): rlevel effective() + clampVerdict() (off/passive/active/reel)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 15:00:25 +02:00
f38d388d1c docs(toolbox): plan d'implémentation R-level par peer (8 tâches, grounded sbxmitm)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 14:58:56 +02:00
328e102307 docs(toolbox): design R-level MITM par peer wg-toolbox (4 modes, self-service borné + override admin)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 14:52:25 +02:00
ada2d3972e fix(proxypac,tor): revue finale — role toml honoré + TransPort/DNSPort coord + NNP fallback + esc + port socks
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 11:19:11 +02:00
d98172d5b8 build(proxypac,tor): packaging — dropins, sudoers scopé, postinst wiring (socks-lan+transparent+wpad), changelogs
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 11:02:40 +02:00
418d7673b5 fix(proxypac): panneau — délégation d'événements (supprime XSS stockée via onclick host interpolé)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:55:51 +02:00
240033e1b0 feat(proxypac): panneau réécrit — sidebar navbar + statut rôle/SOCKS/transparent + candidats + runbook client
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:49:58 +02:00
c378fdb499 feat(proxypac): API /status + /wpad/apply + /wpad/state + /transparent (délègue via sudo scopé)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:44:16 +02:00
82f4a9aea4 test(proxypac): garde bascule de rôle WPAD (nettoyage cross-tier, pas d'accumulation)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:40:55 +02:00
1a86739975 feat(proxypac): actuateur WPAD best-effort (dnsmasq 252 / DNS wpad / no-op) idempotent
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:37:01 +02:00
34b8027f43 fix(proxypac): role probe ancre :53/:67 sur l'espace de fin (évite faux positif :5300/:6700)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:35:51 +02:00
c2f89ae70a test(tor): verrouille local-zone transparent + domain-insecure onion (débloque .onion, validé live)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:27:52 +02:00
470004fa67 fix(tor): transparent — route_localnet par iface entrante (conf.all ne suffit pas) + unbound local-zone/domain-insecure onion (débloque .onion Firefox-agnostique)
Trouvé en validation live: route_localnet=0 sur wg-toolbox/eth2 jetait le DNAT
vers loopback; Unbound bloquait .onion (RFC6761) -> NXDOMAIN autoritaire.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:27:23 +02:00
f542968925 feat(proxypac): détection passive de rôle master/slave + résolveur DNS (tier 1/2/3)
Implémente role.detect() avec injection de probe pour tests isolés.
Master détecté si DHCP écoute UDP/67 sur l'IP LAN (tier 1).
Slave avec résolveur DNS si UDP/53 lié (tier 2), sinon tier 3.
Aucune émission de paquet DHCP (read-only passive detection).

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:11:27 +02:00
2d00ac44ce docs(proxypac): README — onion rule placeholder + proxypac.toml override (socks_endpoint/role/transparent)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:10:14 +02:00
11fb00df1d feat(proxypac): onion rule -> LAN SOCKS via proxypac.toml (placeholder subst, plus de mesh en dur)
- config.py: load() détecte IP LAN via tor-lan-ip, override proxypac.toml socks_endpoint
- 00-onion.rules: remplace hardcoded 10.10.0.1:9050 par __LAN_SOCKS__ placeholder
- generator.py: substitue __LAN_SOCKS__ dans chaque rule.directive après parse_rules_dir
- proxypac.toml: template conf (role, wpad_domain, pac_url, transparent, optionnel socks_endpoint override)
- test_config.py: tests TDD (defaults, override prefs, placeholder assertion)

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:05:35 +02:00
67bbb49970 fix(tor): torctl SHARE surchargeable + tests transparent réellement discriminants (invariant double TransPort)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 10:02:59 +02:00
b61b5aa13a feat(tor): transparent .onion — unbound onion-forward + TransPort dropin + nft redirect + torctl on/off (port-coord, route_localnet)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 09:53:19 +02:00
8ec1bda871 test(tor): cible directive SocksPolicy en début de ligne + restaure commentaire explicatif
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 09:49:17 +02:00
febd54a878 feat(tor): SocksPort LAN dropin (no SocksPolicy) + LAN-IP detect helper + torctl
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 09:45:33 +02:00
2eb3890d53 docs(proxypac): plan d'implémentation WPAD/DHCP autodetect + transparent .onion (8 tâches)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 09:40:56 +02:00
8204490dea docs(proxypac): design WPAD/DHCP autodetect + transparent .onion (wg-toolbox+LAN)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 09:33:30 +02:00
750a7b7c69 docs(plan): PAC .onion -> Tor (secubox-tor) — 4 tâches TDD + recette manuelle
Some checks are pending
License Headers / check (push) Waiting to run
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 08:42:08 +02:00
4e1d3b5d6d docs(spec): PAC auto-routing .onion → Tor (secubox-tor)
PAC qui dévie les .onion vers le SOCKS Tor local (SOCKS5 = remote DNS, Tor
résout le .onion), tout le reste en DIRECT (le transparent wg-toolbox inspecte
déjà). SOCKS confiné sur 192.168.1.200:9050 (SocksPolicy accept LAN+wg reject *,
jamais open-relay). Servi par nginx en application/x-ns-proxy-autoconfig,
LAN-only. Prérequis signalé : tor est inactive/failed sur le board, le plan doit
le réparer avant de livrer une pièce morte.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 08:38:12 +02:00
github-actions[bot]
eaa3fdb1fa ci: Update metrics [skip ci] 2026-07-24 06:07:58 +00:00
35967f698e docs(todo): frigate (bloqué podman-in-LXC) + hexo (non-LXC) — intégration webui différée
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 08:04:04 +02:00
e79343c67c feat(picobrew): page admin = cockpit supervision + accès (au lieu de l'iframe pleine page)
L'UI réelle de picobrew_pico vit sur son sous-domaine LAN-confiné
(picobrew.gk2, avec zoom), donc une iframe pleine page dans la page admin était
redondante. La page admin devient un cockpit de supervision :
- contrôle : démarrer / arrêter / redémarrer / rafraîchir ;
- cartes statut : état, version figée, session, mémoire du conteneur ;
- bloc « accès distant » : lien Ouvrir vers l'interface complète, adresse LAN
  directe, note mode zoom, redirection DNS de l'appareil ;
- journal picobrew_pico (tail du conteneur).

ctl : status --json expose mem_mb (best-effort cgroup, toujours un entier pour
ne pas casser le JSON) ; nouveau verbe restart (stop+start ; la garde de session
ne concerne que update). API : routes /restart et /logs (dégradation propre
conteneur à l'arrêt). Sudoers inchangé (tout picobrewctl déjà accordé).

Vérifié live : status mem_mb=88, /logs rend le vrai journal, panel sans iframe.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 07:46:58 +02:00
947df7e058 feat(sbxmitm): proxifier l'upgrade WebSocket dans les flux MITM'd (fix wss via wg-toolbox)
Le pipeline MITM sert un flux déchiffré avec un handler manuel (up.Do → resp →
write) : un http.Client ne peut PAS porter un « 101 Switching Protocols », donc
tout wss:// sur un hôte MITM'd traversant wg-toolbox échouait (Socket.IO de
picobrew_pico, zigbee2mqtt, toute app temps-réel). Seuls les hôtes splicés
passaient (leur io.Copy brut portait nativement le WebSocket).

Fix (comme httputil.ReverseProxy) : détecter l'upgrade (Connection: upgrade +
Upgrade: websocket), dialer upstream en forçant http/1.1 (un 101 ne peut pas
rider h2), transmettre le handshake verbatim, relayer le 101, puis piper les
octets bruts bidirectionnellement jusqu'à fermeture. Fait AVANT anonymize/DPI/
inject (un upgrade est un canal de contrôle, pas une requête inspectable ; les
en-têtes Sec-WebSocket-* doivent atteindre upstream intacts). La copie
client→upstream lit le bufio.Reader du pipeline (frames déjà bufferisées),
sinon elles seraient perdues.

dialUChrome gagne un paramètre ALPN (dialUChromeALPN) — un seul call-site — pour
que la voie WS force http/1.1 sans toucher la voie normale (h2 préféré).

Tests : détection (liste de Connection, casse, faux positifs) + relais 101 +
pipe bidirectionnel echo sur un upstream TLS réel. build + vet + suite complète
-race verts, zéro régression.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 07:46:58 +02:00
github-actions[bot]
d8c1697233 ci: Update metrics [skip ci] 2026-07-24 05:43:17 +00:00
e287389138 feat(picobrew): panel embarque l'UI complète picobrew_pico (iframe) + bandeau de contrôle
Some checks are pending
License Headers / check (push) Waiting to run
Webui admin la plus complète possible sans dev : bandeau de contrôle SecuBox
(état + start/stop/rafraîchir + plein écran) au-dessus d'un iframe de l'UI
complète de picobrew_pico, servie via son sous-domaine LAN-confiné
picobrew.gk2.secubox.in (même domaine parent → cert wildcard valide, pas de
mixed-content). L'iframe n'est chargée que conteneur en marche.

Réf. montage vhost (live gk2, patron lyrion) : sous-domaine LAN-only
(snippet exposure allow-privé/deny-all) → nginx:9080 → LXC:80, TLS terminée
par HAProxy, route dans /etc/secubox/waf/haproxy-routes.json, upgrade WebSocket
(Upgrade/Connection comme spiderfoot). CAVEAT: un wss:// via wg-toolbox échoue
tant que sbxmitm ne pipe pas l'upgrade WebSocket dans un flux MITM'd (général,
pas spécifique picobrew).

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 07:02:36 +02:00
1208374a9a feat(picobrew): installer active l'appareil (provision oneshot --no-block) + guideline
« apt install secubox-picobrew » provisionne désormais le conteneur tout seul :
nouveau service oneshot secubox-picobrew-provision.service qui lance
`picobrewctl install` (debootstrap + clone picobrew_pico + venv + nginx/TLS),
démarré --no-block par le postinst (dpkg rend la main en ~40s, pas 5-8 min) et
gardé par ConditionPathExists=!pinned-sha (provisionne une fois, no-op ensuite).

Prouvé live sur gk2 : reset LXC → dpkg -i 2.0.1 → auto-provision en arrière-plan
→ LXC RUNNING 10.100.0.150, panel 200, /api/v1/picobrew/status JSON, appareil
:80 et TLS :443 à 200. Le webui est bien le frontend de picobrewctl via l'API.

Documente le motif comme guideline recommandée (.claude/MODULE-COMPLIANCE.md,
section « Install activates — LXC / external-service modules ») : install =
activation complète, provisionnement lourd en oneshot --no-block gardé par
marqueur, contrat ctl install (idempotent, pin-not-float, marqueur au succès
seul), et le piège de routage gk2 (webui.conf exact-match ne charge pas
secubox.d → module à socket dédié = location explicite modèle waf). Réf :
secubox-picobrew.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-24 06:45:51 +02:00
github-actions[bot]
f3697a9508 ci: Update metrics [skip ci] 2026-07-23 21:42:33 +00:00
9b45487da7 feat(picobrew): panel hybrid-dark avec navbar + toasts + lien appliance
Réécrit le panneau selon le look management par défaut du dépôt
(hybrid-dark, Courier Prime, sidebar via /shared/sidebar.js), au lieu du
squelette minimal C3BOX. Ajoute des toasts d'action (échec start/stop plus
silencieux), vérifie response.ok, et un lien vers l'interface réelle de
l'appareil (https://<ip>/) affiché quand le conteneur tourne.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-23 21:41:31 +02:00
1dfcb66fa5 fix(picobrew): postinst — unbound-control reload charge le drop-in, pas SIGHUP
Découvert à l'installation live sur gk2 : `systemctl reload unbound` (SIGHUP)
ne recharge PAS les local-zone d'unbound — le drop-in picobrew.com n'était donc
jamais appliqué avant un restart. Préférer `unbound-control reload` quand il est
configuré, sinon try-restart, sinon reload en dernier recours (best-effort).

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-23 21:20:45 +02:00
CyberMind
7dd2f6217b
Merge pull request #899 from CyberMind-FR/feat/picobrew-phase1-lxc
feat(picobrew): phase 1 — LXC qui redonne vie à l'appareil PicoBrew
2026-07-23 21:13:52 +02:00