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>
The endpoint shelled to 'mailctl user passwd', which writes a host-side copy the
LXC never reads, so admin password resets silently didn't take (login kept
failing). Now generates the SHA512-CRYPT hash via the container's doveadm and
updates DATA_PATH/config/users directly (the bind-mounted file dovecot reads),
normalising any legacy 2-field line to the full 8-field record.
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
Status/components/ports now use direct TCP probes instead of lxc-info (which
read the wrong lxc_path and reported everything Stopped); storage reads vmail.
Webui rebuilt in the /certs/ hybrid-skin look: emoji stat-cards, 15s live
pulse, External-mailboxes tab, scanline engine dropped; all endpoints preserved.
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.
configure_roundcube writes config.inc.php.local with the SQLite db_dsnw,
a random des_key, tls://localhost IMAP/SMTP, and verify_peer=false for the
LXC self-signed cert; included last so it overrides dbconfig-common's MySQL
default (which 500s with no MariaDB present). Inits the SQLite schema from
/usr/share/dbconfig-common/.../sqlite3 and ships the sqlite3 CLI. Idempotent;
mirrors the verified live gk2 hotfix. Bump 2.3.1 -> 2.3.2.
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>
* test(mail): Phase 2 scaffolding — rspamd.sh + templates + routers + bats baseline (ref #153)
* feat(mail): rspamd.sh — install_rspamd + re-entry guard (ref #153)
* feat(mail): Rspamd config templates (9 .conf files) — DKIM/ARC/DMARC/greylist/ratelimit (ref #153)
* feat(mail): lib/mail/rspamd.sh — configure helpers + DKIM keygen + D9 purge gate (ref #153)
* feat(mail): Milestone C — rspamd_client + /rspamd/* router + legacy shims (ref #153)
- rspamd_client.py: httpx wrapper around the Rspamd HTTP controller
(read /stat /history /graph; write /reload /learnspam /learnham).
- routers/rspamd.py: 11 new endpoints under /api/v1/mail/rspamd/*
(status, history, scores, reload, learn-{spam,ham}, whitelist CRUD,
dkim/{domain}/{status,keygen}).
- routers/legacy.py: 14 deprecation shims for the Phase 1 /dkim/*
/spam/* /grey/* surface. Each emits X-Deprecated-Endpoint: rspamd
and forwards to the Rspamd equivalent. Removed in v3.0.
- main.py: drops the 14 inline legacy handlers and mounts the two
routers via app.include_router(). 62/62 Phase 1 endpoint pytest
still passes.
* feat(mail): Milestone D — mailctl cmd_rspamd dispatcher (ref #153)
Adds rspamd subcommand to mailctl: install / start / stop / restart /
reload / status / dkim-keygen / dns-records / learn-spam / learn-ham /
purge-legacy. The install path calls lib/mail/rspamd.sh helpers in
order: install_rspamd → configure_rspamd_milter → configure_rspamd_controller
→ configure_rspamd_dkim → configure_rspamd_postfix_milter.
The purge-legacy verb requires Rspamd to respond healthy on :11334
before removing SA + OpenDKIM (D9 safety gate).
* feat(mail): Milestone E — install adds rspamd; mail.toml [mail.rspamd]; postinst secrets (ref #153)
- install_mail_packages: now installs rspamd + redis-server + enables
postfix/dovecot/rspamd at LXC boot (Phase 1 follow-up). Drops Apache/
Roundcube — they live in the roundcube LXC after the rev. 3 split.
- mail.toml: [mail.rspamd] section (greylist, bayes_autolearn,
ratelimit_outbound=200/h/user, web_ui_host). horde_url added.
- debian/postinst: on upgrade <2.3, generate /etc/secubox/secrets/
rspamd-controller.pw (32-byte random) and mkdir /data/volumes/mail/
rspamd/{dkim,bayes,history,settings} chown 100110 (_rspamd in unpriv).
* feat(mail): Milestone F — rspamd-route-sync-patch deploy helper (ref #153)
Idempotent one-shot script run at deploy time:
1. Removes 10.100.0.{10,11,12} from sync-mitmproxy-routes.sh's
DEAD_CONTAINER_IPS list so the periodic timer does not reroute
the mail/horde/roundcube LXC routes to webui.
2. Adds rspamd.gk2.secubox.in → [10.100.0.10, 11334] to both the
host's /srv/mitmproxy/haproxy-routes.json and the mitmproxy LXC
copy, then restarts the mitmdump worker.
No source-side nginx vhost added: rspamd UI goes through the
existing HAProxy → mitmproxy_inspector → 10.100.0.10:11334 path.
* feat(mail): bump secubox-mail to 2.3.0 (Phase 2 Rspamd) (ref #153)
* test(mail): Phase 2 13-gate acceptance smoke (ref #153)
Run with: bash tests/scripts/test-mail-phase2-acceptance.sh root@admin.gk2.secubox.in
Gates 1-3 are source-side (parses, pytest, deb path coverage).
Gates 4-11 are board-side (Rspamd listening, Postfix milter wired,
DKIM key generated, modules loaded, web UI via WAF).
Gates 12-13 are post-cutover (legacy purged + Phase 1 regression
check — 5 secubox.in users intact, webmail still WAF-routed).
Every board call uses timeout — Phase 1 lesson, never raw pipes.
* fix(mail): debian/rules ships templates/rspamd/ (path-coverage fix, ref #153)
Phase 1 lesson redux: debian/rules silently misses files unless every
new source-tree subdir gets its explicit cp line. Adds the templates/
rspamd/ install line so the 9 local.d configs + the postfix milter
snippet land at /usr/lib/secubox/mail/templates/rspamd/.
* fix(mail): live-deploy fixes — _rspamd uid via lxc-attach + smoke gate 7/12 (ref #153)
Two issues surfaced during Phase 2 live deploy on admin.gk2.secubox.in:
1. configure_rspamd_controller hardcoded chown to host uid 100110, assuming
_rspamd is uid 110 inside the LXC. On this Debian 12 image _rspamd is
uid 107 — rspamd crash-looped with "Permission denied" on secrets.inc.
Fix: chown via `lxc-attach -- chown _rspamd:_rspamd` so the kernel's
idmap maps the right uid regardless of image package set. Mode 0640
instead of 0600 so the worker (uid _rspamd) can read it even if launched
from a slightly different group context.
2. Smoke gate 7 grepped for the literal string "rspamd" in the first 5
lines of `rspamc stat`, but rspamc only prints "Results for command:
stat" + counters. Fix: grep for an always-present stat marker
("Messages scanned" or "Pools allocated") with a wider head -10 window.
3. Smoke gate 12 ran `dpkg -l opendkim spamassassin` under `set -e` and
silently aborted the smoke when the named packages were unknown (the
success case after purge). Fix: append `|| true` so the grep below is
the actual gate.
All 13 gates green on admin.gk2.secubox.in after these fixes + the manual
deploy steps (DKIM keygen via lxc-attach, bind-mount entries, HAProxy +
mitmproxy route map for rspamd.gk2.secubox.in).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mail): rspamd lib + sbin write through live LXC (ref #153)
Phase 2 deploy on admin.gk2.secubox.in surfaced three brittleness points
that the live-deploy fixes patched ad-hoc:
1. configure_rspamd_milter wrote to ${LXC_BASE}/<container>/rootfs/etc/
rspamd/local.d/ — but on this board the runtime LXC mounts a different
rootfs (/data/lxc/<container>/rootfs/ per lxc.rootfs.path). Files written
to the host-side guess landed in a stale shell the running LXC didn't
see, and the first install's local.d was empty.
2. configure_rspamd_controller had the same path-guess issue plus
hardcoded chown 100110:100110 (had to be patched in commit 637b2221
because _rspamd is uid 107 on this image, not 110).
3. rspamd_keygen called rspamadm on the host PATH — rspamadm only exists
inside the rspamd-installed LXC, so the function errored out on every
install. The Phase 2 deploy worked around it manually.
This refactor makes all three write THROUGH the live LXC via lxc-attach:
- Each local.d template is streamed in with `lxc-attach -- tee` (kernel
resolves the rootfs path; idmap applies to the resulting file uid).
- secrets.inc + worker-controller.inc likewise written via tee, then
chown'd inside the LXC to _rspamd:_rspamd (kernel maps to the correct
outside-LXC subuid regardless of image).
- rspamd_keygen takes a `container` arg, runs `rspamadm dkim_keygen`
inside the LXC, writes the keypair to /etc/rspamd-keys/<domain>/ (the
bind-mounted path; falls back to /var/lib/rspamd/dkim/ if the bind
mount isn't active yet), and mirrors the DNS TXT to the host data dir
for DNS-publishing tooling.
- mailctl's `dkim-keygen` subcommand now delegates to the lib function.
- rspamd-route-sync-patch.sh verifies each write (reads back + asserts
the entry equals expected) and fails loudly on mismatch — the Phase 2
deploy needed a manual second pass for the mitmproxy LXC copy.
Pre-conditions: configure_rspamd_milter and configure_rspamd_controller
now require the LXC to be RUNNING (added an explicit lxc-info guard).
This matches the cmd_rspamd install path which starts the LXC via
install_rspamd before configuring.
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>
The squash-merge of PR #141 dropped the last 4 commits from feature/136,
leaving master with a broken secubox-mail 2.2.0:
* lib/install.sh / lib/lxc.sh / lib/migrate.sh shipped to wrong path
(debian/rules expects lib/mail/, package only ships users.sh).
* mailctl cmd_install/cmd_start/cmd_stop/cmd_sync/cmd_dkim still call
/usr/sbin/mailserverctl + /usr/sbin/roundcubectl — those are now
deprecation shims that exec mailctl, producing the same fork-bomb
recursion that took down 192.168.1.200 during initial deploy.
* mail-migrate-to-single-lxc.sh in-tree fallback expected old lib path.
* helpers.bash bats fixtures source wrong lib paths.
* Phase 1 post-mortem doc + smoke-gate-11 + cmd_install fix were absent.
This replays the affected files from feature/136 tip
(85394e10) onto master. Net effect identical to a clean merge.
Required before Phase 2 (which sources lib/mail/install.sh).
- Changed body class from "crt-body crt-scanlines" to "crt-light" on all pages
- Fixed portal menu to point to /portal/ instead of /c3box/
- Removed c3box/portal duplicate in menu
- All pages now start with light theme, sidebar.js handles theme switching
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Generate README.md for all 45 secubox-* packages with screenshots
- Add multilingual wiki pages (EN, FR, DE, ZH) documenting 47 modules
- Add generate-docs.py tool for documentation generation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add -P /srv/lxc flag to lxc-info and lxc-attach commands
- Run service as root for LXC access permissions
- Remove systemd sandboxing that blocked LXC operations
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add security features section with toggle switches (DKIM, SpamAssassin, Greylisting, ClamAV)
- Add security score indicator (0-4)
- Add mail logs viewer tab
- Add mailbox repair action
- Add DKIM record display in DNS setup
- Toast notifications for all actions
- Improved component status display
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New modules (35 total):
- secubox-repo v1.0.0: APT repository management
- repoctl CLI for package management
- GPG key generation and signing
- Multi-distribution support (bookworm, trixie)
- Web dashboard for repository status
- secubox-hardening v1.0.0: Kernel and system hardening
- hardeningctl CLI for security management
- Sysctl hardening (ASLR, kptr_restrict, SYN cookies)
- Module blacklist (uncommon protocols, filesystems)
- Security benchmark with 100% score on VM
CI/CD workflows:
- build-packages.yml: Dynamic matrix for all packages
- build-image.yml: 5 board images with compression
- publish-packages.yml: APT repo publishing
- release.yml: Unified release orchestration
APT repository scripts:
- export-secrets.sh: Export GPG/SSH keys for GitHub Actions
- local-publish.sh: Local test server
- install.sh: User installation script
Security (Phase 5):
- AppArmor profiles for all services
- Audit rules for SecuBox services
- build-all.sh for local builds
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- mailserverctl v2.3.0: dkim setup/keygen/status/sync/install/configure
- Install OpenDKIM in LXC container with dpkg --force-confold
- Configure Postfix milter integration (inet:localhost:8891)
- Generate 2048-bit RSA keys with DNS records in standard and BIND format
- Add API endpoints: /dkim/status, /dkim/setup, /dkim/keygen, /dkim/sync
- Update startup script to auto-start OpenDKIM
- Tested and verified: OpenDKIM running, Postfix milter configured
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add real backend control scripts and migration support:
- secubox-streamlit: streamlitctl for LXC container management,
app deployment, Gitea integration, OpenWrt migration
- secubox-haproxy: haproxyctl with LXC/Docker/native modes,
vhost/backend/cert management, WAF integration, migration
- secubox-metablogizer: updated UI with three-fold tabs
All modules now follow Components/Status/Access architecture
with tabbed UI and migration modals for importing from OpenWrt.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All 19 module index.html files now link to /shared/sidebar.css
for consistent navbar styling across the application.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>