===== Opendoas =====
**Opendoas** is a [[https://en.wikipedia.org/wiki/Doas|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 [[en:philosophy:sudo_complexity|sudo]], known for being substantially smaller in size compared to it. The **tool** was originally written for [[https://www.openbsd.org|OpenBSD]] by Ted Unangst. [[https://wiki.gentoo.org/index.php?title=Doas|Opendoas]] is a portable version of OpenBSD's **doas** for GNUlinux.
==== Background ====
[[https://en.wikipedia.org/wiki/Unix|UNIX]] systems have **two classes of user**:
* the **super** user.
* and **regular** users.
**opendoas**, while still respecting this principals, don't give the [[https://en.wikipedia.org/wiki/Superuser|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 [[en:philosophy:sudo_complexity|sudo]] command, allows for [[https://en.wikipedia.org/wiki/Principle_of_least_privilege|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 [[https://www.hyperbola.info/packages/core/x86_64/opendoas/|opendoas]] just use:
pacman -S doas
dont forget to update your local package database before installing
===== Usage =====
The [[https://www.hyperbola.info/packages/core/x86_64/opendoas|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 =====
* See also [[en:philosophy:sudo_complexity|sudo]].
* See manpage [[https://man.openbsd.org/doas|doas(1)]] for more information.
* Check Opendoas author guide, [[https://flak.tedunangst.com/post/doas-mastery|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."