Skip to content

fix: run plugin evaluation in a sandboxed process - #162

Open
youkoulayley wants to merge 3 commits into
traefik:masterfrom
youkoulayley:split-fetch-evaluate
Open

youkoulayley wants to merge 3 commits into
traefik:masterfrom
youkoulayley:split-fetch-evaluate

Conversation

@youkoulayley

@youkoulayley youkoulayley commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Description

Plugin code ran inside the Piceus process, next to its secrets. Each plugin is now checked in a separate piceus evaluate process, with an empty environment, Landlock and seccomp.

Details

  • The child writes its result on fd 3, not stdout, so plugin output cannot fake it.
  • No issue is created when the evaluator itself fails (crash, setup error): a broken deployment must not open issues on every plugin repository.
  • The text that goes into issues is cut to 1024 characters and cannot close the code block.
  • The GitHub token can come from a file (--github-token-file); --github-token is deprecated. The evaluation timeout is configurable (--evaluation-timeout).

@mmatur mmatur left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, splitting the evaluation into its own process with Landlock + seccomp is the right direction, and the fixtures make it easy to see what's covered.

A few things before merging:

  • The plugin can forge its own result. It runs in the same process as the evaluator and os.NewFile is available through Yaegi, so New() can do os.NewFile(3, "").Write([]byte("{}")), close the fd and return an error. The parent reads {}, ignores the exit code, and the broken plugin passes. I checked it locally with a small fixture. See the inline suggestions (check the exit code + drop NewFile). The PR description should also be updated since fd 3 doesn't really prevent faking the result.
  • Network rules need Landlock ABI v4 (kernel >= 6.7). On older kernels BestEffort() drops them silently and IMDS on port 80 is reachable again, which is one of the closure checks of the pentest. Do we know which kernel the prod/preprod nodes run?
  • No memory limit on the child: a plugin allocating in a loop will OOM the whole pod, Piceus included. Fine as a follow-up (the Job approach suggested on the issue would cover it), but worth tracking.
  • Worth checking in preprod that the pod seccomp profile allows the landlock syscalls, otherwise Piceus won't start (which is the right behavior, just good to know before deploying).

Comment thread pkg/core/evaluator.go Outdated
Comment thread pkg/core/symbols.go Outdated
Comment thread pkg/core/evaluator.go Outdated
Comment thread pkg/sandbox/sandbox_linux.go
Comment thread pkg/core/evaluator.go
Comment thread pkg/core/evaluator.go
Comment thread pkg/core/evaluator.go Outdated
Comment thread pkg/sandbox/sandbox_linux.go
Comment thread pkg/core/wasm.go Outdated
Comment thread pkg/core/wasm.go Outdated
Comment thread cmd/evaluate/command.go Outdated
- Read the result only when the evaluator exits cleanly, and drain the result pipe.
- Truncate the plugin error in the evaluator, and remove os.NewFile from the plugin symbols.
- Require Landlock ABI v6: the network and signal rules are always enforced.
- Do not create an issue when Piceus fails to create the temp files.

@darkweaver87 darkweaver87 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mmatur mmatur left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the quick fixes! A few small things, none of them blocking:

  • Right now any non-clean exit counts as an evaluator failure, so a plugin that crashes (panic in a goroutine, fatal error, stack overflow) is skipped on every run and its author never hears about it. We could keep exit code 3 as the only evaluator failure and treat the other exit codes as plugin errors. A fake {} followed by a crash would still be rejected.
  • TestEvaluator_Evaluate_emptyEnvironment doesn't really test the empty env: t.Setenv doesn't show up in /proc/<pid>/environ, so the fixture never sees the token anyway. It also needs requireSandbox(t): without the sandbox, it can read the real env of the test process.
  • Since we now need Landlock ABI v6, Piceus won't start on nodes with a kernel older than 6.12. Let's check the preprod/prod nodes before deploying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants