runas is a Rust utility that provides an authentication front-end, with optional backend, for running commands with elevated privileges on trusted, personal systems. Its goal is to do one thing well: verify the caller and hand off execution to a reliable backend rather than attempt to become a full policy engine like sudo, for hardware that does not require that sort of control.
- Authenticate, then delegate.
runasverifies the invoking user and then delegates command execution to one of several execution backends (systemd-run, run0, or a minimal internal executor). - Minimal configuration. No complex policy files; user membership in the "wheel" group is the single mechanism to grant privilege.
- Build-time choices. Backend and authentication mechanisms are selected at compile time using Cargo features so deployments remain small and explicit.
- Intended audience. Personal machines, home labs, single-admin environments — not enterprise policy management.
- Two authentication modes:
- Shadow-file verification (default): locally compare password hashes from the system shadow file.
- PAM-based authentication (
use_pamfeature): leverage system PAM for authentication and session handling.
- Three execution backends (selectable at build time):
systemd-run(default): uses systemd through systemd-run to start the command.run0(backend_run0feature): uses systemd through systemd-run via run0 to start the command.- Native "scopex" executor (
backend_scopexfeature): a minimal internal executor that runs commands in a clean process environment.
- "Sudo-like" CLI surface where applicable — common flags are supported, but not the full
sudofeature set.
Features
| Options | Value | Description |
|---|---|---|
| -s, --shell | Run $SHELL as the target user | |
| -u, --user | USER | Run process as the specified user name or ID |
| -g, --group | GROUP | Run process as the specified group name or ID |
| -h, --help | Display help screen | |
| --env | VAR=VAL | Set environment variable |
| --preserve-env | LIST | Comma separated list of variables to preserve |
| -n, --non-interactive | Non-interactive mode, don't prompt for password | |
| -S, --stdin | Read password from standard input | |
| -A, --askpass | Use an askpass helper program (build-time option) | |
| -v, --version | Display version information and exit | |
| -- | Stop processing command line arguments |
Build Features
| Flag | Description |
|---|---|
| use_pam | This will build runas with PAM support. |
| backend_run0 | This will build runas to target run0 instead of systemd-run directly |
| backend_scopex | This will build runas with it's own backend implementation |
| with_askpass_support | This will build runas with --askpass support. |
| without_expand_env | This will build runas without --expand-environment=false (systemd-run) |
- Simplicity over policy:
runasintentionally avoids a full policy language. Membership in the "wheel" group grants run capability. - No timestamp cache:
runasdoes not store credentials or implement password-timestamping. If you require cached authentication, configure it through PAM (when built with PAM) or use a different tool. - Intended for trusted machines:
runasis not designed for multi-administrator or high-compliance environments. For granular access control, usesudowith a propersudoerspolicy.
Build commands demonstrate how to enable different features. Features are additive; choose the backend and authentication you want at compile time.
Default build (systemd-run backend, shadow auth):
cargo build --release
Build with
run0backend:cargo build --release --features "backend_run0"Build with the native executor:
cargo build --release --features "backend_scopex"Build with PAM authentication:
RUSTFLAGS="-l pam" cargo build --release --features "use_pam"
You must manually add
-l pamto theRUSTFLAGSenvironment variable. Sadly there is no way to do this automatically via cargo.toml or build.rs.Build with askpass support:
cargo build --release --features "with_askpass_support"Askpass is a separate, opt-in feature because it adds another path for supplying authentication credentials. Mistakes in this security-sensitive code could potentially introduce an authentication bypass. Only enable it when askpass integration is required.
Omit systemd's "--expand-environment=false" option (available when building for systemd-run):
cargo build --release --features "without_expand_env"You can combine features:
RUSTFLAGS="-l pam" cargo build --release --features "backend_run0,use_pam"
The systemd-run executable defaults to /usr/bin/systemd-run. A different
absolute path can be selected at build time using RUNAS_SYSTEMD_PATH:
RUNAS_SYSTEMD_PATH=/custom/path/systemd-run cargo build --releaseWhen built with the with_askpass_support feature, -A or --askpass reads
the password from an external helper instead of the terminal. Set
RUNAS_ASKPASS to the helper's absolute path:
RUNAS_ASKPASS=/usr/libexec/seahorse/ssh-askpass runas --askpass commandFor compatibility with existing sudo askpass configurations, runas falls
back to SUDO_ASKPASS when RUNAS_ASKPASS is unset or empty. If both are set,
RUNAS_ASKPASS takes precedence. The helper receives the password prompt as
its first argument and must write the password to standard output. The helper
path must be absolute.
In addition to --env and --preserve-env, runas can set environment
variables from /etc/runas.env. The file is optional; if it does not exist,
runas continues without an error.
Each non-empty line uses one of the following formats:
TARGET NAME=VALUE
TARGET NAME
TARGET is either the resolved target user name or * to match every target
user. NAME=VALUE assigns a value, while a bare NAME preserves that variable
from the invoking process environment. A bare variable is skipped when it is
missing or its value is not valid UTF-8. NAME= explicitly assigns an empty
value. Only one variable can be assigned or preserved per line. Blank lines
and lines whose first non-whitespace character is # are ignored.
For example:
# Applied to every target user
* EDITOR=/usr/bin/vim
* RUNAS_TARGET=${USER}
* DISPLAY
* SSH_AUTH_SOCK
# Applied only when running as root
root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
# Applied only when running as alice
alice CACHE_DIR=/var/cache/users/${UID}
alice TARGET_HOME=${HOME}
alice TARGET_GROUP=${GROUP}
alice TARGET_GID=${GID}
The following placeholders are replaced using the resolved target account:
| Placeholder | Replacement |
|---|---|
${USER} | Target user name |
${GROUP} | Target primary or selected group |
${HOME} | Target user's home directory |
${UID} | Numeric target user ID |
${GID} | Numeric target group ID |
Placeholder replacement is literal. The file is not interpreted by a shell, so quoting, command substitution, variable expansion other than the five placeholders above, and escape processing are not supported. Leading and trailing whitespace around names and values is removed; whitespace inside a value is retained.
Variable names must begin with an ASCII letter or underscore and may contain
only ASCII letters, digits, and underscores. Malformed lines and invalid names
are skipped. If a variable is assigned more than once, the first applicable
assignment wins. Values supplied with --env or imported with
--preserve-env take precedence over /etc/runas.env.
For security, /etc/runas.env must be owned by root and must not be writable
by its group or by other users. A typical setup is:
chown root:root /etc/runas.env
chmod 0644 /etc/runas.envIf the file cannot be read or has unsafe ownership or permissions, runas
prints a warning to standard error, ignores the file, and continues launching
the command. Malformed individual entries are skipped.
When compiled with the use_pam feature and using the native executor:
runaswill establish a PAM session after successful authentication.- PAM session modules may set environment variables or perform session initialization; those are pulled into the executed session.
PAM allows flexible integrations (including optional timestamping via PAM modules) without adding complexity to runas itself. If you need PAM-based session or timestamp behavior, configure it in your PAM stack.
NOTE: You need to add a PAM configuration file, e.g.
/etc/pam.d/runasin order for PAM authentication to behave correctly. These are very distro dependent and it's not possible or safe to make a universal one. You need one that is tailored to the way existing PAM configurations are setup on your specific OS installation.
- No
/etc/sudoers//etc/doas.confequivalent — there is no policy parser. - No timestamp-based password caching by default.
- No fine-grained environment protections:
runasdoes not attempt to replicatesudo's protected environment features. - Any user placed in "wheel" obtains full
runasprivileges.
An alias can be used to customise the command as a limited replacement for full configuration files.
alias runas='runas --preserve-env MYVAR'- Use on single-admin machines or homelabs where group membership is manageable.
- Keep the binary owned by root with strict file permissions:
chown root:root /usr/bin/runas chmod 4750 /usr/bin/runas
- Prefer PAM if you need richer authentication flows or session hooks; otherwise, the shadow-file mode is a compact default.