About

This post shows how to achieve light program isolation by running programs with a dedicated, unprivileged user account via the doas(1) utility. This is a simple, effective way to isolate a program from your "main" user account.

doas(1) from the OpenBSD project enables one user to execute a command as another user. Although ports exist for other platforms, they do not offer the same features or security guarantees and generally I do not recommend using them. On FreeBSD, mdo(1) is the recommended alternative. See an introduction to mdo(1) for more details.

Motivation

Sometimes it can be helpful to isolate a program from your "main" user account in case the program is compromised, at least then the damage can be minimized. An attacker would have to first break out of the dedicated user account, and achieve root or otherwise access your "main" user account.

It also becomes possible to apply certain controls and limits to a particular user account by assigning the user a login class – these might be limits you may not want your "main" user account to have.

It is important to note though that as far as isolation goes, this approach is thin. There are alternatives such as VMs, jails and containers that provide better isolation but at the same time, this approach also has its time and place.

User

Context

The first step is to create a new, unprivileged user account for the program you want to isolate. We'll use the IRC client weechat as our example because it is simple and straight forward. Let's create the user:

root@localhost# useradd \
  -d /home/weechat \
  -s /sbin/nologin \
  -m \
  -v \
  weechat

root@localhost# chmod u=rwx,go= /home/weechat

Explanation

Configuration

Context

In the next step we will configure doas(1) to allow our "main" user account execute the weechat program as the weechat user. This can be done through a configuration file that defines one or more rules, and each rule defines what user(s) can execute a command as another user. Let's get started by editing /etc/doas.conf and adding the following contents:

# /etc/doas.conf
permit nopass main as weechat cmd /usr/local/bin/weechat args

Explanation

Conclusion

Context

Before we execute doas(1) it can help to have a general idea of how it works. As a setuid binary owned by root, doas(1) will be run with an effective user id of 0 (root). When successful doas(1) will execute a system call that drops privileges to the weechat user, and in turn changes the real|effective user ID along with the real|effective group ID associated with a process. Finally, execvpe is called and a new process is started as the weechat user. That explanation is a slight over-simplication but it does accruately describe how tools like doas(1) generally work.

Demo

With the dedicated user created and doas.conf configured, we can now log in with the main user account and launch weechat as the weechat user.

# Launch weechat as the 'weechat' user
main@localhost$ doas -u weechat /usr/local/bin/weechat