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
-
-d /home/weechat
This flag sets/home/weechat
as the home directory. This is where all of weechat's data (configuration, scripts, logs, etc) is kept. -
-s /sbin/nologin
This flag sets the user's login shell tonologin
and effectively prevents logging into the account unless it is done and permitted by doas(1). -
-m
This flag automatically creates the user's home directory. -
-v
This flag enables verbose mode. -
chmod u=rwx,go= /home/weechat
This command gives theweechat
user full read, write, and execute permissions over its home directory and prevents access for anyone else.
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
-
permit nopass main as weechat
This part of the rule allows yourmain
user to execute commands as theweechat
user without a password. It is convenient for quick access but asking for a password would add another layer of security (this can be done by omittingnopass
). -
cmd /usr/local/bin/weechat
This part of the rule specifies the full path to the weechat executable. The full path is used to prevent running an unintended executable that might be on your $PATH. -
args
By includingargs
on its owndoas
will refuse to execute/usr/local/bin/weechat
unless it is given exactly zero arguments. This could be further relaxed to allow only certain arguments, or omitted entirely to allow all arguments.
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