Sandboxing Dropbox
There was a recent rumor about Dropbox accessing all files of your machine, and while it turns out that it was probably not true this time, I remembered getting a sinking feeling when reading this and worrying about whether my files were safe. Dropbox is one of the few pieces of proprietary software that I run, and this story reminded me that I was just blindly trusting it to not act wrong, as I was not restricting its interaction with the rest of my data in any way.
I had to change this. Hence this post, which can probably also serve as a more general description of how to properly restrict any kind of untrusted or possibly insecure programs. I should point out that I am using a Debian system, so this may vary for other Linux distros.
I want to stress that this is post is not an endorsement of Dropbox, even though it may have the unintended effect of encouraging privacy-conscious users to run Dropbox because they can more easily tame it. Dropbox is software I'd really rather not run: it is a proprietary client, for a proprietary protocol, for a centralized cloud service, with no suitable privacy guarantees, it fosters network effects by rewarding users if they can attract more users, it encourages collaboration within the ecosystem and does not facilitate it across ecosystems, and forkability is low (you can have your files, but unlike, e.g., git, I'm not sure it's easy to export the history). The problem is that I need to collaborate with other people who use Dropbox, and I don't know what to suggest to them instead if they won't use a real VCS (I have complained before about this). If you're one of the few people whom I have to use Dropbox to work with, I invite you to feel guilty about all the work that I have to do for you. ;)
So this post describes how to sandbox Dropbox if you have to use it, but keep in mind that the right solution is not to use it altogether.
I took the most part from an
existing guide but
deviated from it in quite many respects, that I will point out. There is another more
paranoid guide about running
Dropbox in a chroot
, but this is a bit more heavy and the guide relies on
Gentoo-specific tools, so I preferred to stick with the simpler approach of
running Dropbox as its own user. (There would also be the even stronger approach
of running Dropbox in a virtual machine, e.g., using
KVM.)1
Of course, the crucial parts below are those where you run Dropbox as a separate
user with no access to your files or the X server, plus the necessary bindfs
trick to access its files as your real user. The rest is icing on the cake.
Preliminary precautions
First, of course, for what we are doing to make sense, you should make sure that the permissions of your own files with your real user are properly set up, so that the restricted dropbox user that we will create is indeed unable to access them. This probably includes setting up a suitable umask and going over your files to set the right permissions. (If you haven't done this before, you should be prepared for some problems later. For instance, when first updating this blog with my new umask, I temporarily broke it because the permissions of the compiled files were not right.)
Second, you should ensure that access to your X server is suitably restricted.
Verify that xhost
is returning only SI:localuser:youruser
, where youruser
is your user name. Otherwise, set it up. To do so automatically, I use the
following script when starting my X session (I wonder if there is a cleaner
way):
xhost +si:localuser:`whoami`
xhost | sed 1d | grep -v `whoami` | while read l; do
xhost "-$l"
done
Contrary to the other guide, we will not be setting up any form of access to the X server by the dropbox user. Indeed, from my attempts, Dropbox works fine on an entirely headless system. This simplifies things a bit.
It may the case that, no matter what xhost
says, local
connections to the X server are still allowed, so that the dropbox user will be
able to connect to it in the next step. If this happens, you need to
use xauth instead. This may already have been setup for you by your graphical
display manager; otherwise, you can rely on startx
to do it for you. However,
beware: xinit
will not do it for you.
Creating the user
We create a separate dropbox user to run Dropbox, with disabled password (i.e., password authentication is not permitted) and no login shell.
sudo adduser --disabled-password dropbox
sudo chsh -s /bin/false dropbox
Contrary to the
other guide,
everything Dropbox-related will run as that user
and live within that user's home directory. In particular, we will not be
installing Dropbox in /usr/bin
.
Now, open a shell as the dropbox user by issuing:
sudo su -s /bin/bash dropbox
Try to go around the home directory of your real user and check that your
precious files cannot be opened. Check that, even with the DISPLAY
variable
correctly set, you cannot run graphical applications (e.g., xeyes
).
Disk usage
I like the idea of restricting quotas so I recap it from the
other
guide.
Edit /etc/fstab
to add
usrjquota=aquota.user,jqfmt=vfsv0
as
mount options to the partition where the home of the dropbox user is (let's say
it's /home
), and do the following, adapting for your Dropbox quota (4GB here):
sudo apt-get install quota quotatool
sudo mount -o remount /home
sudo quotacheck -cavm
sudo quotaon /home
sudo quotatool -bu dropbox -l 4000MB /home
By contrast with the original guide, this is using journaled quotas, which are supposed to be more resilient; and I only set up user quotas because group quotas do not seem needed here.
Test the setup as the dropbox
user by issuing, e.g.:
dd if=/dev/zero of=test bs=1M count=5000
Check that this fails with a Disk quota exceeded
error. As for persisting
across reboots, with my config the quotas were automatically enforced at boot
(you can check this with quotaon -p /home
).
Memory usage
This section restricts the overall RAM usage of the dropbox user, so that the system would not malfunction if Dropbox started allocating too much memory. It generalizes to other restrictions that can be enforced using cgroups, e.g., IO and CPU priorities, etc., but I won't go into this here.
You need to install the cgroup-tools
package. Check if memory support is
enabled by issuing:
cat /proc/cgroups | grep memory | awk '{print $4}'
If this returns 0, as it seems to do by default in Debian, add
cgroup_enable=memory
to your GRUB_CMDLINE_LINUX
in /etc/default/grub
, run
sudo update-grub2
and reboot. (Yes, it sucks having to reboot, but I don't
know a different way.)
Now a confusing thing is that for cgroups to be created and tasks to be
allocated to the right cgroup, you have to edit /etc/cgconfig.conf
and
/etc/cgrules.conf
, but for this to do anything you need to arrange for
cgconfigparser
and cgrulesengd
to be started at boot time, and there is
apparently no init script to do it in Debian (see
/usr/share/doc/cgroup-tools/TODO.Debian
). As a crappy hack I added to
/etc/rc.local
:
cgconfigparser -l /etc/cgconfig.conf
cgrulesengd
Now we can setup the cgroup
for dropbox, by adding the following to
/etc/cgconfig.conf
to define the group (adjust 256 MB to what you think is
acceptable). (This is where additional restrictions (CPU shares, IO shares,
etc.) would go if you wanted to add them.)
group dropbox {
memory {
memory.limit_in_bytes = 256000000;
}
}
Now, add the following to /etc/cgrules.conf
to assign tasks by the dropbox
user to the right group. The first dropbox
is the user, the second is the
cgroup. (Note that memory
would need to be changed to *
if you added
different kinds of restrictions.)
dropbox memory dropbox/
Now reload the groups and tell the cgrulesengd
daemon to reload the rules
(assuming you have started that daemon, at boot or otherwise):
sudo cgconfigparser -l /etc/cgconfig.conf
sudo pkill -SIGUSR2 cgrulesengd
To check that this works, open a shell as the dropbox user. Check that the shell
is indeed in the dropbox
cgroup, by issuing:
cat /proc/$$/cgroup | grep dropbox
To control that being in that cgroup prevents you from allocating too much
memory, you can use the stress
program (packaged as stress
in Debian); note
that it will also use CPU. Check that you cannot allocate 500M of memory:
stress -m 1 --vm-keep --vm-bytes 500M
This should fail (the process should get a signal). You can check that the
memory limit is cumulative for all processes of the dropbox user: running
stress
to allocate 200 MB will work, but trying to run an additional such
process will fail.
Network access
Unlike the other guide, I will not try to restrict Dropbox traffic to use Tor. However the official documentation says that Dropbox is using only the HTTP and HTTPS ports, so I will prevent it from connecting to anything else (except DNS, of course). I don't think I need the "Open button" or "LAN sync" features mentioned in the doc, so I didn't allow them. We have to configure rules both for IPv4 and IPv6.
for cmd in iptables ip6tables
do
sudo $cmd -A OUTPUT -m owner --uid-owner dropbox -p tcp --dport 80 -j ACCEPT
sudo $cmd -A OUTPUT -m owner --uid-owner dropbox -p tcp --dport 443 -j ACCEPT
sudo $cmd -A OUTPUT -m owner --uid-owner dropbox -p udp --dport 53 -j ACCEPT
sudo $cmd -A OUTPUT -m owner --uid-owner dropbox -j REJECT
done
You can make those rules persistent by installing the iptables-persistent
package in Debian and saying that you want to save the current rules.
Installing Dropbox
Do as follows, as the dropbox user of course:
# required because Dropbox tries to access the display and crashes otherwise
export DISPLAY=""
cd
wget -O dropbox.py "https://linux.dropbox.com/packages/dropbox.py"
chmod 755 dropbox.py
./dropbox.py start -i
# required because you won't get the auth URL if the daemon is backgrounded
./dropbox.py stop
~/.dropbox-dist/dropboxd start
Say yes when asked to download the proprietary daemon. You should obtain an
authentication URL. Open it in a web browser and authenticate. Then it should
magically work and your files should synchronize in ~dropbox/Dropbox
. You can
^C
the daemon and run ./dropbox.py start
to start it in background.
Whenever you need to interact directly with the Dropbox daemon as your real user, do, e.g.:
sudo su dropbox -s /bin/bash -c 'DISPLAY="" ~/dropbox.py status'
There's just one slight glitch left: how to access your Dropbox files as your real user?
Accessing the files
The
original guide
uses SMB as a hack to mount the Dropbox folder of the dropbox user to access it
as your real user. I use a dedicated tool instead, bindfs
, which I feel is
slightly less hacky and more flexible, even though the resulting invocation is quite verbose.
The following should be run as your regular user, not the dropbox user.
sudo apt-get install bindfs
sudo mkdir -p ~/mnt/dropbox
sudo bindfs --create-for-user=$(id -u dropbox) \
--create-for-group=$(id -g dropbox) \
--create-with-perms='f-x' \
--chown-deny --chgrp-deny \
--chmod-filter='of-x,gf-x,uf-x' -p 'f-x' \
-u $(id -u) -g $(id -g) \
~dropbox/Dropbox ~/mnt/dropbox
This means that you can interact with your Dropbox files in ~/mnt/dropbox
,
with all existing files and new files appearing to be owned by your real user
and its group, but they are actually owned by dropbox and its group in the home
of the dropbox user. All chmod
and chown
operations in the mount from your
real user will fail (which is right, as they make no sense), and files will
never appear there as executable nor can be set to be executable (this
approximates the noexec
option and makes it harder to execute code from the
Dropbox files, which is useful if you're paranoid about Dropbox smuggling
harmful executables there).
I use a little script to pass commands
to Dropbox and do the bindfs
invocation if necessary, checking beforehand that
Dropbox is suitably restricted. Again, the script should be run as your regular
user, not the dropbox user. I think those tests are important: as the system
is insecure by default, you want to test explicitly if the restrictions are
still there; otherwise, if they break and Dropbox can start doing unpleasant
things like connect to your X server, you will never find out about it (except
the hard way).
Limitations
In comparison with the
original guide
I am not covering the use of AppArmor to restrict the dropbox program. (I don't
feel this is that useful as my setup installs Dropbox locally to the dropbox
user and not as /usr/bin
, although it would be nice, e.g., to limit Dropbox's
ability to investigate /proc
.) I do not cover the question of starting Dropbox
automatically (I run it manually and stop it when I'm done), only the question
of maintaining the restrictions across boots.
I do not cover the question of encrypting files in Dropbox (as my use case is to collaborate with people who would not use encryption). I do not cover the question of having a GUI, status icons, nautilus plugins, etc. (I do not use those.)
Concluding remarks
All of this makes the installation of Dropbox on a new machine a bit more tedious. However, it's important to understand that the privacy-conscious user has no alternative: the Dropbox client is a proprietary program that they have no reason to trust, especially given that Dropbox is US-based, and the US have been known for massive privacy violations. Dropbox is a tool designed to upload your data to untrusted third parties, so it is very important that your computer enforces the necessary checks to ensure that it shares only the data that you want it to share.
There is a general principle at play here: whereas the general public is happy to install random apps and toys with little regard to which harm they could cause, the privacy-conscious user will always need more effort to integrate anything new into their computing infrastructure, because it needs to be done in a controlled and secure way.
-
In all of this, of course, with contrast to the only "really secure" approach of running Dropbox on a physically separate machine, you are relying on the kernel to implement access control correctly. In particular, if you don't patch the occasional privilege escalation vulnerabilities faster than a malicious Dropbox could exploit them, then you can be screwed. ↩