jLuger.de - hustior

In my last posts I've written about the usage of Linux namespaces. I've took the lessons learned there and went one step further to create a sandbox called hustior.

What does it do?
hustior tries to hide all of your home directory except the folders you specify as the program arguments and it will hide all other processes.
If you call hustior with no arguments it will start a bash and you will be in your home directory. But your home directory will be empty. Don't worry your file won't be deleted. They are just not visible from this bash (and all their child processes).
When you call hustior with the argument "/home/<user>/path1/path2" it will be the same as above except that you have one directory in your home: "path2". "path2" will contain all the subdirectories and files that it also contains outside of the hustior started bash.
When stopping the hustior started bash all files saved in the home will disappear. Except they were stored in a mounted directory. Like e.g. "path2" from the example above.

When to use:
Use it when you want to get an extra line of defense before executing software or surfing on a site when everything should be OK. E.g. you start software like NodeJS, VisualCode, Eclipse, ... All this software is created by well known companies/organisations and used by millions of people. It should be safe to use. But one malicious/infected plugin and an attacker has access to all your mails/picutures/videos/documents. See this XKCD.
The idea of hustior is to allow programs like Eclipse only access to its binary and its workspace. There is no need to access my mails or my holiday pictures.

When not to use:
hustior is not battle hardened. Do not use it when you expect to get attacked. E.g. you execute a script/binary downloaded from a shady website.
In this case use a virtual machine on a spare PC that you nuke regularly.
Also don't call hustior as root. You should have absolutely no doubt about the software you execute as root.

How does it work:
Upon calling hustior restarts itself but in a new user, pid and mount namespace. The caller id is mapped to 0 and the current used information is added as a program parameter.
Then it creates a tmp filesystem where it mount bind several directories and files in it so that you get a complete root filesystem there.
All binaries are from your main system. That mean when you update your main system the binaries used by hustior are also up to date. This is unlike docker where you have to update each container separately.
Than it will create your home directory and mount bind all directories that were given as a program argument into it. No arguments, no directories in home.
All the mount binds are private. That means they aren't visible outside the process and its children. It means also that once hustior stops that they will be automatically unmounted.
The last step is to start a bash in a new user namespace. There the 0 id is mapped back to the id of the original caller.

Limits:
The temp filesystem used is only 200MB. Storing more in your home isn't possible. When you want to store larger files give a directory as an argument and store the files there.
The started programs have full network access and can even access ports opened by other processes.
The processes started by the hustior bash aren't isolated from attacks outside. E.g. an attacker made it to your machine and runs in your user context (not restricted by hustior) and you start a browser from the hustior bash. The attacker can see the browser.
Setuid won't work. As a normal user you can only map your own user id. Per default "an unmapped user ID is converted to the overflow user ID". So in the restarted hustior all files that belonged to root will belong to nobody (when nobody has the overflow user ID). I haven't found a way to fix this when starting the bash. So in the bash the setuid programs like ping or Chrome sandbox won't start as the don't belong to root.
Please also note that hustior is currently tested only on Ubuntu 17.10.

Lessons learned:
I've learned some lessons that aren't worth its own article.
E.g. I didn't know that you can mount bind files. Just create an empty file and you can mount bind the desired file to it.
When mount binding directories that have mounted file systems (e.g. you want to mount bind "/var" but there is a mount "/var/lib/docker/aufs") you need to use recursive as a parameter. At least when you've got root via user namespace mapping. That is a kind of security feature as else you may would see what is under e.g. "/var/lib/docker/aufs" before the real root mounted something there.

Alternatives:
subuser: Uses docker to isolate desktop programs. They are aware that docker allows easy access to root but I don't know if they fix the docker installation or only their usage of docker.
Bubblewrap: From their readme: "The goal of bubblewrap is to run an application in a sandbox, where it has restricted access to parts of the operating system or user data such as the home directory.". As far as I can tell is isn't an out of the box sandbox but the user has to provide the right parameters to get the sandbox he needs. Requires setuid root.
isolate: From their readme: "Isolate is a sandbox built to safely run untrusted executables, offering them a limited-access environment and preventing them from affecting the host system." Requires setuid root.
Qubes OS: Uses virtual machines to isolate workspaces. This is definitely more secure than hustior but also needs more resources.