How to run `sudo` in a nix-shell --pure shebang script?

I was converting an old bash script to use a nix-shell with the below shebang, to help ensure reproducibility going forward. The body of the script is immaterial but requires elevated access for some one small part and the rest should be done as a regular user.

#!/usr/bin/env nix-shell
#!nix-shell -i bash --pure
#!nix-shell -p bash
#!nix-shell -p sudo

sudo true

When I run it, the resulting error is the same as below:

$ nix-shell -p bash -p sudo --command "sudo true"
sudo: /nix/store/l2wsqr891kcm5kq2l4r2949g9kkxzdiz-sudo-1.9.12p1/bin/sudo must be owned by uid 0 and have the setuid bit set

How does one use both nix-shell --pure (which removes /run/wrappers/bin/sudo from PATH) and sudo?

1 Like

You do not use a pure shell or manually include the security wrappers in PATH.

The solutions are all kludges because Nix doesn’t directly handle them, and it turns sudo and some other packages that need wrappers into foot guns if you embed them in a build and deploy them without realizing they won’t work.

resholve has the ~same need to reference these within nix builds.

I have been wishing we had some kind of oracle (edit: posted about this in Limited interface to system/nix-external dependencies?) that could return a symlink path somewhere in /nix that nix updates to point at some of these unavoidable ~system executables (and issue an error at build time if this dependency can’t be satisfied).

To ensure they aren’t used in the build itself, the symlink either wouldn’t exist at build time or wouldn’t be accessible in the build sandbox. If the former, maybe the directory that contains these would itself be a symlink that gets detached before any build and reattached after.

My solution was a little complicated because I want it to be pure, and I want it to be cross-platform (MacOS and Linux), and sudo is problematic using nix on MacOS. Using the full path to the wrappers and a conditional package dependency seems to be working okay. For example:

#!/usr/bin/env nix-shell
#!nix-shell -i bash --pure
#!nix-shell -p other_dependencies
#!nix-shell -p "if pkgs.stdenv.isLinux then sudo else null"

set -Eeuf -o pipefail
shopt -s inherit_errexit

main() {
  case "${plat}" in
    Linux)
      build_cmd=(
        /run/wrappers/bin/sudo
        nixos-rebuild
        switch
        --flake
        ~/git/nixos
        --show-trace
      )
      ;;
    Darwin)
      build_cmd=(
        /run/current-system/sw/bin/darwin-rebuild
        switch
        --flake
        ~/git/nixos
        --show-trace
      )
      ;;
    *)
      err "unrecognized platform"
      ;;
  esac

  status=0
  output=$("${build_cmd[@]}" 2>&1 | tee /dev/tty) || status=$?
  if [[ "${status}" -ne 0 ]]; then
    less <<< "${output}"
  fi
}
main "$@"