Pages: Welcome | Projects

Sandboxed firefox

2019/5/31
Tags: [ GNU/Linux ] [ security ] [ SELinux ]

As I mentioned yesterday, I'd like to harden what I perceive as the major security weakness of my set-up: the web browser.

Inspired by this discussion I had on lobste.rs, I'm using Dan Walsh's sandbox, which in short allows to run the cmd application within a tightly confined SELinux domain. (just quoting the man page).

This tool supports a protected X11 environment, allows to mount separated $HOME and /tmp, and allows to select a SELinux Security Context to apply to the sandboxed program. It even comes with a set of pre-compiled sandbox types matching some common needs.

A one-liner for Firefox follows, see sandbox(8) for explanations:

$ sandbox -X -M -H /tmp/sehome/ -T /tmp/setemp/ -t sandbox_web_t firefox

A couple of gotchas:

1. The need for more policies

Sandbox is a generic tool. It comes with policies, but I'm guessing it's not easy to predict what software the user is going to confine with it.

Sure enough, starting Firefox in this way ends up in a ton of AVC denials. It is not even necessary to visit some evil site for this to happen: Firefox will reasonably try reading system configurations (e.g. /etc/dconf) at startup. It is actually quite reasonable.

Those AVC denials will however end up flooding the system. Besides the useless noise, this can make the system unusable, as it ends up into notifications in the graphical environment (at least, on Fedora).

Lurking around I figured that the solution is surprisingly simple:

  1. Track those audit messages which involve the process (in this case Firefox)

    root# ausearch -c firefox
    

    Note: this command appears to be simply a nice to use filter on /var/log/audit/audit.log. But maybe has some fancy extra feature, I should lurk more.

  2. Turn those messages into a policy definition with the audit2allow tool.

    root# ausearch -c firefox -ts recent --raw |
        audit2allow -D -m firefox_sandbox > firefox_sandbox.ts
    

    Note: This tool will write "allow" policies by default, but the -D flag specifies we want to keep denying those actions. The goal is in fact just to avoid the unnecessary auditing. I keep denying all those permissions, however reasonable, that are not strictly necessary for Firefox to operate.

  3. Compile the obtained policy into a binary modular policy package, as by the example in the audit2allow manual page

    root# checkmodule -M -m -o firefox_sandbox.mod firefox_sandbox.te
    root# semodule_package -o firefox_sandbox.pp -m firefox_sandbox.mod
    root# semodule -i firefox_sandbox.pp
    

    Or simply rely on audit2allow to do the compilation directly:

    root# ausearch ... | audit2allow -D -M firefox_sandbox
    

    Note: The .te is in textual format: it can be modified with a ed(1)-itor (or a vi(1)-itor, or an emacs(1)-itor for what it matters).

  4. Load the resulting policy in the kernel:

    root# semodule -i firefox_sandbox.pp
    

When this is done, the one-liner will no longer result in the auditing system being spammed, at least not for the same reasons.

I believe the obtained policy might use some iterative refinement, up to the sweet spot in which only anomalous activity is detected (with a few or no false positives). It could be necessary to relax the policy and allow for some strictly required operation. I would not be surprised to learn that the obtained policy can be improved manually!

2. No copy paste from/to the sandboxed browser

Deal with it. The browser is in a confined environment, and can't easily communicate with the rest of the operating system.

Or maybe not? Maybe there's a way. Probably the policy can be refined to allow for copy-paste buffers. I need to lurk more.

EDIT it is really trivial and supported out of the box. Look here. In short, the mounted user home will have a file named seremote containing the display to use.

Use case: pass(1) will easily work:

$ more /tmp/sehome/seremote
#!/bin/sh
DISPLAY=:1 "$@"
$ /tmp/sehome/seremote pass show -c stuff/bank

3. Reboots

I'll have to figure out how to make the policy resilient to reboots. For example, iptables rules and sysctl settings must be restored upon reboot. But perhaps SELinux works differently.

EDIT: policy is maintained after reboot.

3. I need to lurk more

Generally true. This seems the tip of the iceberg, really :)