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.
- 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.
- 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.
- 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.
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.
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.