left4me/docs/superpowers/specs/2026-05-15-session-handoff.md
mwiegand f5f8db84ef
spec(session-handoff): hardening refactor landed and verified on left4.me
12-task subagent-driven refactor complete. left4me-server@1: 7.5 → 1.3
systemd-analyze. left4me-web: 8.7 → 4.1. All 6 Test 8 attack vectors
blocked post-deploy. One acceptable SECCOMP audit line per gameserver
restart (Breakpad's ptrace fork, blocked by design). Test tooling
(gdb, seccomp, libseccomp-dev) apt-removed from left4.me. uid-split
spec marked superseded.

No queued follow-up. Adjacent work: build-overlay-unit refactor and
the deferred drop-in / configmgmt-responsibility reshape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:17:06 +02:00

126 lines
6 KiB
Markdown

# Session handoff — hardening refactor landed
The hardening refactor planned at
`docs/superpowers/plans/2026-05-15-hardening-refactor.md` is deployed
to `left4.me` and verified. This session executed all 12 tasks
subagent-driven; no follow-up implementation work is queued.
## What landed
**left4me commits** (this session, in order; all on `master`, pushed):
- `7c64910``spec(hardening-refactor): resolve emitter open items`
(verified ckn-bw systemd-bundle emitter handles tuples + empty values)
- `8e678b6``deploy/files: annotate reference units with per-directive hardening comments`
- `37309ba``spec(hardening-test-plan): fix four bugs surfaced by executor`
- `f615d0d``spec(user-uid-split): mark superseded by the hardening refactor`
**ckn-bw commits** (this session, in order; all on `master`, pushed):
- `85b9af0``bundles/left4me: add HARDENING_{COMMON,SERVER,WEB} constants`
- `640461c``bundles/left4me: spread HARDENING_SERVER into left4me-server@.service`
- `c6721e7``bundles/left4me: spread HARDENING_WEB into left4me-web.service`
- `130b0b1``bundles/left4me: ship kernel.yama.ptrace_scope=2 sysctl drop-in`
**Deploy:** `bw apply ovh.left4me` ran clean in 10 s (194 OK, 4 fixed,
0 failed). `left4me-web.service` restarted automatically by `bw`;
`left4me-server@1` and `@2` restarted manually post-apply.
## What's live on `left4.me`
| Unit | systemd-analyze score | State |
|---|---|---|
| `left4me-server@1.service` | **1.3 OK** (was 7.5 baseline) | active since 13:13:39 UTC |
| `left4me-server@2.service` | 1.3 OK | active since 13:14:40 UTC |
| `left4me-web.service` | **4.1 OK** (was 8.7 baseline) | active since 13:01:06 UTC |
Sysctl: `kernel.yama.ptrace_scope = 2` (managed by ckn-bw bundle now,
not hand-applied).
Composition matches Test 7 of the test plan with two amendments
(`SystemCallArchitectures=native x86`, `PrivatePIDs=true`) and one
addition (`SocketBindAllow=udp:27000-27999 tcp:27000-27999`).
`MemoryDenyWriteExecute=true` permanently excluded.
## Attack vectors blocked (Test 8 subset rerun post-deploy)
- **D1.a — srcds reads DB**: `cat /var/lib/left4me/left4me.db` from
inside the unit's mount namespace → `No such file or directory`
- **D1.b — srcds reads web.env**: `cat /etc/left4me/web.env`
`No such file or directory`
- **D1.c — srcds sees /opt**: empty listing
- **D2.b — srcds sees gunicorn PID via /proc**: `cannot access /proc/<pid>`
(PrivatePIDs in effect; PID doesn't exist in the namespace)
- **D5 — cross-instance ptrace**: `cannot access /proc/<peer-srcds-pid>`
(cross-instance PID isolation)
- **Syscall filter compiled correctly**: `ptrace` and `process_vm_*`
not in the compiled allow list (verified via
`systemd-analyze syscall-filter`)
## Known acceptable noise
- **One SECCOMP audit line per gameserver restart** (`type=1326`,
i386 syscall 26 = `ptrace`, sig=31 SIGSYS, code=0x80000000
SECCOMP_RET_KILL_PROCESS). Source: srcds's Breakpad crash-reporter
init forks a child that attempts `ptrace`; we block it by design.
The child gets killed; the main srcds process is unaffected. Net
effect: Valve doesn't get crash minidumps from this host.
Acceptable trade-off given the threat model. If the audit-log noise
becomes a problem, switch the SECCOMP filter's action from
`KILL_PROCESS` to `EPERM` via `SystemCallErrorNumber=EPERM` (would
let breakpad fail cleanly instead of getting killed; same security
outcome).
## Host cleanup done
`gdb`, `libseccomp-dev`, `seccomp` removed via `apt remove --purge`.
Test tooling was installed during the test-plan execution session
(commit `461b8d0`); not needed in steady state. ~13 MB freed.
## What's next
No queued follow-up from this work. Adjacent open work:
- **`build-overlay-unit` refactor**
(`docs/superpowers/specs/2026-05-15-build-overlay-unit-design.md`).
Will reuse `HARDENING_COMMON` (or a sandbox-class variant) when it
lands. Sequenced after this; not blocked.
- **Broader configmgmt-responsibility reshape** — hardening as drop-in
files living in left4me with ckn-bw as a thin file-shipper. Real
direction, deliberately deferred to a dedicated session in this
refactor's design doc.
- **Stale RCON port app bug** flagged in the prior executor's handoff.
Not a hardening issue; separate scope.
## Open items the operator should sanity-check manually
I executed everything programmatically that I could. The following
need an eyeballed check via the web UI from your laptop:
1. Login to the web UI; confirm session works (would catch a SECRET_KEY
regression or session-cookie issue).
2. Start/stop a server from the UI (exercises the sudo path on the web
unit; if the SystemCallFilter or any other web hardening broke
sudo, this would fail).
3. View live logs for a server (uses `sudo left4me-journalctl`).
4. Trigger an overlay rebuild for a script overlay (exercises the
sandbox; unchanged by this refactor, but a smoke against the
full chain).
If any of those break, the most likely cause is the web unit's
`SystemCallFilter`. Drop-in override at
`/etc/systemd/system/left4me-web.service.d/00-debug.conf` with
`SystemCallLog=...` instead of `SystemCallFilter` to identify the
offending syscall, then narrow the filter.
## Pointers
- Threat model: `docs/superpowers/specs/2026-05-15-hardening-threat-model.md`
- Defenses survey: `docs/superpowers/specs/2026-05-15-hardening-defenses-survey.md`
- Test plan (with executor results + this session's bug fixes):
`docs/superpowers/specs/2026-05-15-hardening-test-plan.md`
- Design doc: `docs/superpowers/specs/2026-05-15-hardening-refactor-design.md`
- Implementation plan: `docs/superpowers/plans/2026-05-15-hardening-refactor.md`
- uid-split spec (marked superseded): `docs/superpowers/specs/2026-05-15-user-uid-split-design.md`
- Live unit emission: `~/Projekte/ckn-bw/bundles/left4me/metadata.py`
(`HARDENING_COMMON` etc. near top; spreads at the
`left4me-server@.service` and `left4me-web.service` entries)
- Reference units (annotated): `deploy/files/usr/local/lib/systemd/system/`