This is the next in a series on changes I made when I upgraded from Debian squeeze to wheezy (see the first one). I decided to take to opportunity of the upgrade to do some better privilege separation on my system. The goal was to get the software that I don't think should have any access to the rest of my running X session, to run as its own user and be completely cut off from my X session. This had to happen in a way that doesn't change my work flow and browsing habits. I accomplished this using Xpra, and some handler scripts.

Why separate privileges?

This idea was described in a post on Jamie McClelland's blog conveniently titled Privilege Separation. He lays out the problem pretty well.

What are the biggest security threats to my laptop? Almost all the software I install is vetted and signed by a member of the Debian team. I run reliable virus software on our mail server. My disk is encrypted and xscreensaver locks my screen after a few minutes of inactivity. What else?

The two biggest threats I've recently considered are: web browsing and non-free software or software that doesn't come from Debian

Another threat is only introduced if you're not running a DM, but I've addressed how to handle that, so in general what I don't trust is web browsing and non-free software.

There are two pieces of non-free software that I run with any regularity; Flash plugin and Skype.

The Adobe Flash plugin is evil. Lots of people, famously including Apple, think Flash is a scourge on the Internet. I could write a whole post on why Flash needs to go away, but I'll leave it at three basic problems.

  1. Flash is essentially a trojan. That is, it allows web sites to run arbitrary code on your computer and access hardware devices, in certain circumstances, invisibly to you.
  2. Flash decodes video in software, rather than hardware. This is bad for any number of reasons. Apple complains that it runs the batteries out faster in their devices, in Linux you can watch it start hogging the CPU as soon as you load some video. It also tends to raise the temperature of my machine about 10°C. This is horribly inefficient, and I've heard amusing speculation about the aggregate wast of all that of the increased energy use.
  3. Flash isn't free. I don't know what's going on in there, and neither do you (unless you happen to work in the right part of Adobe). Enough said.

However, I enjoy being able to watch video and access other "enhanced content" from time to time. In Debian, there is the flashplugin-nonfree package, which downloads a copy of Flash from Adobe, and makes it available system wide to installed browsers. This is all you need to get flash working in Debian.

The second piece of non-free software I use is Skype. I use the binary package provided by Skype. Again, who knows what its doing? However, it has become an industry standard way to communicate in distributed work groups, and I gotta work — so i gotta use Skype.

Then there's the web. The browser presents all sorts of opportunities for mischief. The proliferation of JavaScript means that you are almost constantly leaking information about yourself to third parties. This is somewhat mitigated by using NoScript, but you lose out even more by blocking JavaScript than you do by blocking Flash. So if you run JavaScript, you don't want it anywhere near your other information.

Finally, there is Tor. I trust Tor, but I want my Tor Browser Bundle running in a different session, because that browser session will have no access to things in my X session that could leak my identity. It is another step towards better privacy.

Why I'm using Xpra

A comment at the end of Jamie's blog post suggested that his approach to privilege separation did not adequately protect one against an attacker injecting commands into the terminal, or listening for passwords in the X session (even though they would be doing so as a different user). The commenter suggested Xpra as a way to avoid access from the user running the untrusted software to the parent X session.

What does Xpra do? Its site has a reasonable description.

Xpra is 'screen for X': it allows you to run X programs, usually on a remote host, direct their display to your local machine, and then to disconnect from these programs and reconnect from the same or another machine, without losing any state. It gives you remote access to individual applications.

Xpra launches a separate X session, which in my setup is owned by a different user than my primary account. Xpra uses SSH for remote connections, but you can also connect locally if you launch the Xpra session properly.

In addition to the untrusted software not having access to my primary X session, an unexpected benefit of this setup is that I can restart my X session without having to restart Iceweasel, or other software running in other sessions. This has proven to be a bit of a time saver.

How I set it up

This setup is using Debian wheezy. I am going to skip the part where I install Iceweasel, Skype and Tor Browser Bundle.

Installing Xpra

The version of Xpra that is in Debian wheezy is already woefully out of date. There has been a ton of development on Xpra over the last couple of months. I have been using upstream binaries that are built for wheezy, and hosted on a repository maintained by the developers. The newer versions fix major bugs that were in the upstream issue tracker. I've been running them for a while now, and have found them to be good and getting better. Hopefully once wheezy goes stable, newer versions can be uploaded to backports. It looks like somone has already uploaded them to experimental.

The repository instructions specify apt-get install winswitch, but unless you actually want to run winswitch, you can just apt-get install xpra.

Users, groups and permissions

Remember that the goal here is to have a specific user run a specific piece of software with minimal hassle to me as I go about my day. So, I started by creating a few users, with home directories. I used the name of the software as the user name, so all of my scripts use those names.

If I were planning to use Xpra remotely, I would just give myself SSH access to that user and wouldn't need any special configuration. Since I am running this locally, lazy access takes a bit more work up front.

You can launch an Xpra session with the --mmap-group option, which allows users that are in the same group as the Xpra session's socket to attach to the Xpra session. I added myself to the iceweasel, skype, and torbrowser groups. I needed to restart my X session before the group change took effect at the level that Xpra needed.

The other changes are to /etc/sudoers, making two kinds of changes. One is which environment variables get passed from my session to that of the user I'm switching to via sudo. The other change lets my user run certain commands as another user without having to type in a password. The additions look like this for the environment variables:

Defaults env_keep += "MONKEYSPHERE_VALIDATION_AGENT_SOCKET"
Defaults env_keep += "GTK2_RC_FILES"

The GTK2_RC_FILES var is there mainly because I want the windows I use in Xpra to match the ones in my running session. I set that path in my scripts.

The MONKEYSPHERE_VALIDATION_AGENT_SOCKET is important because I actually want Monkeysphere's validation agent to use my primary user's gpg keyring when it goes to check my trust path to signed certificates.

The changes to /etc/sudoers look like:

nat  ALL=(iceweasel) NOPASSWD: /usr/bin/iceweasel
nat  ALL=(iceweasel) NOPASSWD: /usr/bin/xpra
nat  ALL=(skype) NOPASSWD: /usr/bin/skype
nat  ALL=(skype) NOPASSWD: /usr/bin/xpra
nat  ALL=(torbrowser) NOPASSWD: /usr/bin/xpra
nat  ALL=(torbrowser) NOPASSWD: /home/torbrowser/tbb/tor-browser_en-US/start-tor-browser

You can, of course, manually launch all of this without changing /etc/sudoers, but it makes it easier to use handler scripts and other tools of laziness if you do.

Getting started using Xpra

Using Xpra is pretty simple. First, I'll go through launching everything by hand; this makes the scripts easier to follow, and will give some idea as to how to extend this to other software you might use.

In a terminal, switch to another user.

0 nat@pigtown:~$ sudo su - iceweasel
[sudo] password for nat:
0 iceweasel@pigtown:~$

All of my scripts are using special socket directories for each Xpra session I attach to, so I start by creating that directory.

0 iceweasel@pigtown:~$ mkdir xpra-socket

Now launch an Xpra session.

0 iceweasel@pigtown:~$ xpra --mmap-group --socket-dir="/home/iceweasel/xpra-socket/" start :201

I've picked a high X session display number, just to avoid possible collisions with anything else that might start an X session. Also notice that I've used the --mmap-group and --socket-dir options, since I am not using the default Xpra socket, and allowing other users in the iceweasel group to attach to the session.

Next launch iceweasel.

0 iceweasel@pigtown:~$ MONKEYSPHERE_VALIDATION_AGENT_SOCKET=$MONKEYSPHERE_VALIDATION_AGENT_SOCKET DISPLAY=:201 iceweasel

If you don't use monkeysphere, you can remove those bits. Then as my primary user, I attach to the Xpra session with:

0 nat@pigtown:~$ xpra --socket-dir=/home/iceweasel/xpra-socket attach :201

That's basically it. At that point it is up and running.

Stopping the Xpra session is easy.

0 iceweasel@pigtown:~$ xpra --socket-dir="/home/iceweasel/xpra-socket/" stop :201

or, if you've modified /etc/sudoers.

0 nat@pigtown:~$ sudo -u iceweasel xpra --socket-dir=/home/iceweasel/xpra-socket stop :201

Handler scripts and configuration

As easy as that is, it is still a bit of a hassle, and how do you do things like pass URLs from the terminal to the browser if its in another X session? I ended up writing a few handler scripts that make the whole thing a lot easier to use day to day.

The first configuration steps are covered in the changes to /etc/sudoers above.

All of the scripts that I've written are kept in ~/bin/, though I suppose there are better places to keep this stuff. These scripts are very much a functional first pass, so there are likely better ways to do some of this stuff.

I decided to create a configuration file, so that it would be easier to tweak settings in the future.

# Settings for various xpra launchers.

##
# Shared settings
##

# set the GTK theme to use.
GTKRC="/usr/share/themes/Shiki-Dust/gtk-2.0/gtkrc"
# set the default encoding
# the default is jpg, but rgb24 works best for local use
ENCODING="rgb24"

XPRA=$(which xpra)
SUDO=$(which sudo)
HOSTNAME=$(cat /etc/hostname)


##
# Iceweasel settings
##

# Iceweasel user/group
#  -- the intendended end user must be in this group
#  -- the /etc/sudoers file needs a line like:
#     nat  ALL=(iceweasel) NOPASSWD: /usr/bin/iceweasel
ICE_USER="iceweasel"
ICE_GROUP="iceweasel"

# set the dir where the xpra socket will live for iceweasel
ICE_SOCKET_DIR="/home/iceweasel/xpra-socket"

# Set a default display number for the Iceweasel display;
# I set it high to avoid collisions with any other tools that
# might create X displays
ICE_DISPLAY_NUM=201
# set a custom icon for the systray. this will be used  of the default Xpra
# icon. This helps to differentiate which sessions is which when running
# multiple.
ICE_TRAY_ICON="/usr/local/share/icons/Smokikon_v09/scalable/categories/applications-internet.svg"
# path to iceweasel binary.
ICEWEASEL=$(which iceweasel)


##
# Skype settings
##

SKYPE_USER="skype"
SKYPE_GROUP="skype"
SKYPE_SOCKET_DIR="/home/skype/xpra-socket"

SKYPE_DISPLAY_NUM=202
SKYPE_TRAY_ICON="/usr/share/icons/skype.png"
SKYPE=$(which skype)

##
# Tor browser bundle
##

TBB_USER="torbrowser"
TBB_GROUP="torbrowser"
TBB_SOCKET_DIR="/home/torbrowser/xpra-socket"

TBB_DISPLAY_NUM=203
TBB_TRAY_ICON="/home/nat/graphics/icons/tor/tor.png"
TBB="/home/torbrowser/tbb/tor-browser_en-US/start-tor-browser"

There are two other general helper scripts. One that I use to attach to the various Xpra sessions, and the other to stop them.

the attach script

#!/bin/bash

# load in a bunch of variables used across the xpra scripts.
. /home/nat/bin/xpra-launchers.conf

# check which expected session we're connecting to.
if [ $1 == 'skype' ] || [ $1 == '-s' ] || [ $1 == '--skype' ]; then
  if [ -z $SKYPE_TRAY_ICON ]; then
    $XPRA --socket-dir=$SKYPE_SOCKET_DIR --encoding="$ENCODING" attach :$SKYPE_DISPLAY_NUM
  else
    $XPRA --socket-dir=$SKYPE_SOCKET_DIR --tray-icon="$SKYPE_TRAY_ICON" --encoding="$ENCODING" attach :$SKYPE_DISPLAY_NUM
  fi
  exit 0
elif [ $1 == 'iceweasel' ] || [ $1 == '-i'  ] || [ $1 == '--iceweasel' ]; then
  if [ -z $ICE_TRAY_ICON ]; then
    $XPRA --socket-dir=$ICE_SOCKET_DIR --encoding="$ENCODING" attach :$ICE_DISPLAY_NUM
  else
    $XPRA --socket-dir=$ICE_SOCKET_DIR --tray-icon="$ICE_TRAY_ICON" --encoding="$ENCODING" attach :$ICE_DISPLAY_NUM
  fi
  exit 0
elif [ $1 == 'tbb' ] || [ $1 == '-t'  ] || [ $1 == '--tbb' ] || [ $1 == '--torbrowser' ] || [ $1 == 'torbrowser' ]; then
  if [ -z $TBB_TRAY_ICON ]; then
    $XPRA --socket-dir=$TBB_SOCKET_DIR --encoding="$ENCODING" attach :$TBB_DISPLAY_NUM
  else
    $XPRA --socket-dir=$TBB_SOCKET_DIR --tray-icon="$TBB_TRAY_ICON" --encoding="$ENCODING" attach :$TBB_DISPLAY_NUM
  fi
  exit 0
else
  exit 1
fi

the Xpra session stop script

#!/bin/bash

# since neither quitting an program, nor disconnecting from an xpra session
# actually stops the underlying xpra session. that's actually fine most of the
# time, since all the launch scripts check if the socket exists.
#
# however, sometimes they need to close, and its a pain the do it from the cli.

# load in a bunch of variables used across the xpra scripts.
. /home/nat/bin/xpra-launchers.conf

# check which expected session we're stopping
if [ $1 == 'skype' ] || [ $1 == '-s' ] || [ $1 == '--skype' ]; then
  if [ -S $SKYPE_SOCKET_DIR/$HOSTNAME-$SKYPE_DISPLAY_NUM ]; then
    $XPRA --socket-dir=$SKYPE_SOCKET_DIR stop :$SKYPE_DISPLAY_NUM
    exit 0
  fi
elif [ $1 == 'iceweasel' ] || [ $1 == '-i'  ] || [ $1 == '--iceweasel' ]; then
  if [ -S $ICE_SOCKET_DIR/$HOSTNAME-$ICE_DISPLAY_NUM ]; then
    $XPRA --socket-dir=$ICE_SOCKET_DIR stop :$ICE_DISPLAY_NUM
  fi
  exit 0
elif [ $1 == 'tbb' ] || [ $1 == '-t'  ] || [ $1 == '--tbb' ] || [ $1 == '--torbrowser' ] || [ $1 == 'torbrowser' ]; then
  if [ -S $TBB_SOCKET_DIR/$HOSTNAME-$TBB_DISPLAY_NUM ]; then
    $XPRA --socket-dir=$TBB_SOCKET_DIR stop :$TBB_DISPLAY_NUM
    exit 0
  fi
else
  exit 1
fi

Iceweasel

Iceweasel has the most complicated start up script. I regularly use Iceweasel with multiple profiles, and am likely to pass it a wider variety of options than with Skype or TorBB, so I need to actually handle those arguments. This isn't fool proof, I only wrote it to be used by one fool; me.

I'm passing the MONKEYSPHERE_VALIDATION_AGENT_SOCKET variable, which makes sure that the monkeysphere xul extension will use the instance of the monkeysphere validation agent that is running in my X session (and not Iceweasel's).

#!/bin/bash
# load in a bunch of variables used across the xpra scripts.
. /home/nat/bin/xpra-launchers.conf

# launch the iceweasel browser
function launch_iceweasel() {
  USE_PROFILE=0
  USE_NOREMOTE=
  REMOTE=
  URL=
  NEWTAB=
  # handle just the profile and set no-attach
  for ARG in "$@"; do
    if [ $ARG == '-P' ]; then
      USE_PROFILE=1
    elif [ $USE_PROFILE -eq 1 ]; then
      PROFILE_NAME=$ARG
      USE_PROFILE=0
    elif [ $ARG == '--no-remote' ]; then
      REMOTE=$ARG
    elif [ $ARG == '-new-tab' ]; then
      NEWTAB=$ARG
    # there's probably some room for mischief here, as a well crafted URL
    # could get passed, provided it starts with http.
    # a risk i will live with, since it requires apriori access to my account
    # though the right way to fix is probably with a proper regex.
    elif ?span> http<span class="hl opt">* ; then
      URL=$ARG
    fi
  done
  if [ $PROFILE_NAME ]; then
    PROFILE="-P $PROFILE_NAME"
  else
    PROFILE="-ProfileManager"
  fi

  # now actually launch iceweasel
  $SUDO -u $ICE_USER MONKEYSPHERE_VALIDATION_AGENT_SOCKET=$MONKEYSPHERE_VALIDATION_AGENT_SOCKET DISPLAY=:$ICE_DISPLAY_NUM GTK2_RC_FILES="$GTKRC" $ICEWEASEL $PROFILE $REMOTE $NEWTAB $URL
}

# create a new xpra socket for iceweasel
function create_xpra_socket() {
  $SUDO -u $ICE_USER $XPRA --mmap-group --socket-dir="$ICE_SOCKET_DIR" start :$ICE_DISPLAY_NUM
}

# Here's the actual flow of the script

# check if there's already an iceweasel xpra socket open
# if so, launch iceweasel, if not create a socket and then launch.
if [ -S $ICE_SOCKET_DIR/$HOSTNAME-$ICE_DISPLAY_NUM ]; then
  launch_iceweasel $@
else
  # Since there isn't already an xpra socket open, set it up
  create_xpra_socket
  sleep 5
  launch_iceweasel $@
fi

Skype

The skype script is pretty straightforward.

#!/bin/bash

# load in a bunch of variables used across the xpra scripts.
. /home/nat/bin/xpra-launchers.conf

# function to create a new xpra socket for when there isn't one
function create_xpra_socket() {
  $SUDO -u $SKYPE_USER $XPRA --mmap-group --socket-dir="$SKYPE_SOCKET_DIR" start :$SKYPE_DISPLAY_NUM
}

function launch_skype() {
  $SUDO -u $SKYPE_USER DISPLAY=:$SKYPE_DISPLAY_NUM GTK2_RC_FILES="$GTKRC" $SKYPE
}


if [ -S $SKYPE_SOCKET_DIR/$HOSTNAME-$SKYPE_DISPLAY_NUM ]; then
  launch_skype
else
  # Since there isn't already an xpra socket open, set it up
  create_xpra_socket
  sleep 5
  launch_skype
fi

Tor Browser Bundle

And here is my launcher for the Tor Browser Bundle.

#!/bin/bash

# load in a bunch of variables used across the xpra scripts.
. /home/nat/bin/xpra-launchers.conf

# function to create a new xpra socket for when there isn't one
function create_xpra_socket() {
  $SUDO -u $TBB_USER $XPRA --mmap-group --socket-dir="$TBB_SOCKET_DIR" start :$TBB_DISPLAY_NUM
}

function launch_tor_browser_bundle() {
  $SUDO -u $TBB_USER DISPLAY=:$TBB_DISPLAY_NUM GTK2_RC_FILES="$GTKRC" $TBB
}

if [ -S $TBB_SOCKET_DIR/$HOSTNAME-$TBB_DISPLAY_NUM ]; then
  launch_tor_browser_bundle
else
  # Since there isn't already an xpra socket open, set it up
  create_xpra_socket
  sleep 5
  launch_tor_browser_bundle
fi

How I use it, and integrate it with Xfce

The web browser is one of those things I end up using all the time. Its always kinda just sitting there with sixty or so tabs. I'm constantly clicking on links from emails and IRC. I usually want those to open in my default profile.

Xfce has a dialog for "Preferred Applications" (in the Settings menu). I changed Web Browser setting to my Xpra launcher script. This handles everything coming from the terminal, and a bunch of other things in Xfce. The actual setting looks like iceweasel-xpra -new-tab "%s", which opens the URL in a new tab.

I generally launch sessions, and attach by calling the scripts from the Run dialog.

Bugs and Issues

These are my settings, and they work for me. You may want to change any number of things if you're doing this yourself.

I originally attempted to attach to the Xpra session from the various launcher scripts. I'm not sure why, but this never worked. My guess is you have to wait long enough for there to be a drawn X window to attach to, and if there isn't it just fails. Adding a long sleep to the script might be a reasonable workaround, but I thought it was easier to just use the attach script, which I needed anyway for when I disconnect and want to reconnect.

If you're having trouble with the scripts, run them in the terminal, and see if there are any errors. You can always double check your settings by running everything by hand.

Next steps

I would like a better way of setting the display numbers, so that they don't have to be hard coded in the configuration file. Ideally I might actually want profile a, and profile b running in different X sessions (and possibly different users). Collapsing all the scripts into one command might also be helpful.

Though, to me, it seems like the most obvious next step in usage is to run these pieces of software on their own virtual machines and use Xpra's ssh connection to attach to the software that way. This would mean a complete separation of privileges, and would mean that my system wouldn't need to have Skype, or Flash installed. However, that will have to wait until I get a bigger hard drive, and possibly rebuild my machine; so not a task I'm going to take on any time soon.

Thanks
Nice blog. This really makes me wonder if we should create some kind of package that could put all of this together and present an easy user interface for setting up. Given how seamless it is to run different programs as different users, and how useful it is to do that, I think we should be making it easier for people to do.
Comment by Anonymous
Thanks!

I hadn't read about xpra before this, and it might be exactly the tool I'm looking for. I have been following a Linux distro called Qubes OS for a couple of years and while I like the strong privilege separation it provides it's far from ready for prime time.

Fast forward to 2014, where tools like lxc, or Docker are readily available and networking utilities like Open vSwitch and I think we can really do some magic.

Comment by Anonymous
Doing this with Docker

Hi,

sorry to spam your comments, but for the past year or so I have been working on a project called subuser which does this but with Docker containers rather than separate users.

Comment by Anonymous