Locally this just prefixes activation commands with `run0 --`, so the
user's normal polkit agent (graphical or pkttyagent) handles auth.
For --target-host, run0 would need a controlling terminal, which we
deliberately do not allocate over SSH. Instead the equivalent
`systemd-run --uid=0 --pipe --wait ...` form is used. Without
--ask-elevate-password this relies on a polkit rule on the target
granting the deploying user `org.freedesktop.systemd1.manage-units`.
With --ask-elevate-password the command is wrapped in
polkit-stdin-agent, which registers a per-process polkit agent for the
child and answers the PAM conversation from stdin.
A transient unit does not inherit the SSH login environment. On NixOS
the unit's PATH is just the systemd store path, so neither `env` nor
`nix-env` would be found inside it. Elevator.wrap_remote() therefore
now takes the env mapping and the command, and returns the full remote
argv: sudo keeps the existing `/bin/sh -c 'exec /usr/bin/env -i ...'`
wrapper inside itself, while run0 puts the shell wrapper *around*
systemd-run and forwards the resolved values into the unit via
--setenv. The Arg/Args/EnvValue/PRESERVE_ENV types and the env-shell
helper move from process.py into elevate.py to avoid a circular import,
and callers are updated to import them from there.
polkit-stdin-agent is resolved on the machine doing the elevation
rather than baked in as a host-arch store path, which would be wrong
for --no-reexec, cross-arch deploys (re-exec hits ENOEXEC and falls
back), --rollback/--store-path, and Darwin deployers. Locally that is
PATH. Remotely, Elevator.for_target_config() binds the elevator to the
toplevel just placed on the target, and a small /bin/sh picker tries
<toplevel>/sw/bin/polkit-stdin-agent (target-arch, in the copied
closure) then PATH, exiting with an actionable error pointing at
system.tools.nixos-rebuild.enableRun0Elevation when neither resolves.
That option (added here) puts the agent in environment.systemPackages
and asserts security.polkit.enable.
A nixos-rebuild-target-host subtest exercises the full remote run0
path (polkit-stdin-agent + systemd-run + activation) end-to-end.
Drop the now-unused stdenv argument from package.nix while here.
Closes#507054.
A non-interactive polkit authentication agent that answers PAM prompts
from a file descriptor. Provides the missing `sudo --stdin` equivalent
for run0/systemd-run, which authorise via polkit and otherwise require
a controlling terminal for pkttyagent.
Used by the upcoming `nixos-rebuild --elevate=run0
--ask-elevate-password`. The machine performing the elevation enables
system.tools.nixos-rebuild.enableRun0Elevation (added in the next
commit), which puts this package on PATH locally and at
<toplevel>/sw/bin on a remote target.
Remote no longer carries auth state, the elevator owns the password
end-to-end. The prompt itself lives in Elevator.with_prompted_password()
so the CLI entry point stays a thin orchestrator and all
elevation-related plumbing is in one module.
--elevate={none,sudo} picks the privilege-elevation backend and
--ask-elevate-password (-S) prompts locally and feeds the password to
whichever backend is selected.
--sudo and --ask-sudo-password are kept as silent aliases that map onto
the new flags. --use-remote-sudo stays deprecated as before.
Mechanical replacement of `sudo: bool` with `elevate: Elevator` across
run_wrapper, nix.py and services.py, plus test updates. The temporary
shim in run_wrapper is removed.
`args.elevator` is constructed in execute() from the existing --sudo /
--ask-sudo-password flags so the CLI surface is unchanged in this
commit.
Replaces the inline sudo command assembly with calls into the Elevator
introduced in the previous commit. The `sudo: bool` parameter is kept
as a temporary shim that constructs a SudoElevator internally, so no
caller or test changes are needed and the existing test suite proves
the move is behaviour-preserving.
The shim is removed in the next commit.
Adds elevate.py with an Elevator base, NoElevator and SudoElevator.
Nothing uses it yet. This is a pure addition so the next commit can
swap run_wrapper over without mixing new code with the move.
The SudoElevator wrapping is byte-for-byte what run_wrapper currently
emits (including NIX_SUDOOPTS handling), so behaviour is unchanged
once the switch happens.
Motivated by #507054: threading a second `run0: bool` alongside
`sudo: bool` through every signature does not scale and makes invalid
combinations representable.