Skip to content

docs(proposal): add build isolation design for sandboxed builds#1077

Open
pavank63 wants to merge 1 commit intopython-wheel-build:mainfrom
pavank63:proposal/build-isolation
Open

docs(proposal): add build isolation design for sandboxed builds#1077
pavank63 wants to merge 1 commit intopython-wheel-build:mainfrom
pavank63:proposal/build-isolation

Conversation

@pavank63
Copy link
Copy Markdown
Contributor

Add design proposal for --build-isolation flag that sandboxes PEP 517 build backend subprocesses using ephemeral Unix users and Linux namespaces. Includes security findings from proof-of-concept testing with build-attack-test package.

See: #1019

@pavank63 pavank63 requested a review from a team as a code owner April 21, 2026 20:35
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

Adds a new proposal document describing a --build-isolation/--no-build-isolation CLI flag (default off) to sandbox PEP 517 build backend subprocesses. The proposal specifies creating/removing a short‑lived system user, dropping privileges, entering Linux namespaces via unshare (network, limited PID, IPC, UTS), using --map-root-user for loopback, scrubbing environment, passing a WorkContext.build_isolation flag through build/hook invocations, running dependency installation with isolation disabled, prepending an isolation wrapper in external_commands, adjusting build directory permissions, and setting CARGO_NET_OFFLINE. The proposal lists findings from a PoC and explicit non‑goals. A toctree entry was added to include the doc in proposals.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a design proposal document for build isolation with sandboxed builds.
Description check ✅ Passed The description is directly related to the changeset, explaining the purpose of the added proposal document and referencing the related issue #1019.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/proposals/build-isolation.md`:
- Around line 206-210: The sentence claiming "Works in unprivileged Podman and
Docker containers" is contradictory because Docker's default seccomp may block
unshare; update the text around that sentence to make Docker support conditional
on seccomp/user namespace configuration: explicitly state that Podman works
unprivileged, and for Docker note that it only works if the container runtime
permits user namespaces/unshare (e.g., using a permissive seccomp profile or
enabling userns), and keep the existing note about Ubuntu 24.04 requiring sysctl
kernel.apparmor_restrict_unprivileged_userns=0; reference the existing terms
"unprivileged Podman and Docker containers", "unshare", and "sysctl
kernel.apparmor_restrict_unprivileged_userns=0" when making the clarification.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 81f43311-90aa-45a7-ac42-a78c1e652930

📥 Commits

Reviewing files that changed from the base of the PR and between c833650 and 60e3892.

📒 Files selected for processing (2)
  • docs/proposals/build-isolation.md
  • docs/proposals/index.rst

Comment thread docs/proposals/build-isolation.md Outdated
Copy link
Copy Markdown
Contributor

@rd4398 rd4398 left a comment

Choose a reason for hiding this comment

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

The proposal is strong on threat modeling but needs tightening on the integration details. I have added few comments / questions

Comment thread docs/proposals/build-isolation.md Outdated
#### 1. Ephemeral Unix user

Before each build invocation, the isolation script creates a
short-lived system user with `useradd` and removes it with `userdel`
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.

useradd / userdel, can modify /etc/passwd and /etc/shadow from what I know. This means fromager (or the isolation script) must run as root inside the container. That's a major assumption that isn't mentioned anywhere. What happens if fromager runs as a non-root user?

Comment thread docs/proposals/build-isolation.md Outdated

Before each build invocation, the isolation script creates a
short-lived system user with `useradd` and removes it with `userdel`
on exit (via `trap EXIT`). The user has:
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.

The userdel runs in a trap EXIT handler, but SIGKILL cannot be trapped. If a build gets OOM-killed or force-killed, the ephemeral user leaks. Over a long bootstrap run with hundreds of packages, this could accumulate orphaned fmr_* users in /etc/passwd.

Can we add that as a limitation?

## Goals

- A `--build-isolation/--no-build-isolation` CLI flag (default off)
that supersedes `--network-isolation` for build steps
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.

Clarification question: What happens with these combinations?

    • --build-isolation --network-isolation — redundant? Does build isolation absorb network isolation for build steps while network isolation still applies to non-build steps?
    • --build-isolation --no-network-isolation — does the user get network isolation for builds anyway (since build isolation includes it)?
    • --no-build-isolation --network-isolation — today's behavior?

Looking at the current code, network_isolation is passed to _run_hook_with_extra_environ for build hooks but also to _createenv for venv creation. Does build isolation apply to venv creation too, or
only PEP 517 hooks?

Comment thread docs/proposals/build-isolation.md Outdated

#### BuildEnvironment (`build_environment.py`)

- `run()` method accepts `build_isolation` parameter, defaults to
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.

This doesn't look right. Looking at the actual code in dependencies.py:547-553, _run_hook_with_extra_environ calls external_commands.run() directly — it doesn't go
through BuildEnvironment.run(). This matters because BuildEnvironment.run() is where env var setup (like CARGO_NET_OFFLINE) happens. The proposal needs to either change the hook runner to go through BuildEnvironment.run(), or duplicate that logic.

Add design proposal for --build-isolation flag that sandboxes PEP 517
build backend subprocesses using ephemeral Unix users and Linux
namespaces. Includes security findings from proof-of-concept testing
with build-attack-test package.

Signed-off-by: Pavan Kalyan Reddy Cherupally <pcherupa@redhat.com>
Co-Authored-By: Claude <claude@anthropic.com>
@pavank63 pavank63 force-pushed the proposal/build-isolation branch from 5fb0f31 to b632624 Compare April 28, 2026 12:16
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
docs/proposals/build-isolation.md (3)

47-48: ⚠️ Potential issue | 🟠 Major

Condition Docker support; current statement is factually over-broad.

Line 47 claims unprivileged Podman/Docker support unconditionally, but Docker commonly blocks unshare under default seccomp/userns settings. Make Docker support explicitly conditional.

Proposed doc fix
-- Works in unprivileged containers (Podman/Docker) without
--   `--privileged` or `--cap-add SYS_ADMIN`
+- Works in unprivileged Podman containers without `--privileged` or
+  `--cap-add SYS_ADMIN`.
+- Docker support is conditional on seccomp/user-namespace settings
+  that permit `unshare`.

As per coding guidelines **/*.md: Only comment on factual errors or broken links.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/proposals/build-isolation.md` around lines 47 - 48, The statement "Works
in unprivileged containers (Podman/Docker) without `--privileged` or `--cap-add
SYS_ADMIN`" is too broad; update the sentence to make Docker support conditional
by noting that unprivileged operation works on Podman and on Docker only when
userns/seccomp configurations permit `unshare` (or when explicit flags are
used), e.g. replace the current line with a conditional phrasing that calls out
Podman as supported unprivileged and Docker as supported only with appropriate
user namespace or seccomp configuration or when privileged/capabilities are
granted.

61-62: ⚠️ Potential issue | 🟠 Major

trap EXIT cleanup is not guaranteed in kill/OOM scenarios.

Line 61-62 states cleanup occurs on exit via trap, but SIGKILL/OOM kills bypass traps and can leak fmr_* users. Document this as a known limitation.

Proposed doc fix
- is created with `useradd` and removed on exit via `trap EXIT`. The user
+ is created with `useradd` and removed on normal exit via `trap EXIT`.
+ (Limitation: `SIGKILL`/OOM kill can bypass trap cleanup and leave orphaned
+ `fmr_*` users.) The user

As per coding guidelines **/*.md: Only comment on factual errors or broken links.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/proposals/build-isolation.md` around lines 61 - 62, Update the text
around the `trap EXIT` description to explicitly document that cleanup via `trap
EXIT` is not guaranteed for SIGKILL or OOM situations and therefore `fmr_*`
users may be leaked; mention this as a known limitation and suggest possible
mitigations (e.g., external watchdog/garbage-collector, systemd unit with
KillMode, or periodic cleanup job) so readers are aware the user removal can
fail under kill/OOM conditions.

60-63: ⚠️ Potential issue | 🟠 Major

Document required privileges for useradd/userdel.

The flow in Line 60-63 assumes permissions to modify /etc/passwd and /etc/shadow. Add an explicit prerequisite (root or equivalent container capability), and note behavior when running as non-root.

Proposed doc fix
 Build isolation combines an ephemeral Unix user with Linux namespace
 isolation. Before each build, a short-lived system user (`fmr_<random>`)
 is created with `useradd` and removed on exit via `trap EXIT`. The user
 has no home directory and no login shell, so it cannot read root-owned
 credential files like `.netrc` (mode 600). After dropping to the
 ephemeral user with `setpriv`, the script enters new namespaces with
 `unshare`:
+
+Prerequisite: the wrapper must run with privileges sufficient for
+`useradd/userdel` (typically root inside the build container/host).
+When run as non-root without those privileges, build isolation cannot
+create the ephemeral user and should fail fast.

As per coding guidelines **/*.md: Only comment on factual errors or broken links.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/proposals/build-isolation.md` around lines 60 - 63, The text describing
creation/removal of the short-lived system user (useradd/userdel creating
fmr_<random> and cleanup via trap EXIT) must state that this requires permission
to modify /etc/passwd and /etc/shadow (i.e., running as root or a container with
equivalent capabilities to update system accounts); update the doc to add a
prerequisite line that says “requires root or equivalent Linux capabilities to
run useradd/userdel,” and explicitly document expected behavior when run as
non-root (useradd will fail and the build should either error out or use an
alternative strategy such as using an existing unprivileged user, a user
namespace, or an ephemeral home-dir approach). Ensure you reference the existing
symbols useradd, userdel, trap EXIT, fmr_<random>, /etc/passwd and /etc/shadow
so readers can locate the relevant section.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docs/proposals/build-isolation.md`:
- Around line 47-48: The statement "Works in unprivileged containers
(Podman/Docker) without `--privileged` or `--cap-add SYS_ADMIN`" is too broad;
update the sentence to make Docker support conditional by noting that
unprivileged operation works on Podman and on Docker only when userns/seccomp
configurations permit `unshare` (or when explicit flags are used), e.g. replace
the current line with a conditional phrasing that calls out Podman as supported
unprivileged and Docker as supported only with appropriate user namespace or
seccomp configuration or when privileged/capabilities are granted.
- Around line 61-62: Update the text around the `trap EXIT` description to
explicitly document that cleanup via `trap EXIT` is not guaranteed for SIGKILL
or OOM situations and therefore `fmr_*` users may be leaked; mention this as a
known limitation and suggest possible mitigations (e.g., external
watchdog/garbage-collector, systemd unit with KillMode, or periodic cleanup job)
so readers are aware the user removal can fail under kill/OOM conditions.
- Around line 60-63: The text describing creation/removal of the short-lived
system user (useradd/userdel creating fmr_<random> and cleanup via trap EXIT)
must state that this requires permission to modify /etc/passwd and /etc/shadow
(i.e., running as root or a container with equivalent capabilities to update
system accounts); update the doc to add a prerequisite line that says “requires
root or equivalent Linux capabilities to run useradd/userdel,” and explicitly
document expected behavior when run as non-root (useradd will fail and the build
should either error out or use an alternative strategy such as using an existing
unprivileged user, a user namespace, or an ephemeral home-dir approach). Ensure
you reference the existing symbols useradd, userdel, trap EXIT, fmr_<random>,
/etc/passwd and /etc/shadow so readers can locate the relevant section.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 40e894b7-b62c-4db4-962e-1afaa6d6baba

📥 Commits

Reviewing files that changed from the base of the PR and between 60e3892 and b632624.

📒 Files selected for processing (2)
  • docs/proposals/build-isolation.md
  • docs/proposals/index.rst
✅ Files skipped from review due to trivial changes (1)
  • docs/proposals/index.rst

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.

2 participants