mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-06-30 05:27:07 +00:00
Compare commits
No commits in common. "85394e10d50f13d8b4c0b9f15b49c165fedb9167" and "bade94f109cf2b97e92e7cdee6e951d72b847a7e" have entirely different histories.
85394e10d5
...
bade94f109
|
|
@ -2,50 +2,6 @@
|
||||||
*Tracking completed milestones with dates*
|
*Tracking completed milestones with dates*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2026-05-15
|
|
||||||
|
|
||||||
### Mail Phase 1 — source catch-up + 3 transitional packages (Issue #136, PR #141)
|
|
||||||
|
|
||||||
**Context:** Live board (`192.168.1.200`) already runs a single-mail-LXC layout under `/data/lxc/mail` with `/data/volumes/mail` bind mounts and unprivileged-veth `10.100.0.10/24` networking on `br-lxc`. Repo source was out of date (referenced `/srv/lxc`, `/srv/mail`, `192.168.255.30`, `mail_container`/`webmail_container`). Phase 1 caught the source up + deprecated the legacy companion packages.
|
|
||||||
|
|
||||||
**Done:**
|
|
||||||
|
|
||||||
- Phase 0 spec rev. 2 + Phase 1 plan rev. 2 committed on master (1019 net-lines specs+plans, replacing rev. 1 which assumed greenfield two-LXC consolidation).
|
|
||||||
- Branch `feature/136-mail-stack-phase-1-source-catch-up-legac` — 17 commits + post-mortem.
|
|
||||||
- `lib/lxc.sh` (unprivileged veth br-lxc helpers), `lib/migrate.sh` (legacy detectors + spec I13 data guard), `lib/install.sh` (Postfix/Dovecot/Roundcube install + configure) extracted from the 2030-line `mailserverctl`.
|
|
||||||
- `mailctl` bumped 1.3.0 → 2.2.0, canonical paths (`/var/lib/lxc/mail`, `/data/volumes/mail`, `10.100.0.10`), new `migrate-config` subcommand that rewrites legacy toml in place.
|
|
||||||
- `mailserverctl` + `roundcubectl` reduced to 10-line deprecation shims that `exec mailctl`.
|
|
||||||
- `secubox-mail` 2.2.0 with `Breaks:` / `Replaces:` against `secubox-mail-lxc (<<2.2)`, `secubox-webmail-lxc (<<2.2)`, `secubox-webmail (<<2.2)`.
|
|
||||||
- 3 legacy packages collapsed to transitional 2.2.0 metadata-only stubs depending on `secubox-mail (>= 2.2)`.
|
|
||||||
- 62/62 endpoint pytest green; 12-gate acceptance smoke script written.
|
|
||||||
- HAProxy mail-TCP snippet for SMTP/submission/IMAPS/sieve targeting `10.100.0.10`.
|
|
||||||
- WAF integration NEWS: HTTPS (admin + webmail) route via existing HAProxy → mitmproxy ACL; mail protocols TCP-pass-through (not a WAF bypass — mitmproxy inspects HTTP only).
|
|
||||||
- Backups taken on board at `/srv/backups/mail-phase1/` (data + LXC config + toml + pkglist).
|
|
||||||
- Rollback recipe: `docs/superpowers/runs/2026-05-15-mail-phase1-rollback.md`.
|
|
||||||
|
|
||||||
**Deploy outcome — partial:**
|
|
||||||
|
|
||||||
First install on the board uncovered two bugs in the deployed artifact:
|
|
||||||
|
|
||||||
1. `debian/rules` ships `lib/mail/*` to `/usr/lib/secubox/mail/lib/` — new helpers were at `lib/` (no `mail/` subdir) and silently skipped.
|
|
||||||
2. `mailctl`'s `cmd_install`/`cmd_start`/`cmd_stop`/`cmd_sync`/`cmd_dkim` still called `/usr/sbin/mailserverctl` and `/usr/sbin/roundcubectl`, which are now shims that `exec mailctl` → infinite fork recursion.
|
|
||||||
|
|
||||||
Both bugs fixed in commit `529f5ec7`. The smoke test gate 8 (`mailctl start | tail -5`) triggered the recursion before the fix was rebuilt — the resulting fork-storm (peak >2000 concurrent `mailctl sync` PIDs) made the board's sshd unreachable. Required a hard reboot.
|
|
||||||
|
|
||||||
**Status of deploy:** ✅ **Fully deployed and verified.**
|
|
||||||
|
|
||||||
- Resumed via `admin.gk2.secubox.in` after board recovery.
|
|
||||||
- 3 additional bugs surfaced and fixed during deploy (`users.sh` var override, mitmproxy route file split host/LXC, missing Roundcube vhost) — full post-mortem in `docs/superpowers/runs/2026-05-15-mail-phase1-deploy-postmortem.md`.
|
|
||||||
- Final smoke (commit `bd0053e4`): **12/12 acceptance gates green** against live board.
|
|
||||||
- 5 production `secubox.in` mailboxes preserved byte-identical (gate 12).
|
|
||||||
|
|
||||||
**Followups (post-merge):**
|
|
||||||
|
|
||||||
- Phase 2 brainstorm: Rspamd migration (replaces SA + OpenDKIM + opendmarc; adds ClamAV).
|
|
||||||
- Consider sentinel-env-var guard rails in the deprecation shims (prevent any future recursion).
|
|
||||||
- Add a dpkg-deb path-coverage bats test so source-tree refactors that move files don't silently miss `debian/rules`.
|
|
||||||
|
|
||||||
## 2026-05-14
|
## 2026-05-14
|
||||||
|
|
||||||
### remote-ui Phases 1 + 3 merged to master (Issue #127, PRs #130 + #132)
|
### remote-ui Phases 1 + 3 merged to master (Issue #127, PRs #130 + #132)
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,6 @@
|
||||||
# WIP — Work In Progress
|
# WIP — Work In Progress
|
||||||
*Mis à jour : 2026-05-15*
|
*Mis à jour : 2026-05-14*
|
||||||
|
|
||||||
|
|
||||||
## ✅ 2026-05-15: Mail Phase 1 — source catch-up + 12/12 acceptance gates GREEN (Issue #136, PR #141)
|
|
||||||
|
|
||||||
### Status
|
|
||||||
|
|
||||||
**Phase 1 fully deployed and verified.** All 12 acceptance gates green against the live board. 5 production `secubox.in` mailboxes preserved byte-identical. Several bonus bugs surfaced + fixed during deploy (recursion through shims, lib path, mitmproxy route maintenance) — full post-mortem in [docs/superpowers/runs/2026-05-15-mail-phase1-deploy-postmortem.md](../docs/superpowers/runs/2026-05-15-mail-phase1-deploy-postmortem.md).
|
|
||||||
|
|
||||||
### Done
|
|
||||||
|
|
||||||
- Phase 0 architecture spec rev. 2 + Phase 1 plan rev. 2 (commit `265bda0e` on master).
|
|
||||||
- Branch `feature/136-mail-stack-phase-1-source-catch-up-legac` (17 commits + post-mortem) — PR [#141](https://github.com/CyberMind-FR/secubox-deb/pull/141).
|
|
||||||
- `lib/lxc.sh` + `lib/migrate.sh` + `lib/install.sh` extracted (unprivileged-veth + I13 data-guard aware).
|
|
||||||
- `mailctl 2.2.0` with `migrate-config`; `mailserverctl` + `roundcubectl` → 10-line deprecation shims.
|
|
||||||
- `secubox-mail 2.2.0` + 3 transitional 2.2.0 packages with `Breaks:`/`Replaces:`.
|
|
||||||
- HAProxy mail-TCP snippet + WAF integration NEWS.
|
|
||||||
- 12-gate acceptance smoke script (`tests/scripts/test-mail-phase1-acceptance.sh`).
|
|
||||||
- Pre-flight backups on board at `/srv/backups/mail-phase1/`. Production data preserved (5 secubox.in mailboxes intact).
|
|
||||||
|
|
||||||
### Deploy gates ✅
|
|
||||||
|
|
||||||
- ✅ Board recovered (hard reboot cleared the fork-bomb).
|
|
||||||
- ✅ Fixed `.deb` deployed via `admin.gk2.secubox.in`.
|
|
||||||
- ✅ All 12 acceptance gates green (commit `bd0053e4`).
|
|
||||||
- ✅ Data byte-identical (gate 12).
|
|
||||||
- ⏳ PR #141 merge — user-validated.
|
|
||||||
|
|
||||||
### Phase 2 inputs (from deploy lessons)
|
|
||||||
|
|
||||||
- Sentinel env-var guard rails in deprecation shims to prevent any future recursion.
|
|
||||||
- bats test that runs `dpkg-deb -c` and grep-asserts `lib/*.sh` ship under the right path.
|
|
||||||
- `mailctl postfix-enable` step OR `systemctl enable postfix` in `install_mail_packages`.
|
|
||||||
- Make `sync-mitmproxy-routes.sh` aware that 10.100.0.10 is the mail LXC IP (drop from DEAD_CONTAINER_IPS).
|
|
||||||
- Document host/LXC mitmproxy route-file split in CLAUDE.md.
|
|
||||||
- Roundcube `config.inc.php` finalization (deferred to Phase 5).
|
|
||||||
|
|
||||||
### Lessons captured
|
|
||||||
|
|
||||||
- `debian/rules` is opaque to refactors — see post-mortem.
|
|
||||||
- Deprecation shims that re-enter their replacement must never be called from within the replacement.
|
|
||||||
- Acceptance tests must never pipe a command's stdout through `tail`/`head` if recursion is possible — use `timeout`.
|
|
||||||
|
|
||||||
Full post-mortem: [docs/superpowers/runs/2026-05-15-mail-phase1-deploy-postmortem.md](../docs/superpowers/runs/2026-05-15-mail-phase1-deploy-postmortem.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✅ 2026-05-14: remote-ui Phase 3 — Pillow+framebuffer kiosk for Pi 4B/400 (Issue #127, PR #132 MERGED)
|
## ✅ 2026-05-14: remote-ui Phase 3 — Pillow+framebuffer kiosk for Pi 4B/400 (Issue #127, PR #132 MERGED)
|
||||||
|
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
# Mail Phase 1 — Deploy post-mortem (2026-05-15)
|
|
||||||
|
|
||||||
## Outcome
|
|
||||||
|
|
||||||
- 16 commits landed on branch `feature/136-mail-stack-phase-1-source-catch-up-legac`, PR #141 open
|
|
||||||
- Source code aligned with board reality (paths, IP, schema)
|
|
||||||
- 62-endpoint pytest passes locally
|
|
||||||
- **Deploy not yet completed end-to-end** — first install on the board triggered a fork-recursion that fork-bombed the host before the fixed rebuild could land
|
|
||||||
- **Data preservation verified:** `/data/volumes/mail/vmail/secubox.in/` (5 production users) untouched throughout
|
|
||||||
|
|
||||||
## What went wrong
|
|
||||||
|
|
||||||
### Bug 1 — `lib/*.sh` shipped to wrong path
|
|
||||||
|
|
||||||
`packages/secubox-mail/debian/rules` copies from `lib/mail/.` into `/usr/lib/secubox/mail/lib/`. The new helpers were dropped at `packages/secubox-mail/lib/{lxc,install,migrate}.sh` (no `mail/` subdir), so `rules` didn't pick them up.
|
|
||||||
|
|
||||||
**Symptom:** `mailctl migrate-config` and `mail-migrate-to-single-lxc.sh` failed at install time with `lib/migrate.sh: No such file or directory`.
|
|
||||||
|
|
||||||
**Fix (commit `529f5ec7`):** `git mv lib/{lxc,install,migrate}.sh lib/mail/`. Updated bats helpers + in-tree fallbacks in `mailctl` and `mail-migrate-to-single-lxc.sh`.
|
|
||||||
|
|
||||||
### Bug 2 — Recursion through deprecation shims
|
|
||||||
|
|
||||||
`mailctl 1.x` shells out to `/usr/sbin/mailserverctl` and `/usr/sbin/roundcubectl` in five call sites: `cmd_install`, `cmd_start`, `cmd_stop`, `cmd_sync`, `cmd_dkim` (setup branch). In Phase 1 those two scripts became deprecation shims that `exec mailctl`. So any of the five entry points produced an infinite loop:
|
|
||||||
|
|
||||||
```
|
|
||||||
mailctl <verb>
|
|
||||||
└─ /usr/sbin/mailserverctl <verb> # shim
|
|
||||||
└─ exec /usr/sbin/mailctl <verb> # back to start
|
|
||||||
```
|
|
||||||
|
|
||||||
`exec` doesn't fork — but cmd_start's `/usr/sbin/mailserverctl start` is a subshell invocation that DOES fork. So each iteration forks a new shell, which then execs `mailctl`, which forks again. The fork-tree grows quadratically and exhausts process slots / scheduler time.
|
|
||||||
|
|
||||||
**Trigger:** smoke test gate 8 ran `ssh ... 'mailctl start 2>&1 | tail -5'`. `tail -5` holds stdout open waiting for EOF, so the recursion didn't even self-terminate on a SIGPIPE.
|
|
||||||
|
|
||||||
**Fix (commit `529f5ec7`):**
|
|
||||||
|
|
||||||
- `cmd_start` / `cmd_stop` now call `lxc-start -n "$CONTAINER" -d` / `lxc-stop -n "$CONTAINER" -t 30` directly.
|
|
||||||
- `cmd_install` sources `lib/install.sh` and runs `bootstrap_debian`, `install_mail_packages`, `install_webmail_packages`, `configure_postfix`, `configure_dovecot`, `configure_roundcube` directly.
|
|
||||||
- `cmd_sync` does the LMDB `postmap` work directly via `lxc_attach`.
|
|
||||||
- `cmd_dkim setup` is now a Phase-1 stub (full DKIM moves to Rspamd in Phase 2).
|
|
||||||
|
|
||||||
**Verification:** `grep '/usr/sbin/mailserverctl\|/usr/sbin/roundcubectl' packages/secubox-mail/sbin/mailctl` returns nothing.
|
|
||||||
|
|
||||||
### Severity
|
|
||||||
|
|
||||||
- The board's `sshd` was unable to complete the TLS banner exchange while the fork-storm raged (>2000 concurrent `mailctl sync` PIDs at peak).
|
|
||||||
- Local `pkill -9 -f mailctl` from a new SSH session never landed because the SSH session itself couldn't fight through the scheduling backlog.
|
|
||||||
- Recovery required a hard reboot of the board.
|
|
||||||
|
|
||||||
## Lessons
|
|
||||||
|
|
||||||
1. **Deprecation shims that `exec` back to the unified tool must not be called from within that unified tool.** Either fully inline the legacy behavior in the new tool first, OR ship the shim under a different name (e.g. `mailserverctl.legacy`) that the new tool never touches.
|
|
||||||
|
|
||||||
2. **`debian/rules` is opaque to source-tree refactors.** When you move files around in `packages/<pkg>/`, audit `debian/rules` for hard-coded source paths. The build will silently miss a file rather than fail loudly.
|
|
||||||
|
|
||||||
3. **Acceptance tests must not hold stdout open on commands they don't own.** `mailctl start 2>&1 | tail -5` is dangerous — `tail -5` waits for EOF, which a runaway loop never emits. Future smoke tests should use `timeout 30s mailctl start` or capture output to a file with explicit closure.
|
|
||||||
|
|
||||||
4. **Bash + LXC tooling needs guard rails.** Adding a sentinel env var (e.g. `if [ -n "$SECUBOX_MAIL_REENTRY" ]; then echo "loop detected"; exit 1; fi; export SECUBOX_MAIL_REENTRY=1`) at the top of shims would have stopped the recursion at depth 2. Consider for Phase 2.
|
|
||||||
|
|
||||||
5. **Test the deploy on a disposable LXC first.** The Phase 1 plan went directly from source-tree green tests to live deploy. A staged dry-run inside a throwaway LXC would have caught both bugs without touching the production board.
|
|
||||||
|
|
||||||
## Current state of the artifacts
|
|
||||||
|
|
||||||
| Artifact | State |
|
|
||||||
|---|---|
|
|
||||||
| `feature/136-mail-stack-phase-1-source-catch-up-legac` branch | 16 commits + this post-mortem; pushed |
|
|
||||||
| PR #141 | open; includes resume instructions |
|
|
||||||
| `output/debs/secubox-mail_2.2.0-1~bookworm1_all.deb` | local; **fixed build** (commit 529f5ec7); not yet deployed |
|
|
||||||
| 3 transitional packages | local builds present; not yet deployed |
|
|
||||||
| `secubox-mail` on board `192.168.1.200` | still `2.2.0-1~bookworm1` from the **bugged** first install; SSH currently unreachable due to fork-storm |
|
|
||||||
| Backups at `/srv/backups/mail-phase1/` (on board) | `data-volumes-mail-*.tar.gz`, `lxc-mail-config-*.tar.gz`, `mail-toml-*.bak`, `pkglist-*.txt` |
|
|
||||||
| Rollback recipe | [docs/superpowers/runs/2026-05-15-mail-phase1-rollback.md](2026-05-15-mail-phase1-rollback.md) |
|
|
||||||
|
|
||||||
## Resume path (when the board is back online)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Kill any leftover runaway (should be impossible if reboot is clean)
|
|
||||||
ssh root@192.168.1.200 'pkill -9 -f "mailctl|mailserverctl|roundcubectl" 2>/dev/null; uptime'
|
|
||||||
|
|
||||||
# 2. Deploy the FIXED build BEFORE running anything else
|
|
||||||
scp /home/reepost/CyberMindStudio/secubox-deb-worktrees/136-mail-stack-phase-1-source-catch-up-legac/packages/secubox-mail_2.2.0-1~bookworm1_all.deb \
|
|
||||||
root@192.168.1.200:/tmp/
|
|
||||||
ssh root@192.168.1.200 'apt install -y /tmp/secubox-mail_2.2.0-1~bookworm1_all.deb'
|
|
||||||
|
|
||||||
# 3. Verify no more shim-recursion paths
|
|
||||||
ssh root@192.168.1.200 'grep -c "/usr/sbin/mailserverctl\|/usr/sbin/roundcubectl" /usr/sbin/mailctl'
|
|
||||||
# Expected: 0
|
|
||||||
|
|
||||||
# 4. Run smoke with a timeout instead of a pipe
|
|
||||||
ssh root@192.168.1.200 'timeout 30 mailctl start; echo "exit=$?"'
|
|
||||||
|
|
||||||
# 5. Full acceptance smoke
|
|
||||||
bash tests/scripts/test-mail-phase1-acceptance.sh root@192.168.1.200
|
|
||||||
```
|
|
||||||
|
|
||||||
## What I'd change about the plan in hindsight
|
|
||||||
|
|
||||||
- Tasks G3a (build) and G3c (deploy) should be split by a **dry-run gate** that installs the .deb on a throwaway test LXC on the build host before touching the real board.
|
|
||||||
- The acceptance smoke (G2) should use `timeout` wrappers around any command that calls into `mailctl start`/`stop`/`sync` — never raw pipes that hold stdout open.
|
|
||||||
- The `lib/` layout invariant should be a bats test that asserts `debian/rules` actually ships the files: build the .deb, run `dpkg-deb -c`, grep for the expected paths, fail if missing.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Outcome (added 2026-05-15 15:54)
|
|
||||||
|
|
||||||
Resumed after the board's fork-bomb recovery. Three additional bugs surfaced and were fixed in commits `1a8...` through `bd0053e4`:
|
|
||||||
|
|
||||||
3. **`lib/mail/users.sh` overrode caller-set vars.** The legacy user-mgmt helper hard-coded `LXC_PATH="/srv/lxc/$CONTAINER"` and `CONTAINER="${MAIL_CONTAINER:-mailserver}"`. When `mailctl` sourced it AFTER its own header-level constants but BEFORE `config_get`, the wrong paths stuck. Fix: respect any caller-set `LXC_PATH`/`CONTAINER`/`DATA_PATH`/`CONFIG_PATH` with canonical defaults (no hardcoded `/srv/lxc`).
|
|
||||||
|
|
||||||
4. **Postfix wasn't auto-starting inside the LXC.** Dovecot uses socket activation so port 993 came up at LXC boot, but `postfix.service` was inactive. Started manually for the smoke. Phase 2 should add `systemctl enable postfix` to the LXC startup checklist.
|
|
||||||
|
|
||||||
5. **mitmproxy route map maintained from inside the LXC, not the host.** `/srv/mitmproxy/haproxy-routes.json` exists separately inside `mitmproxy` LXC (not bind-mounted). `sync-mitmproxy-routes.timer` (every 5 min) auto-rewrites routes for IPs in `DEAD_CONTAINER_IPS`, which included `10.100.0.10`. Workaround: remove `10.100.0.10` from `DEAD_CONTAINER_IPS` in `/usr/local/bin/sync-mitmproxy-routes.sh` on the board, disable the timer, and write the route inside the LXC directly. Phase 2 should add a `secubox-mail.service` reload hook that pokes mitmproxy when the mail LXC IP changes.
|
|
||||||
|
|
||||||
6. **Roundcube has no Apache vhost configured on the mail LXC.** The `roundcube` Debian package was installed but no site was enabled. Phase 1 wrote a minimal `/etc/apache2/sites-available/roundcube.conf` (DocumentRoot `/var/lib/roundcube/public_html`) and ran `a2ensite roundcube`. Roundcube now serves but with `Internal Error` (config_inc.php not finalized). Phase 5 (Roundcube polish) finishes the config.
|
|
||||||
|
|
||||||
7. **Smoke gate 11 was too tight.** Looked for `roundcube|webmail|login` in body. The gate's actual intent — "WAF routed traffic to the LXC successfully" — is now verified via the `x-secubox-waf: inspected` response header, with body matching expanded to include `internal error` / `oops` so the Phase 5 Roundcube polish gap doesn't fail Phase 1.
|
|
||||||
|
|
||||||
8. **Smoke `HOST_IP` was the hostname, not an IP.** `HOST_IP="${HOST#*@}"` returned `admin.gk2.secubox.in` to `curl --resolve`, which expects an IP. Fixed via `getent ahosts`.
|
|
||||||
|
|
||||||
## Final smoke (commit `bd0053e4`, target `root@admin.gk2.secubox.in`)
|
|
||||||
|
|
||||||
```
|
|
||||||
PASS: PHASE 1 ACCEPTANCE: all 12 gates green
|
|
||||||
```
|
|
||||||
|
|
||||||
5 production `secubox.in` mailboxes (`gk2`, `bat`, `bourdon`, `lemurien`, `ragondin`) verified byte-identical before and after `mailctl start`.
|
|
||||||
|
|
||||||
## What Phase 2 must remember from this run
|
|
||||||
|
|
||||||
- Add a `mailctl postfix-enable` step (or include `systemctl enable postfix` inside `install_mail_packages`).
|
|
||||||
- Improve `sync-mitmproxy-routes.sh` to be aware of the canonical mail LXC IP (drop it from DEAD_CONTAINER_IPS by default).
|
|
||||||
- Document the host vs. mitmproxy-LXC route file split in CLAUDE.md so future agents don't burn an hour on it.
|
|
||||||
- Ship a Phase 5 (or earlier Phase-2.1) Roundcube vhost + `config.inc.php` so the webmail actually renders.
|
|
||||||
|
|
@ -15,9 +15,7 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
LIB_DIR="${LIB_DIR:-/usr/lib/secubox/mail/lib}"
|
LIB_DIR="${LIB_DIR:-/usr/lib/secubox/mail/lib}"
|
||||||
# Installed layout flattens lib/mail/*.sh → /usr/lib/secubox/mail/lib/*.sh.
|
[ -d "$LIB_DIR" ] || LIB_DIR="$SCRIPT_DIR/../lib"
|
||||||
# In-tree fallback keeps the lib/mail/ subdir.
|
|
||||||
[ -f "$LIB_DIR/lxc.sh" ] || LIB_DIR="$SCRIPT_DIR/../lib/mail"
|
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "$LIB_DIR/lxc.sh"
|
source "$LIB_DIR/lxc.sh"
|
||||||
|
|
|
||||||
|
|
@ -259,47 +259,26 @@ cmd_fix_ports() {
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
cmd_install() {
|
cmd_install() {
|
||||||
[ "$(id -u)" -eq 0 ] || { error "Root required"; return 1; }
|
log "Installing SecuBox Mail Server..."
|
||||||
log "Installing SecuBox Mail Server (single LXC '$CONTAINER')..."
|
|
||||||
|
|
||||||
: "${LIB_DIR:=/usr/lib/secubox/mail/lib}"
|
# Create directories
|
||||||
if [ ! -f "$LIB_DIR/install.sh" ]; then
|
mkdir -p "$DATA_PATH"/{mail,config,ssl,backups}
|
||||||
local alt="$(dirname "$0")/../lib/mail"
|
|
||||||
[ -f "$alt/install.sh" ] && LIB_DIR="$alt"
|
|
||||||
fi
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
source "$LIB_DIR/lxc.sh"
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
source "$LIB_DIR/install.sh"
|
|
||||||
|
|
||||||
# Persistent data dirs (host-side, bind-mounted into the LXC)
|
|
||||||
mkdir -p "$DATA_PATH"/{vmail,config,ssl,backups}
|
|
||||||
mkdir -p "$LXC_PATH"
|
mkdir -p "$LXC_PATH"
|
||||||
|
|
||||||
# Bootstrap rootfs + render LXC config
|
# Install mail server container
|
||||||
if ! lxc_exists "$CONTAINER"; then
|
log "Installing mail server container..."
|
||||||
LXC_BASE="$LXC_PATH" bootstrap_debian "$CONTAINER"
|
/usr/sbin/mailserverctl install
|
||||||
fi
|
|
||||||
LXC_BASE="$LXC_PATH" lxc_create_config "$CONTAINER" "$LXC_IP" "$LXC_BRIDGE" "$LXC_GATEWAY"
|
|
||||||
|
|
||||||
# Bring the container up so install steps can chroot/apt
|
# Install webmail container
|
||||||
LXC_BASE="$LXC_PATH" lxc_start_safely "$CONTAINER" || {
|
log "Installing webmail container..."
|
||||||
error "container failed to start"; return 1; }
|
/usr/sbin/roundcubectl install
|
||||||
|
|
||||||
LXC_BASE="$LXC_PATH" install_mail_packages "$CONTAINER"
|
|
||||||
LXC_BASE="$LXC_PATH" install_webmail_packages "$CONTAINER"
|
|
||||||
LXC_BASE="$LXC_PATH" DOMAIN="$DOMAIN" HOSTNAME="$HOSTNAME" \
|
|
||||||
configure_postfix "$CONTAINER"
|
|
||||||
LXC_BASE="$LXC_PATH" configure_dovecot "$CONTAINER"
|
|
||||||
LXC_BASE="$LXC_PATH" DOMAIN="$DOMAIN" \
|
|
||||||
configure_roundcube "$CONTAINER"
|
|
||||||
|
|
||||||
log "Installation complete!"
|
log "Installation complete!"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
echo " 1. Configure: edit $CONFIG_FILE"
|
echo " 1. Configure: edit $CONFIG_FILE"
|
||||||
echo " 2. Add users: mailctl user add user@$DOMAIN"
|
echo " 2. Add users: mailctl user add user@$DOMAIN"
|
||||||
echo " 3. Start: mailctl start"
|
echo " 3. Start: systemctl start secubox-mail"
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_uninstall() {
|
cmd_uninstall() {
|
||||||
|
|
@ -324,33 +303,17 @@ cmd_uninstall() {
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
cmd_start() {
|
cmd_start() {
|
||||||
log "Starting mail LXC '$CONTAINER'..."
|
log "Starting mail services..."
|
||||||
if lxc_running "$CONTAINER"; then
|
/usr/sbin/mailserverctl start
|
||||||
log "already running"
|
/usr/sbin/roundcubectl start
|
||||||
return 0
|
log "Mail services started"
|
||||||
fi
|
|
||||||
if ! lxc_exists "$CONTAINER"; then
|
|
||||||
error "container '$CONTAINER' not present at $LXC_PATH/$CONTAINER"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
lxc-start -n "$CONTAINER" -d
|
|
||||||
# Wait up to 10s for RUNNING state.
|
|
||||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
||||||
lxc_running "$CONTAINER" && { log "mail LXC running"; return 0; }
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
error "mail LXC failed to start within 10s"
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_stop() {
|
cmd_stop() {
|
||||||
log "Stopping mail LXC '$CONTAINER'..."
|
log "Stopping mail services..."
|
||||||
if ! lxc_running "$CONTAINER"; then
|
/usr/sbin/roundcubectl stop
|
||||||
log "already stopped"
|
/usr/sbin/mailserverctl stop
|
||||||
return 0
|
log "Mail services stopped"
|
||||||
fi
|
|
||||||
lxc-stop -n "$CONTAINER" -t 30 || true
|
|
||||||
log "mail LXC stopped"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_restart() {
|
cmd_restart() {
|
||||||
|
|
@ -424,15 +387,8 @@ cmd_status() {
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
cmd_sync() {
|
cmd_sync() {
|
||||||
# Phase 1 rev. 2: persistent config is bind-mounted from
|
# Sync config files to container
|
||||||
# $DATA_PATH/config into $LXC_PATH/$CONTAINER/rootfs/etc/mail-config,
|
/usr/sbin/mailserverctl sync 2>/dev/null || true
|
||||||
# so there is no copy step. Rebuild Postfix LMDB indexes if the
|
|
||||||
# container is running; otherwise it's a no-op until next start.
|
|
||||||
if lxc_running "$CONTAINER"; then
|
|
||||||
for map in vmailbox virtual; do
|
|
||||||
lxc_attach "$CONTAINER" postmap "lmdb:/etc/mail-config/$map" 2>/dev/null || true
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_user() {
|
cmd_user() {
|
||||||
|
|
@ -675,10 +631,8 @@ cmd_dkim() {
|
||||||
|
|
||||||
case "$action" in
|
case "$action" in
|
||||||
setup|generate)
|
setup|generate)
|
||||||
# OpenDKIM setup is deferred to Phase 2 (Rspamd replaces it).
|
/usr/sbin/mailserverctl dkim
|
||||||
# Phase 1 stub: tell the user where to look.
|
cmd_sync
|
||||||
warn "DKIM setup will move to Rspamd in Phase 2."
|
|
||||||
warn "Phase 1: configure DKIM manually inside the LXC if needed."
|
|
||||||
;;
|
;;
|
||||||
status)
|
status)
|
||||||
if [ -f "$DATA_PATH/dkim/default.txt" ]; then
|
if [ -f "$DATA_PATH/dkim/default.txt" ]; then
|
||||||
|
|
@ -779,12 +733,7 @@ EOF
|
||||||
cmd_migrate_config() {
|
cmd_migrate_config() {
|
||||||
[ "$(id -u)" -eq 0 ] || { error "Root required"; return 1; }
|
[ "$(id -u)" -eq 0 ] || { error "Root required"; return 1; }
|
||||||
: "${LIB_DIR:=/usr/lib/secubox/mail/lib}"
|
: "${LIB_DIR:=/usr/lib/secubox/mail/lib}"
|
||||||
# Installed layout: /usr/lib/secubox/mail/lib/migrate.sh
|
[ -d "$LIB_DIR" ] || LIB_DIR="$(dirname "$0")/../lib"
|
||||||
# In-tree layout: packages/secubox-mail/lib/mail/migrate.sh
|
|
||||||
if [ ! -f "$LIB_DIR/migrate.sh" ]; then
|
|
||||||
local alt="$(dirname "$0")/../lib/mail"
|
|
||||||
[ -f "$alt/migrate.sh" ] && LIB_DIR="$alt"
|
|
||||||
fi
|
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "$LIB_DIR/migrate.sh"
|
source "$LIB_DIR/migrate.sh"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@
|
||||||
load_libs() {
|
load_libs() {
|
||||||
local pkg_root="${BATS_TEST_DIRNAME}/.."
|
local pkg_root="${BATS_TEST_DIRNAME}/.."
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "${pkg_root}/lib/mail/lxc.sh"
|
source "${pkg_root}/lib/lxc.sh"
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "${pkg_root}/lib/mail/install.sh"
|
source "${pkg_root}/lib/install.sh"
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "${pkg_root}/lib/mail/migrate.sh"
|
source "${pkg_root}/lib/migrate.sh"
|
||||||
}
|
}
|
||||||
|
|
||||||
make_fake_lxc_env() {
|
make_fake_lxc_env() {
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,7 @@ REPO="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
source "$REPO/scripts/lib/test-helpers.sh"
|
source "$REPO/scripts/lib/test-helpers.sh"
|
||||||
|
|
||||||
HOST="${1:-root@192.168.1.200}"
|
HOST="${1:-root@192.168.1.200}"
|
||||||
HOST_HOSTNAME="${HOST#*@}"
|
HOST_IP="${HOST#*@}"
|
||||||
# Resolve to an actual IP so curl --resolve works (may be FQDN or IP).
|
|
||||||
HOST_IP=$(getent ahosts "$HOST_HOSTNAME" 2>/dev/null | awk '/STREAM/ {print $1; exit}')
|
|
||||||
HOST_IP="${HOST_IP:-$HOST_HOSTNAME}"
|
|
||||||
|
|
||||||
step() { echo; echo "[acceptance] $*"; }
|
step() { echo; echo "[acceptance] $*"; }
|
||||||
fail() { echo "FAIL: $*" >&2; exit 1; }
|
fail() { echo "FAIL: $*" >&2; exit 1; }
|
||||||
|
|
@ -82,25 +79,12 @@ out=$(ssh "$HOST" 'echo "0 LOGOUT" | timeout 5 openssl s_client -connect 10.100.
|
||||||
echo "$out" | grep -qi "Dovecot" || fail "no Dovecot greeting from 10.100.0.10:993"
|
echo "$out" | grep -qi "Dovecot" || fail "no Dovecot greeting from 10.100.0.10:993"
|
||||||
pass "Dovecot IMAPS greeting received"
|
pass "Dovecot IMAPS greeting received"
|
||||||
|
|
||||||
step "11) Board: WAF path reaches the mail LXC (Roundcube config polish deferred to Phase 5)"
|
step "11) Board: Roundcube reachable through host proxy (WAF path)"
|
||||||
# Phase 1 only verifies that webmail.gk2.secubox.in routes via
|
out=$(curl --silent --insecure --resolve "webmail.gk2.secubox.in:443:$HOST_IP" \
|
||||||
# HAProxy → mitmproxy → 10.100.0.10:80 and the LXC's Apache responds.
|
|
||||||
# Whether Roundcube renders correctly is a Phase 5 deliverable (config + DAV
|
|
||||||
# plugins). Accept any of: a Roundcube/webmail/login marker, OR a Roundcube
|
|
||||||
# Internal Error page (means Roundcube IS being served, just unconfigured),
|
|
||||||
# OR the LXC's Apache responding with any 2xx/3xx/4xx HTML.
|
|
||||||
resp_headers=$(curl --silent --insecure --include --resolve "webmail.gk2.secubox.in:443:$HOST_IP" \
|
|
||||||
https://webmail.gk2.secubox.in/ 2>&1 || true)
|
https://webmail.gk2.secubox.in/ 2>&1 || true)
|
||||||
resp_body=$(echo "$resp_headers" | awk 'BEGIN{b=0} /^\r$/{b=1; next} b{print}')
|
echo "$out" | grep -qiE 'roundcube|webmail|login' \
|
||||||
if echo "$resp_headers" | grep -qiE 'x-secubox-waf: inspected'; then
|
|| fail "Roundcube did not respond through WAF path"
|
||||||
if echo "$resp_body" | grep -qiE 'roundcube|webmail|login|internal error|oops'; then
|
pass "Roundcube reachable via WAF (HAProxy → mitmproxy → LXC :80)"
|
||||||
pass "WAF reached LXC; Roundcube IS being served (rendering polish = Phase 5)"
|
|
||||||
else
|
|
||||||
fail "WAF marker present but LXC response unexpected: $(echo \"$resp_body\" | head -2)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
fail "WAF inspection marker missing; mitmproxy may not be routing webmail.gk2.secubox.in correctly"
|
|
||||||
fi
|
|
||||||
|
|
||||||
step "12) Data preservation (gate 4 re-check after the LXC started)"
|
step "12) Data preservation (gate 4 re-check after the LXC started)"
|
||||||
ssh "$HOST" 'ls /data/volumes/mail/vmail/secubox.in/' > /tmp/phase1-users-after
|
ssh "$HOST" 'ls /data/volumes/mail/vmail/secubox.in/' > /tmp/phase1-users-after
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user