How to run X Applications as another User

I sandbox some applications on all systems I use and I run them under another user. I do this for multiple reasons:

Examples of applications I run as another user:

Getting Started

The following steps show how to do this on OpenBSD but it also works on other BSDs and Linux as well. On Linux you need to look up the correct iptables rules.

I use xodo to run X programs under another user. While I could do this by hand, xodo does all that stuff for me. And since I am lazy… Download xodo from GitHub and place it in the path of your choice, mine is under /usr/local/bin.

Important: The original upstream github repo of xodo has been deleted by its author. The link above points to my fork of the project. So if you find a bug, etc please do NOT bother the orginal author with it!

# ftp https://raw.githubusercontent.com/thexhr/xodo/master/xodo.sh
# mv xodo.sh /usr/local/bin/xodo
# chmod a+x /usr/local/bin/xodo

An alternative to xodo is SSH. Basically, you run a SSH server on localhost and do the following steps:

scp ~/.Xauthority firefox@127.0.0.1:
ssh firefox@127.0.0.1 -n "export DISPLAY=:0.0; TZ=UTC exec firefox"

Create a new User and Privilege Escalation Rules

xodo has a special option to add a new user to the system can creates the needed doas/sudo rules automatically.

# xodo --setup /usr/local/bin/firefox --as firefox --for joe
The following steps will be executed:
 - add user firefox if it doesn't exist;
 - make firefox's home readable and writable by its group;
 - add group firefox to user joe;
 - add an entry to /etc/doas.conf
   allowing existing user joe
   to execute /usr/local/bin/firefox
   as new user firefox with no password.
Proceed? [y/n] y
Done.
If user joe is logged in, it must log out and in again before using xodo as firefox, so that it gets added to the new group.

After this is done you have a new user on the system and a doas/sudo rule to run the specified command:

# cat /etc/doas.conf
permit nopass setenv { DISPLAY HOME=/home/firefox USER=firefox } joe as firefox cmd /usr/local/bin/firefox
# id firefox
uid=1000(firefox) gid=1000(firefox) groups=1000(firefox)

How to start the Application

Now I have the needed prerequisites to start Firefox as another user but how do I start it? The simplest solution would be a shell script/alias to run it. If you're running a current version of OpenBSD (> 6.3) Firefox is pledged and requires a running D-Bus session. Otherwise, it will be killed by pledge. Thus, I created a small shell script to set the session up in advance. You might not need it if you run a full-blown desktop environment.

$ cat /usr/local/bin/ffdbus
#!/bin/sh
/usr/local/bin/dbus-launch /usr/local/bin/firefox "$@"

Don't forget to replace firefox with ffdbus in the Exec line of the desktop file.

If you use a window manager that honors desktop files you can use the following.

$ cat ~/.local/share/applications/ Firefox\ Default.desktop
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Name=Firefox Default
GenericName=Web Browser
Comment=Browse the Web
Exec=/usr/local/bin/xodo firefox --as firefox %u
Icon=firefox
Terminal=false
Type=Application
MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;
StartupNotify=true
Categories=Network;WebBrowser;
Keywords=web;browser;internet;

Limit the Sandboxed Applications

The following section describes some tricks on how to limit the application's or the user's capabilities, respectively.

Limit the Application's Disk Space

Depending on your application you might want to limit the disk space the sandbox user might take up. Just to make sure that the application or a malicious user that took over the application cannot fill up your partition. You do use separate partitions on your systems, do you? I do this by just using disk quotas on the partition.

As you can see I allow 500MB as hard limit for my Firefox. Keep in mind that this is enough for most uses cases, however, not for large downloads. Just adjust the number to a value that suitable for your needs.

# quota firefox
Disk quotas for user firefox (uid 1001):
  Filesystem  KBytes    quota   limit   grace    files   quota   limit   grace
       /home  167252        0  500000            1462        0       0

Another way to restrict disk space is to have a seperate partition. On my newer machines, I have a dedicated partition for all sandboxed applications. Thus, the maximum disk space that can be occupied is 2.7G.

$ mount | grep browser
/dev/sd1n on /home/browser type ffs (local, noatime, nodev, nosuid, softdep)

$ df -h | grep browser
/dev/sd1n      2.7G    442M    2.1G    17%    /home/browser

Restrict Usage of System Services

Forensics of compromised Unix-like systems have shown that the attackers installed cronjobs to communicate with Command & Control servers, make sure the malware is still installed or simply launch programs of the attacker's choice. To deny the usage of cron and at add all non-permitted usernames line by line to /var/cron/cron.deny and /var/cron/at.deny. Mine looks as follows:

# cat /var/cron/cron.deny
firefox
iridium
torbrowser

Restrict Network Access

Sometimes it might be desirable to also block all outgoing network traffic for the user of the sandboxed application. The following example shows how to block all outgoing TCP and UDP traffic for the user sandbox with OpenBSD's pf. Add this line to the beginning of your /etc/pf.conf.

block out quick proto { tcp, udp } from self user sandbox

Note that only TCP and UDP can be firewalled by pf.

$Id: sandbox.md,v 1.6 2020/06/29 15:49:05 cvs Exp $