[PID1 REVIEW] Environment sanitization insufficient -- PID1 env leaks to all children #35

Open
opened 2026-05-11 11:48:12 +00:00 by thabeta · 1 comment
Owner

Context

From a deep architectural review of my_init as PID1 (May 2026).

Problem

The process spawn code in both spawn_shimmed and spawn_process only unsets 6 environment variables:

let sensitive_vars = [
    "LD_PRELOAD", "LD_LIBRARY_PATH", "PYTHONPATH",
    "NODE_PATH", "PERL5LIB", "RUBYLIB",
];

Everything else is inherited. A proper PID1 should use env_clear() and whitelist only essential variables (PATH, HOME, LANG, TERM, and explicitly configured service env).

Why This Matters for PID1

PID1's environment is inherited by every child process. If PID1 was started with:

  • SSH_AUTH_SOCK -- SSH agent socket leaked to all services
  • DBUS_SESSION_BUS_ADDRESS -- D-Bus session leaked
  • AWS_SECRET_ACCESS_KEY or other secrets -- leaked to services running as different users
  • XDG_* variables -- desktop session context leaked into services

For services running with user/group set for privilege separation, this directly undermines the isolation boundary. A service running as app user would inherit the root session environment.

Additional Missing Variables

The following are commonly recommended to clear for privilege-separated processes:

  • LD_AUDIT, LD_DYNAMIC_WEAK, LD_PROFILE, LD_BIND_NOW -- linker behavior
  • GCONV_PATH -- character set conversion paths
  • HOSTALIASES -- hostname resolution override
  • RES_OPTIONS, LOCALDOMAIN -- resolver configuration
  • TMPDIR -- symlink attack vector
  • IFS -- shell injection in shim mode
  • BASH_ENV, ENV -- shell startup file injection
  • PERLIO_DEBUG, PYTHONINSPECT -- interpreter debug modes

Files

  • crates/my_init_server/src/process.rs -- sensitive_vars array (appears twice: spawn_shimmed and spawn_process)

Suggested Fix

Replace the blocklist approach with a whitelist:

let whitelist = ["PATH", "HOME", "LANG", "TERM"];
let mut cmd = Command::new(...);
cmd.env_clear();
for var in whitelist {
    if let Ok(val) = std::env::var(var) {
        cmd.env(var, val);
    }
}
for (key, value) in &config.service.env {
    cmd.env(key, value);
}

This is a breaking change for services that rely on inherited env vars, but for a PID1 running privilege-separated services, correctness matters more than convenience.

## Context From a deep architectural review of my_init as PID1 (May 2026). ## Problem The process spawn code in both `spawn_shimmed` and `spawn_process` only unsets 6 environment variables: ```rust let sensitive_vars = [ "LD_PRELOAD", "LD_LIBRARY_PATH", "PYTHONPATH", "NODE_PATH", "PERL5LIB", "RUBYLIB", ]; ``` Everything else is inherited. A proper PID1 should use `env_clear()` and whitelist only essential variables (`PATH`, `HOME`, `LANG`, `TERM`, and explicitly configured service `env`). ## Why This Matters for PID1 PID1's environment is inherited by **every child process**. If PID1 was started with: - `SSH_AUTH_SOCK` -- SSH agent socket leaked to all services - `DBUS_SESSION_BUS_ADDRESS` -- D-Bus session leaked - `AWS_SECRET_ACCESS_KEY` or other secrets -- leaked to services running as different users - `XDG_*` variables -- desktop session context leaked into services For services running with `user`/`group` set for privilege separation, this directly undermines the isolation boundary. A service running as `app` user would inherit the root session environment. ## Additional Missing Variables The following are commonly recommended to clear for privilege-separated processes: - `LD_AUDIT`, `LD_DYNAMIC_WEAK`, `LD_PROFILE`, `LD_BIND_NOW` -- linker behavior - `GCONV_PATH` -- character set conversion paths - `HOSTALIASES` -- hostname resolution override - `RES_OPTIONS`, `LOCALDOMAIN` -- resolver configuration - `TMPDIR` -- symlink attack vector - `IFS` -- shell injection in shim mode - `BASH_ENV`, `ENV` -- shell startup file injection - `PERLIO_DEBUG`, `PYTHONINSPECT` -- interpreter debug modes ## Files - `crates/my_init_server/src/process.rs` -- `sensitive_vars` array (appears twice: `spawn_shimmed` and `spawn_process`) ## Suggested Fix Replace the blocklist approach with a whitelist: ```rust let whitelist = ["PATH", "HOME", "LANG", "TERM"]; let mut cmd = Command::new(...); cmd.env_clear(); for var in whitelist { if let Ok(val) = std::env::var(var) { cmd.env(var, val); } } for (key, value) in &config.service.env { cmd.env(key, value); } ``` This is a breaking change for services that rely on inherited env vars, but for a PID1 running privilege-separated services, correctness matters more than convenience.
Member

Classification: valid-bug — PID1 env sanitization insufficient; only 6 variables unset when env_clear() + whitelist should be used for a PID1 running privilege-separated services.

This is a separate review of the same code path as #19 but with a PID1-specific perspective. The same sensitive_vars blocklist approach is used; PID1's environment leaks to every child process. SSH_AUTH_SOCK, DBUS_SESSION_BUS_ADDRESS, AWS secrets would all be inherited by services.

> Classification: valid-bug — PID1 env sanitization insufficient; only 6 variables unset when env_clear() + whitelist should be used for a PID1 running privilege-separated services. This is a separate review of the same code path as #19 but with a PID1-specific perspective. The same sensitive_vars blocklist approach is used; PID1's environment leaks to every child process. SSH_AUTH_SOCK, DBUS_SESSION_BUS_ADDRESS, AWS secrets would all be inherited by services.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
geomind_code/my_init#35
No description provided.