Add portable secure background-job executors - #289
Conversation
Co-authored-by: Möbius Agent <mobius-agent@users.noreply.github.com>
Keep the install source separate from the app source path and prove the bootstrap launch passes wait_for_ready while the existing interactive install remains immediate.\n\nCo-authored-by: Möbius Agent <mobius-agent@users.noreply.github.com>
Normalize reviewed background-agent access into one semantic contract, prefer Bubblewrap after a real namespace probe, and fall back to a fully probed Landlock boundary without ever running unsandboxed. Also make direct and scheduled launches use the configured backend and served runner/scaffold, document the design rationale and trade-offs, and verify both executors against the same adversarial contract. Co-authored-by: Möbius Agent <mobius-agent@users.noreply.github.com>
hamzamerzic
left a comment
There was a problem hiding this comment.
The isolation design and adversarial tests are strong, but the current branch cannot build on its own base image: python:3.12-slim resolves to a Debian release whose setpriv lacks --landlock-access, and the Dockerfile correctly fails at its capability probe. Please pin the runtime stages to the supported Trixie variants (node:24-trixie-slim and python:3.12-slim-trixie) and keep a source-level contract test for that image requirement. I have incorporated that correction into the reviewed integration branch so the complete secure-executor implementation can be battle-tested there without weakening the fail-closed probe.
|
Reviewed and merged to main through #304, preserving your authorship. The isolation architecture is retained; the integrated version pins the Trixie runtime so Landlock/setpriv support is actually present, with coverage for that contract. Thanks, Ricardo. |
Dependency
This change is intentionally based on #283 (
Wait for readiness before bootstrap app jobs). It does not replace or duplicate that readiness fix. #283 establishes the correct bootstrap ordering; this change makes the shared background-job boundary portable once that launch reaches the runner.If #283 is squash- or rebase-merged, this branch should be rebased onto the resulting
mainbefore submission so the reviewed diff remains exactly this topic commit.Problem
permissions.background_agent: trueis an owner-reviewed promise that an unattended agent receives a narrower data boundary than an ordinary app job. The runner previously expressed that promise directly as one Bubblewrap command.That works only when the outer container runtime permits Bubblewrap's namespace and capability setup. Having the binary installed, marking it setuid, and configuring the image are not sufficient: a managed runtime can still reject the required operation before app code starts. On the deployment that exposed this issue, the real Bubblewrap probe fails with
capset failed: Operation not permitted, while the kernel exposes Landlock ABI 7 and successfully enforces the required operations.Memory exposed the integration gap because it is the first Store app to combine
background_agentwith install-time initialization. The failure is not Memory-specific. Any current or future app using the same reviewed capability would enter the same shared runner.Verifiable and falsifiable claims
This proposal is built around the following claims. Each claim names a concrete observation that can prove it wrong.
JobAccessvalue.AF_UNIXsocket, or survive the supervisor unexpectedly.background_agentjob as an ordinary process.process,bubblewrap, orlandlock; fallback and failure reasons go to the existing durable job log. This is falsified if an operator cannot determine which executor was selected and why from those existing surfaces.Proposed change
JobAccessvalue:setprivowns filesystem rules and privilege dropping;Why this shape
Alternatives considered
SANDBOX=...: simple initially, but encodes deployment folklore and can select an executor that the current container cannot actually use.setprivkeeps the local helper limited to gaps the tool does not cover.Verification completed
python3 -m py_compilefor the launcher, installer, runner, and executor module./proc/<pid>content denial,AF_UNIXdenial, parent-death termination, and temporary-home cleanup.The broad test command must remove deployment-injected managed-sign-in variables when run inside a live managed container; otherwise local-account fixtures correctly receive a 403. Clean CI does not inherit those deployment variables.
Verification still expected in review/CI
setprivexposes Landlock support andlibseccomp.so.2is present.Forward path
The intended evolution rule is conservative:
That makes the next portability change local and testable without committing Möbius to machinery for hypothetical hosts or capabilities.