chore(deploy): cleanup left4me-web hardening + docs for kernel overlayfs
Drop MountFlags=shared (the assumption that it propagated fuse mounts
to host was incorrect on systemd 257 with ProtectSystem+ReadWritePaths).
Restore PrivateTmp=true (was dropped in 593611e for fuse propagation
that did not work). Rewrite the comment block to describe the new
model: mounts go through the left4me-overlay helper which nsenters
into PID 1's mount namespace, so the unit's mount-ns layout is no
longer load-bearing.
Update the three user-facing READMEs (root, l4d2host, deploy) to drop
fuse-overlayfs / fusermount3 prereqs and call out the kernel overlayfs
mount path through the privileged helper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
172e574a00
commit
9985ecc56c
5 changed files with 21 additions and 15 deletions
|
|
@ -56,7 +56,7 @@ See `deploy/README.md` for the Linux test deployment contract, including the run
|
|||
- Typer, PyYAML, pytest
|
||||
- Flask, SQLAlchemy, Alembic
|
||||
- HTMX (vendored locally), custom CSS, SSE
|
||||
- systemd user units, fuse-overlayfs, steamcmd
|
||||
- systemd units, kernel overlayfs (mounted via the `left4me-overlay` privileged helper), steamcmd
|
||||
|
||||
## Recommended Implementation Order
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ The deployment uses these paths:
|
|||
- `/var/lib/left4me/runtime`: per-instance runtime mount directories.
|
||||
- `/var/lib/left4me/tmp`: temporary files used by deployment/runtime operations.
|
||||
- `/usr/local/lib/systemd/system`: global systemd unit files, including `left4me-server@.service`.
|
||||
- `/usr/local/libexec/left4me`: privileged helper commands, including `left4me-systemctl` and `left4me-journalctl`.
|
||||
- `/usr/local/libexec/left4me`: privileged helper commands, including `left4me-systemctl`, `left4me-journalctl`, and `left4me-overlay` (the latter mounts the per-instance kernel overlay in PID 1's mount namespace via `nsenter`).
|
||||
- `/etc/sudoers.d/left4me`: sudoers rules allowing the web/runtime commands to call the helpers non-interactively.
|
||||
|
||||
Static units are generated for `/var/lib/left4me`. If `LEFT4ME_ROOT` changes, regenerate and reinstall the unit files instead of reusing the existing static units.
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@ EnvironmentFile=/etc/left4me/web.env
|
|||
ExecStart=/opt/left4me/.venv/bin/gunicorn --workers 1 --threads 32 --bind 0.0.0.0:8000 'l4d2web.app:create_app()'
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
# NoNewPrivileges intentionally not set: the worker invokes fusermount3
|
||||
# (setuid-root) and sudo to run the systemctl wrapper.
|
||||
# ProtectSystem=full + ReadWritePaths implicitly give this unit a
|
||||
# private mount namespace. MountFlags=shared makes its mount events
|
||||
# propagate back to the host so per-instance fuse-overlayfs mounts are
|
||||
# visible to the gameserver units (which inherit host mounts at their
|
||||
# own unshare time). Without it, the per-instance mount only exists
|
||||
# inside the worker's namespace and the gameserver units fail CHDIR.
|
||||
# NoNewPrivileges intentionally not set: the worker invokes sudo to run
|
||||
# the left4me-systemctl, left4me-journalctl, and left4me-overlay
|
||||
# privileged helpers, all setuid via sudo.
|
||||
# ProtectSystem=full + ReadWritePaths implicitly give this unit a private
|
||||
# mount namespace, but mount visibility no longer depends on it: overlay
|
||||
# mounts are performed by the left4me-overlay helper, which nsenters into
|
||||
# PID 1's mount namespace, so the resulting mount lives in the host
|
||||
# namespace where the per-instance gameserver units can see it.
|
||||
ProtectSystem=full
|
||||
ReadWritePaths=/var/lib/left4me
|
||||
MountFlags=shared
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
|||
|
|
@ -37,11 +37,17 @@ def test_web_unit_contains_required_runtime_contract():
|
|||
assert "ExecStart=/opt/left4me/.venv/bin/gunicorn" in unit
|
||||
assert "--workers 1" in unit
|
||||
assert "--threads 32" in unit
|
||||
# NoNewPrivileges must remain unset because sudo (used by the overlay,
|
||||
# systemctl and journalctl helpers) is setuid.
|
||||
assert "NoNewPrivileges=true" not in unit
|
||||
assert "PrivateTmp=true" not in unit
|
||||
# Restored now that fuse-overlayfs propagation is no longer the mechanism.
|
||||
assert "PrivateTmp=true" in unit
|
||||
assert "ProtectSystem=full" in unit
|
||||
assert "ReadWritePaths=/var/lib/left4me" in unit
|
||||
assert "MountFlags=shared" in unit
|
||||
# Mounts now happen in PID 1's namespace via the left4me-overlay helper,
|
||||
# so MountFlags propagation is irrelevant — and the previous assumption
|
||||
# that MountFlags=shared made it work was incorrect.
|
||||
assert "MountFlags=" not in unit
|
||||
|
||||
|
||||
def test_server_unit_contains_required_runtime_contract():
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ Validated on Debian 13 during the `ckn@10.0.4.128` smoke test:
|
|||
- Python 3.12+ with virtualenv/pip tooling for installing `l4d2host`.
|
||||
- `steamcmd` available on `PATH` and able to self-update as the runtime user.
|
||||
- 32-bit compatibility libraries for SteamCMD on amd64 Debian: `libc6-i386`, `lib32gcc-s1`, `lib32stdc++6`.
|
||||
- `fuse-overlayfs` and `fusermount3` for per-instance overlay mounts.
|
||||
- Kernel overlayfs (`mount -t overlay`); mount/umount go through the `left4me-overlay` privileged helper, which `nsenter`s into PID 1's mount namespace.
|
||||
- `systemctl --user` and `journalctl --user` available for the runtime user.
|
||||
- User lingering enabled when services must survive SSH sessions: `sudo loginctl enable-linger <user>`.
|
||||
- `/var/lib/left4me` created and writable by the runtime user, unless `LEFT4ME_ROOT` is set to another deployment-managed root.
|
||||
|
|
@ -61,7 +61,7 @@ sudo apt-get update
|
|||
sudo apt-get install -y \
|
||||
python3 python3-venv python3-pip \
|
||||
curl ca-certificates tar gzip \
|
||||
fuse-overlayfs fuse3 \
|
||||
util-linux \
|
||||
libc6-i386 lib32gcc-s1 lib32stdc++6
|
||||
|
||||
sudo mkdir -p /opt/steamcmd /var/lib/left4me/{installation,overlays,instances,runtime,tmp}
|
||||
|
|
|
|||
Loading…
Reference in a new issue