Opendoas
Opendoas is a dedicated application subexecutor, this utility provides a way to perform commands as another user. It aims to be a simplified and a lightweight replacement for sudo, known for being substantially smaller in size compared to it. The tool was originally written for OpenBSD by Ted Unangst. Opendoas is a portable version of OpenBSD's doas for GNUlinux.
Background
UNIX systems have two classes of user:
- the super user.
- and regular users.
opendoas, while still respecting this principals, don't give the super user power all the time, think of the concentration of power and the responsibility that would entail. Given usually we only need super user powers to perform a single task. Doas still like sudo command, allows for subdivision of super user privileges, while taking a different approach, by keeping things simple and only granting them such power when doing specific tasks.
Installation
To install opendoas just use:
pacman -S doas
Usage
The doas command can be used like sudo:
To begin using /etc/doas as a non-privileged user, it must be properly configured. See Configuration.
To use doas, simply prefix the command with it and its related arguments with a space in between:
$ doas ''cmd''
For example, to use pacman:
$ doas pacman -Syu
To get to an interactive shell with root prompt:
$ doas -s
Configuration
The most important part is the configuration file. The doas tool is configured by the ruleset specified in /etc/doas.conf.
The general syntax form of its rules is:
permit|deny [options] identity [as target] [cmd command [args ...]]
For example, to allow members of the libre group to run smartctl without password as Root user.
Edit /etc/doas.conf:
permit nopass :libre as root cmd /usr/bin/smartctl
Basic setup
In config file, to allow members of group wheel to run commands as other users, create a configuration file with the following content:
permit :wheel
The owner and group for /etc/doas.conf should both be 0, file permissions should be set to 0400:
# chown -c root:root /etc/doas.conf # chmod -c 0400 /etc/doas.conf
To check /etc/doas.conf for syntax errors, run:
# doas -C /etc/doas.conf && echo "config ok" || echo "config error"
Rulesets
The doas tool allows the creation of rules which only apply to certain commands.
A rule can be specified to allow a certain user to use a command only available to root.
In /etc/doas.conf. Allow a user to use the reboot command without a password:
permit nopass larry cmd reboot
This allows the user larry to execute the reboot command without having to enter a password. This may allow users to use restricted commands without providing complete root access.
A simple skeleton configuration could be to specify a rule which allows all users in the wheel group to perform any action as root.
Allow all users in the wheel group to execute any command as root, in /etc/doas.conf:
permit :wheel
It's also possible to deny certain actions to specified users. The ruleset is evaluated in a hierarchical manner, thus adding a new rule can override the previous one.
Deny a user to execute a command, in /etc/doas.conf:
permit :wheel
deny larry cmd fdisk
The user larry is part of the wheel group and therefore may perform actions available to root, but the second rule denies this user access to the fdisk command.
doas.conf
The empty config doesn’t do anything, but allows us to discuss the default rule set. There is none. doas starts evaluating rules in the DENY state. If no rules match, then the action is denied. That’s right. Even root will receive a permission denied error. This is not particularly useful, per se, but one can be assured that reading the installed doas.conf file is sufficient to fully understand the system behavior.
If there’s no configuration file at all, doas will instead exit after printing a message that it is not enabled.
The simplest useful configuration probably looks more like this:
permit :wheel
This lets any user in the wheel group run any command as root. It’s approximately equivalent to the su command. For a more complete emulation, we’d like to allow root to run commands without a password:
permit :wheel permit nopass keepenv root
We add the root rule second because doas evaluates rules in a last match manner. root is in the wheel group, so the first rule will match, and then we need to override that with a second rule. Remember to always start with general rules, then make them more specific.
Why even run doas as root? Because sometimes you’d like to switch to a less privileged user. Or you have a script which uses doas to elevate privileges for an important operation, but you’re already root when you run it.
Passwords
The default behavior of doas is to require authentication every time the user runs a command. Normally this means entering their password. This can become tedious if multiple commands are executed. There are two keywords that can be added to a doas.conf rule to alter this requirement.
Adding the nopass keyword to a rule means the user is always permitted to run the command without entering a password. We’ve seen this above in the rule for root. The user is already root, so they can do anything they like and there’s no reason to require a password.
By adding the persist keyword, doas will remember that the user authenticated previously and not require further confirmation for a timeout of five minutes:
permit persist :wheel
This rule recreates the common sudo configuration of requiring a password for wheel users the first time a command is run.
If you have multiple shell logins to a machine, each login will require authentication. Additionally, the authentication information includes the parent shell process ID. This means that executing doas again in a shell script will require authentication. Or, to repeat that another way, if you run a script or program of uncertain quality, it won’t be able to silently elevate privileges. (That’s the theory anyway. In practice the check leaves quite some wiggle room.):
permit persist :wheel permit :wheel cmd reboot
Authentication checks are only skipped for rules marked persist. To prevent mistakes, important commands can be relisted without persist to always require a password.
The -L command line option to doas is analagous to logout and immediately deletes any persisted authentication information in this terminal without waiting for the timeout.
Environment
There are two basic ways information is provided to executed commands. The first and most obvious is the command line. The second and less visible way is via environment variables. Despite being mostly invisible, environment variables can have dramatic effects on a program’s behavior. Therefore it is important that doas provide some filtering to prevent unintended consequences. By default, only a short list of variables is copied.
There are two config keywords related to environment, keepenv and setenv. The first is very simple. We’ve seen it before, in the rule for root above. keepenv simply means that the entire environment should be retained and passed as is to the executed command. This is a convenient shortcut for trusted users.
Using setenv is a little more complicated, because it allows a combination of adding, modifying, and deleting environment variables. Let’s look at an example:
permit setenv { PKG_PATH ENV=/root/.kshrc PS1=$DOAS_PS1 } alice
This rule allows alice to run commands as root. The PKG_PATH environment is retained. The ENV environment, which points to a configuration file for ksh, is redirected to one owned by root. Finally, we override the PS1 setting which controls the shell’s prompt display. We don’t specify the new value in this configuration file, but instead use whatever value is in DOAS_PS1. This lets alice adjust the root prompt as desired.
Users and targets
Each rule in doas.conf applies to an individual or group, specified after permit and any options. There’s no keyword to distinguish between users and groups. Instead, syntax similar to chown is used, with user names by themselves or :group names with a leading colon. Now is a good time for a reminder that rules are always evaluated in order with the last matching rule winning. Specific user rules are not automatically preferred over group rules unless they come later in the file.
In most situations, doas is used to run commands as root. This requires no additional syntax. However, we may also wish to restrict some rules to targeting certain users:
permit nopass alice as dbadmin
This rule will let alice run commands as the database administrator without entering a password, but by itself doesn’t persist alice to run anything as root.
Commands
We’re nearing the end of our tour of the doas.conf syntax. doas can also be restricted so that rules only apply for certain commands, or even certain commands with particular arguments:
permit nopass :libre cmd reboot
Normally, reboot requires root privileges. It is instead executed indirectly by the setuid program shutdown, whose execution is restricted to the libre group. The above rule allows these users to run reboot directly. However, libre's won’t be able to run other commands as root or obtain a shell:
permit alice cmd sh args /etc/netstart
Here we allow alice to rerun the netstart script that configures network interfaces. We don’t give alice permission to run any shell command, only the netstart script.
In both of these examples, the cmd was specified with only the base name. In these cases, doas will restrict itself to only executing commands in the system PATH (/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin). alice won’t be able to install a sh binary in his home directory and set PATH to ~/bin to subvert our intentions. Absolute pathnames can also be specified, however the user will also be required to type them out in full.
Any command arguments specified must be specified in their entirety:
permit alice cmd ifconfig args iwm0 up permit alice cmd ifconfig args iwm0 down
These two rules will allow alice to turn the wifi interface on and off, but not change any of its other parameters.
Some userland utilities that gather information from the kernel only present a restricted subset of information when as regular users. To see the full information requires running as root. For example, fstat will only print minimal information about unix domain sockets:
bob Xorg 94581 16* unix stream 0x0 bob Xorg 94581 17* unix stream 0x0 bob Xorg 94581 18* unix stream 0x0 bob Xorg 94581 19* unix stream 0x0 bob Xorg 94581 20* unix stream 0x0
But when run again as root, we see much more information:
bob Xorg 94581 16* unix stream 0xffff800000b45980 <-> 0xffff800000b3d700 bob Xorg 94581 17* unix stream 0xffff800000b3d880 <-> 0xffff800000b45500 bob Xorg 94581 18* unix stream 0xffff800000b45480 <-> 0xffff800000b45300 bob Xorg 94581 19* unix stream 0xffff800000b45080 <-> 0xffff800000128e80 bob Xorg 94581 20* unix stream 0xffff800000b60280 <-> 0xffff800000b60780
bob xterm 33159 3* unix stream 0xffff800000b45300 <-> 0xffff800000b45480 bob Xorg 94581 18* unix stream 0xffff800000b45480 <-> 0xffff800000b45300
These kernel addresses are normally hidden because they reveal information about the kernel’s memory layout which can be used to facilitate exploits, but if we trust bob, then we can change this with a simple config rule:
permit nopass bob cmd fstat args -u bob
Using fstat, one can always see the open files of other user's processes, but we specify arguments here to prevent bob from matching up connections between processes.
deny
In contrast to all the permit rules we’ve seen so far, it’s also possible to create a deny rule that specifically denies command execution. This feature is most useful as a safeguard against accidental typos by trusted libre's. It should not be used as a security feature because an exhaustive blacklist is exhausting to create. Better to craft a ruleset that doesn’t grant unintentional privileges:
permit :wheel deny alice cmd reboot
Assuming that alice is in group wheel, we’ll let him run any command. Just not reboot. Maybe alice is a little trigger happy and has a habit of typing into the wrong terminal. This ruleset won’t actually prevent alice from rebooting the machine by any means; the first rule grants sufficient privilege to edit doas.conf and remove the second rule, among many other possibilities. The assumption is that everybody is on the same team.
doas
The doas command itself has a few options.
Since we just finished looking at the config file syntax, the -C option can be used to syntax check a new file before installing. It also permits checking the result of rule set evaluation for a given command and arguments without actually running the command. Continuing with the fstat example from earlier, we can check that only the command with arguments is matched:
$ doas -C doas.conf fstat deny $ doas -C doas.conf fstat -u bob permit nopass
When writing possibly noninteractive scripts that incorporate doas, the -n option is helpful to prevent future failures. Only nopass rules will successfully execute. Any rule that requires a password will instead immediately fail. This includes any rules with the persist keyword, regardless of whether the user recently authorized.
Authentication
Nopass feature
The nopass keyword provides the ability to perform actions without having to enter a password.
Edit /etc/doas.conf, to allow all users in the wheel group to perform actions as root without authentication:
permit nopass :wheel
Persist feature
doas provides the persist feature: after the user successfully authenticates. There will be presistance and an authenticated user, will be remember, and not be prompted or a password will not require be required confirmation for five minutes . It is disabled by default, enable it with the persist option.
Edit /etc/doas.conf, to not require passwords for five minutes for all users in the wheel:
permit persist :wheel
OpenDoas
port, and because it is new and potentially dangerous. In the original doas
, a kernel API is used to set and clear timeouts. This API is OpenBSD specific and no similar API is available on other operating systems. As a workaround, the persist feature is implemented using timestamp files similar to sudo
.
Testing
A configuration file can be tested as follows:
doas -C /etc/doas.conf
Specifying a command will show you whether you have permissions to perform this command:
doas -C /etc/doas.conf cat
This test will output deny if you do not have the permissions to execute cat.
You can also check permissions for a specified user:
doas -C /etc/doas.conf cat -u larry
If the user larry has permissions to access cat it may output permit.
Targets
The doas can not only be used to perform actions with root privileges, it also allows to target certain users and groups. The syntax to distinguish between groups (like wheel) and users (like larry) is a leading colon.
Edit /etc/doas.conf, to allow a user to perform actions as another user:
permit nopass larry as postgres
By adding this rule, the user larry is allowed to perform actions as the postgres user without having to enter a password.
Bash tab completion
By default Bash will only tab complete files and directories within the current or referenced directory. To tell Bash to complete arguments as if they were separate commands (also leveraging the tab completion settings of other commands) the following can be added to either the users .bashrc, or the global /etc/bash.bashrc:
complete -cf doas
Smooth transition sudo to doas
For a smooth transition from sudo
to doas
and to stay downward compatible, you could add to your environment:
alias sudo='doas' alias sudoedit='doas rnano'
Or alternatively, symlink doas
to where sudo
would normally be (but it does not provide sudoedit command):
# ln -s $(which doas) /usr/bin/sudo
sudo
preserves some environment variables while doas
does not, most notably XAUTHORITY, LANG and LC_ALL. This means you will not be able to start graphical applications under X nor to access the user's locale without further configuration. For instance, to allow members of the wheel group to run graphical applications and to access the user's locale using the setenv option.
Edit /etc/doas.conf:
permit setenv { XAUTHORITY LANG LC_ALL } :wheel
See also
- See also sudo.
- See manpage doas(1) for more information.
- Check Opendoas author guide, Doas Mastery.
External resources
“It would not have been possible to finish doas without the support of many other OpenBSD developers and users. In particular, Vadim Zhukov contributed immensely to the config parser and regress testsuite; Todd Miller, Damien Miller, and Martijn van Duren provided ideas and inspiration; Theo de Raadt provided backup to rejecting feature requests; Henning Brauer gave me the idea for tying authorization persistence to the terminal; and I owe Michael Lucas for stealing a catchy title.”