Skip to content

tools/sandbox: update with new features, variants, and tests - #3566

Open
gzvolsky-tm wants to merge 5 commits into
masterfrom
gzvolsky/sandbox-uid-gid-mappings
Open

tools/sandbox: update with new features, variants, and tests#3566
gzvolsky-tm wants to merge 5 commits into
masterfrom
gzvolsky/sandbox-uid-gid-mappings

Conversation

@gzvolsky-tm

@gzvolsky-tm gzvolsky-tm commented Aug 3, 2026

Copy link
Copy Markdown

This PR brings several enhancements, reliability improvements, and tests to the sandbox:

  • Add support for SANDBOX_UID_MAP and SANDBOX_GID_MAP environment variables. This allows mapping multiple UID/GID ranges into the user namespace utilizing newuidmap and newgidmap. Both must be set together.
  • Add MOUNT_PROC=0 to skip remounting /proc. This provides a workaround for environments (like newer systemd or certain container runtimes) that prevent remounting a partially masked /proc.
  • Introduce SANDBOX_FILE_MOUNTS to allow bind-mounting a comma-separated list of specific files into the sandbox.
  • The extra loopback IP address (10.1.1.1, added in Add local IP and default gateway to network for sandbox tool #3310) can now be overridden via SANDBOX_LOCAL_IP, or disabled by setting it to the empty string.
  • Mount a tmpfs over /dev/shm so nothing can be inadvertently shared through it.
  • Introduce a pipe (sync_fd) to synchronize the parent and child processes. This prevents the child from hanging indefinitely if the parent dies before the sandbox setup is complete.
  • Add SIGTERM forwarding from the parent to the sandboxed child process.
  • Add a README documenting the sandbox, its configurable environment variables, and how to use them with Please.
  • Add a sandbox_test.go test suite to test the network/mount isolation, /proc remounting, ID mapping, and process lifecycle across all combinations of the environment knobs.
  • Install uidmap (shadow-uidmap on alpine) in the CI images and delegate root:0:65536 in /etc/subuid and /etc/subgid, since the sandbox execs newuidmap/newgidmap even for root. The sandbox tests must stay manual/localonly until the images are updated.

Behaviour changes

  • /proc is now mounted read-only. This may affect tests that write to it, e.g. to /proc/self/oom_score_adj.
  • The TMP_DIR bind mount is now recursive (MS_REC), so submounts of TMP_DIR are visible inside the sandbox.
  • When TMP_DIR is /tmp or lives under it, the sandbox no longer returns early: it still bind-mounts it to /tmp/plz_sandbox, remounts the root read-only, and changes into it.
  • The root read-only remount now preserves the existing nosuid/nodev/noexec/noatime mount flags instead of silently dropping them.

Breaking change

  • Removing nonet_sandbox is a breaking change for downstream repositories that referenced it, and for remote execution workers that invoked a variant binary and relied on its defaults. The migration path is to use a wrapper that defines the environment (see tools/sandbox/README.md).

Resolves #3541

This commit brings several enhancements, reliability improvements, and tests to the sandbox:

- Add support for `SANDBOX_UID_MAP` and `SANDBOX_GID_MAP` environment variables. This allows mapping multiple UID/GID ranges into the user namespace utilizing `newuidmap` and `newgidmap`.
- Add `noproc_sandbox` and `nonetproc_sandbox` which skip remounting `/proc`. This provides a workaround for environments (like newer systemd or certain container runtimes) that prevent remounting a partially masked `/proc`.
- Introduce `SANDBOX_FILE_MOUNTS` to allow bind-mounting a comma-separated list of specific files into the sandbox.
- The loopback IP address can now be specified via `SANDBOX_LOCAL_IP`.
- Introduce a pipe (`sync_fd`) to synchronize the parent and child processes. This prevents the child from hanging indefinitely if the parent dies before the sandbox setup is complete.
- Add `SIGTERM` forwarding from the parent to the sandboxed child process.
- Add a README documenting the sandbox, its variants, and configurable environment variables.
- Add a `sandbox_test.go` test suite to test the network/mount isolation, ID mapping, and process lifecycle across all sandbox variants.
@peterebden

Copy link
Copy Markdown
Collaborator

Maybe let's think about what the structure of these binaries should look like. I'm not sure that it's very optimal to ship multiple different ones with Please for the different combinations of things that it could sandbox - maybe that should be some flag / env var thing instead on a single binary.

…ailures

Backward compatibility:
- add_local_ip: add 10.1.1.1 to the loopback interface by default
  again, for backward compatibility with the previous upstream
  behaviour (#3310). SANDBOX_LOCAL_IP now overrides the address, and
  setting it to empty string disables it.

Build fixes:
- Fix test name clash by renaming sandbox_test to sandbox_go_test
- Use the name `please_sandbox` consistently
- Fix Darwin and FreeBSD builds by updating the contain() stub and
  moving <string.h> back out of the #ifdef __linux__ block.
- Restore static = (CONFIG.get("STATIC_SANDBOX") is not None) so the
  alpine release links the sandboxes statically (defined in .plzconfig.alpine).
- Drop inline from perror_sock: the inline definition without static
  emits no external symbol, breaking -O0 links.

Tests:
- Keep SANDBOX_UID_MAP/SANDBOX_GID_MAP outside ids below 65536 so the
  mappings work in constrained container uid spaces.
- Assert network isolation via curl's exit code (7, couldn't connect)
  instead of error text (wording changed in curl 8).
- Install uidmap (shadow-uidmap on alpine) in the CI images and
  delegate root:0:65536 in /etc/subuid and /etc/subgid, since the
  sandbox execs newuidmap/newgidmap, which check subordinate id
  delegation even for root.
Move the sandbox Go test into its own directory to avoid including
adjacent C files in the Go package.

Fixes
```
level=error msg="[linters_context] typechecking error: C source files not allowed when not using cgo or SWIG: main.c nonet_main.c nonetproc_main.c noproc_main.c sandbox.c"
```
- Restore SIGKILL as the parent-death signal. The sandboxed command is
  PID 1 in the new namespace, so the kernel discards SIGTERM and the
  command leaks as an orphan process when the sandbox crashes.
- map_ids: return an error when waitpid fails instead of reading an
  uninitialised status.
- Tolerate an absent /dev/shm.
- Require SANDBOX_UID_MAP and SANDBOX_GID_MAP to be set together. The
  other options, defaulting to a trivial mapping or mirroring the other
  mapping, made an assumption that isn't always correct.
- Fix the TMP_DIR-under-/tmp check to exclude `/tmp[^/]+`.
- Exec new*idmap via PATH search. This is safe because the sandbox
  binary itself runs with neither setuid bits nor file capabilities.
- Fix fmt.Sprintf lint errors.
@gzvolsky-tm
gzvolsky-tm force-pushed the gzvolsky/sandbox-uid-gid-mappings branch from f6d40cf to 151a097 Compare August 6, 2026 13:27
- Remove `nonet_sandbox`, `noproc_sandbox`, and `nonetproc_sandbox`.
  Under local execution Please sets `SHARE_NETWORK` and `SHARE_MOUNT`
  explicitly on every sandboxed invocation, so the variants' flipped
  defaults only took effect where something else invoked them, e.g.
  remote execution workers.
- Add `MOUNT_PROC=0` option to `please_sandbox` to allow disabling
  `/proc` remounting.
- Add README section explaining how to use the environment variables
  with Please.
- Refactor per-binary test matrices whose premises were per-variant
  defaults and multiple binaries.

BREAKING CHANGE:

Removing `nonet_sandbox` is a breaking change for downstream
repositories that referenced it, and for remote execution workers that
invoked a variant binary and relied on its defaults. The migration path
is to use a wrapper that defines the environment (see
tools/sandbox/README.md).
@gzvolsky-tm
gzvolsky-tm force-pushed the gzvolsky/sandbox-uid-gid-mappings branch from 88fcb15 to 8807a2a Compare August 6, 2026 18:25
@gzvolsky-tm
gzvolsky-tm marked this pull request as ready for review August 6, 2026 18:25
Comment thread tools/sandbox/test/BUILD
@@ -0,0 +1,15 @@
go_test(

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.

Why put the tests in a subdirectory?

Comment thread tools/sandbox/sandbox.c

static int cloned_pid;

int perror_sock(char *errmsg, const int sock) {

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.

Suggested change
int perror_sock(char *errmsg, const int sock) {
inline int perror_sock(char *errmsg, const int sock) {

Comment thread tools/sandbox/README.md
Comment on lines +28 to +29
When the network namespace is used, the loopback interface is brought up with an additional IP
address. `SANDBOX_LOCAL_IP` defines, defined empty string disables the extra address.

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.

Suggested change
When the network namespace is used, the loopback interface is brought up with an additional IP
address. `SANDBOX_LOCAL_IP` defines, defined empty string disables the extra address.
When the network namespace is used, the loopback interface is brought up with an additional IP
address, which defaults to 10.1.1.1. The `SANDBOX_LOCAL_IP` environment variable can be used
to change this IP, and setting it to the empty string disables the extra address entirely.

Comment thread tools/sandbox/test/BUILD
Comment on lines +7 to +10
labels = [
"localonly",
"manual",
],

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.

Presumably these tests aren't currently being run by CircleCI? Can we remove these labels? Do we need to make changes to the CircleCI config?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux Sandbox: Support multi UID/GID mappings via newuidmap and newgidmap

3 participants