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>
126 lines
6 KiB
Markdown
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/`
|