Hardening / sandbox (Linux)
--sandbox shrinks the blast radius of a PHP-level exploit. Even if an attacker
gets code execution inside PHP, the worker can't spawn a shell or tamper with
your code.
askr serve … \
--sandbox \
--sandbox-write /var/www/app/storage --sandbox-write /tmp
Two independent layers (Linux only; no effect elsewhere):
seccomp — no new processes
--sandbox installs a seccomp-BPF filter (all threads) that makes
execve/execveat/ptrace/process_vm_* return EPERM. So a compromised
request can't launch a shell — shell_exec/exec/Symfony\Process just
fail. It's applied before the PHP/tokio threads are created, so it covers the
thread PHP runs on.
If your app legitimately shells out (some packages do), those calls will fail under
--sandbox. Test first, or don't enable it for such apps.
Landlock — write only where allowed
Add --sandbox-write <dir> (repeatable) to also restrict the filesystem with
Landlock: the worker may read everywhere (so PHP,
extensions and templates keep working) but may write only under the listed
paths. A path-traversal or upload exploit then can't drop a webshell into the
docroot or modify your code — writes outside the allowlist get EACCES.
Typical allowlist for a Laravel app:
--sandbox-write /var/www/app/storage # logs, cache, sessions, compiled views
--sandbox-write /var/www/app/bootstrap/cache
--sandbox-write /tmp # uploads (streamed) + sqlite temp
Landlock degrades gracefully: on kernels without it (or an older ABI) the filter
is best-effort and never prevents startup — unless sandbox_required says otherwise.
Askr asks for ABI V6 (the newest the library knows; V2 added file re-parenting,
V3 truncate, V4 network, V5 ioctl) and lets the kernel enforce as much of it as it
has. /api/status shows landlock_abi as the ABI requested; a kernel older than
that enforces a narrower ruleset and the worker logs a warning saying so. A kernel
with no Landlock at all is reported as not applied — under best-effort compatibility
the call succeeds while enforcing nothing, which used to look like success.
Fail closed: --sandbox-required
By default the sandbox is advisory. A kernel without Landlock, a container without the seccomp capability, or any other missing feature logs a warning and the worker serves traffic looking exactly like one that hardened successfully. That default is not changing — an upgrade that started refusing to boot would be worse than the warning — but you can opt out of it:
askr serve … --sandbox-write /var/www/app/storage --sandbox-write /tmp --sandbox-required
A worker that cannot fully harden then exits (status 78) instead of serving, and the supervisor's crash-loop guard turns a fleet-wide failure into one clear "giving up".
It requires --sandbox-write, and refuses to start without it. Seccomp alone blocks
execve, which is not how a webshell runs here: Askr interprets PHP in-process, so a
.php file written into the docroot needs no process creation at all. Landlock write
rules are the control for that, so a "required" sandbox without them would be a promise
the sandbox cannot keep.
Attestation: what the workers actually got
/api/status reports the sandbox twice, side by side:
"sandbox": {"configured": true, "required": true, "workers": 8, "seccomp": 8, "landlock": 8, "landlock_abi": 1}
configured and required are what you asked for. workers, seccomp and landlock
are counted by the workers themselves as they apply it. A fleet where workers is 8 and
landlock is 6 is serving two workers unhardened — which, without sandbox_required,
is exactly the condition that used to log a warning and otherwise look identical to
success. Alert on seccomp < workers or landlock < workers.
Config file
[server]
sandbox = true
sandbox_write = ["/var/www/app/storage", "/var/www/app/bootstrap/cache", "/tmp"]
sandbox_required = false # true = refuse to serve unhardened (needs sandbox_write)
Verified
In a Linux container: with --sandbox --sandbox-write /tmp, a request that calls
shell_exec("id") returns EXEC-BLOCKED, a write to /tmp succeeds, a
write into the docroot is DENIED, and normal pages serve unchanged.
Notes
- Sidecars (queue/scheduler) are not sandboxed — queue jobs may legitimately shell out; only the internet-facing web workers are hardened.
- Combine with the non-root systemd unit + capabilities in UBUNTU.md for defence in depth.