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
dont forget to update your local package database before installing

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.

By using an empty configuration file the default ruleset will be applied which denies all actions.

The general syntax form of its rules is:

permit|deny [options] identity [as target] [cmd command [args ...]]
The configuration file must end with a newline

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" 
It is imperative that /etc/doas.conf is free of syntax errors!

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.

The authentication information doas uses is recorded in the kernel and attached to the current session. Unlike filesystem tickets, it is not accessible to other users and difficult to fake. The timeout will always take place in real time, not computer time, meaning that adjusting the system clock backwards can not grant new life to an expired ticket. Repeated executions will reset the timeout, but only if the rule is marked persist. Rules cannot be both persist and nopass, nor will nopass rules extend the timeout.

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
This allows us to match these sockets up with the process on the other end:
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
Due to OpenBSD-specific kernel API required by doas to set and clear timeouts, the persist feature is disabled by default in the 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
By default 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

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.”