Skip to content

doas-utils/doasudo

Repository files navigation

doasudo

A POSIX shell shim that translates sudo(8) invocations to doas(1), with full option coverage, POSIX-safe argument parsing, and security hardening. Drop it in as sudo on systems where doas is the privilege escalation tool; scripts that call sudo (mostly) work without modification.

Inspired by jirutka/doas-sudo-shim (if you don't need edit-mode support and want a minimal translation layer, consider this as an option).

Compatibility: Linux, FreeBSD, OpenBSD, NetBSD, DragonFly BSD, macOS


Supported options

sudo option Notes
-u USER passed through to doas
-n passed through to doas
-H sets HOME to the target user's passwd entry
-i login shell via target user's passwd entry
-s shell via $SHELL or invoking user's passwd entry
-e / sudoedit / editas edit mode for unprivileged invokers; see below
-k clears doas auth (doas -L); with a command, runs it afterward
-K clears doas auth (doas -L); no command or other options permitted
-l prints a "not supported" notice
-v validates via doas true (honors -n/-u); best-effort, fails closed
-E, -A, -S, -D, -R warned and ignored
-b, -g fatal; see sudo --help for rationale

SUDO_UID, SUDO_GID, SUDO_USER, SUDO_HOME, and SUDO_TTY are set for the target process. The shim provides no support for SUDO_COMMAND; programs in the shim call stack will see it unset.

doas.conf requirement

The shim requires a broad, non-cmd-scoped doas rule. For example:

permit :wheel

Restrictive cmd-scoped rules are not supported. For instance, granting edit mode under one requires adding unrestricted shell access; use doas directly with a narrowly scoped editor rule instead.


Edit mode

When invoked as sudo -e, sudoedit, or editas, the shim copies target files to a temporary directory owned by the invoking user, runs the editor unprivileged, then writes back any changed files as the privileged user.

Each file is processed in a separate editor session and written back independently, unlike real sudoedit(8), which opens all files at once. For the common single-file case the behavior is identical.

The editor is taken from $SUDO_EDITOR, $VISUAL, $EDITOR, or vi. It must be a single absolute path (no spaces, tabs, or flags). To pass options to the editor (e.g. vim -u NONE), use a wrapper script:

# /usr/local/bin/vim-sudoedit
#!/bin/sh
exec /usr/bin/vim -u NONE "$@"

Then set SUDO_EDITOR=/usr/local/bin/vim-sudoedit. See sudo --help.

Edit mode can be opted out at build time (see Installation).

editas

The shim installs editas alongside sudoedit. The name doasedit already exists in the wild, but editas mirrors doas naming better: edit as [user].

Restrictions

  • Symbolic links may not be edited.
  • Files in a user-writable directory may not be edited.
  • Device files may not be edited.
  • Edit mode may not be invoked by root.

Security model

Two attack families in edit mode are in scope. Symlink substitution: an attacker replaces a path component or the target with a symlink, so the privileged write-back lands on the wrong file. Temp-file substitution: the unprivileged working copy is replaced or modified during the edit session so unexpected content reaches the real target during privileged write-back. A set of mitigations address these; the full security model is documented in the SECURITY NOTE at the top of doasudo.in.

Optional (paranoid) edit-mode broker

The default edit mode does not prevent same-UID exposure for the lifetime of the editor session. An optional broker keeps the working copy and editor policy outside the invoking user's tree, and returns edited bytes through a framed protocol; privileged write-back is unchanged. Enable with DOASUDO_EDIT_BROKER=1; for installation and security details see: broker/README.md.

Optional diffs before saving changes

Setting DOASUDO_CONFIRM_DIFF=1 in the environment will show a unified diff and require confirmation before each write-back. Without an interactive TTY (or with -n), edit mode exits.


Installation

make                           # full test suite, then build shim (run as a normal user)
doas make install              # live prefix, default /usr/local
make install PREFIX=/usr       # custom PREFIX (still elevated if under system paths)
make install DESTDIR=/tmp/pkg  # staged install (no host post-install folded)

make install installs files only; it does not run the test suite (run make first). On a live install as root with empty DESTDIR, the Makefile tail-invokes post-install (broker user + staging chown). Otherwise run make post-install (or the shipped post-install.sh) after unpack / from %post, then merge doas-snippet.conf into /etc/doas.conf. See packaging/README.md.

To build a shim without edit-mode support, use make EDIT_MODE=0 and make EDIT_MODE=0 install. This omits sudoedit/editas, edit-mode code, and edit-broker artifacts; sudo -e still parses but exits with a feature-not-built error.

Uninstall

make uninstall

See packaging/README.md for what is removed and how removal is validated.


Testing

Main test entry points:

make check-src  # test shim and broker from source; skips the final rebuild step a full `make` does
make            # full test suite and shim build (run before privileged install)

For per-test details and docker images, see tests/README.md.

If the test suite cannot run in your environment, build with make doasudo and install files to match a normal make install layout (the shim expects shim-utils.sh under $(PREFIX)/libexec/doasudo/; with edit mode enabled, it also expects edit-broker-client.sh, the broker, and contracts beside it). When using DESTDIR for a staged install, run make (or make check-src) on a host similar to the deployment target first. See packaging/README.md.


Development

Design and architecture by p-zubieta. Parts of the codebase were written with the help of AI coding assistants. All changes were reviewed and tested by the maintainers.


License

MIT

About

A POSIX shell shim that translates sudo(8) invocations to doas(1)

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors