3

For security reasons I want to sandbox my browser and so I came up with the following wrapper script to run Firefox inside a bwrap sandbox:

$ cat ~/.local/bin/firefox
#!/bin/sh
MYDISPLAY="${DISPLAY##*:}"
MYDISPLAY="${MYDISPLAY%%.*}"
/usr/bin/bwrap \
    --unshare-all \
    --tmpfs /tmp \
    --bind ${HOME}/.cache/mozilla ${HOME}/.cache/mozilla \
    --bind ${HOME}/.mozilla ${HOME}/.mozilla \
    --bind ${HOME}/Downloads ${HOME}/Downloads \
    --bind /tmp/.X11-unix/X${MYDISPLAY} /tmp/.X11-unix/X${MYDISPLAY} \
    --ro-bind ${HOME}/.config/mimeapps.list ${HOME}/.config/mimeapps.list \
    --ro-bind ${HOME}/.local/share/fonts ${HOME}/.local/share/fonts \
    --ro-bind ${HOME}/.local/share/mime ${HOME}/.local/share/mime \
    --ro-bind /usr/bin /usr/bin \
    --ro-bind /usr/lib /usr/lib \
    --ro-bind /usr/lib64 /usr/lib64 \
    --ro-bind /usr/share /usr/share \
    --ro-bind /etc/alternatives /etc/alternatives \
    --ro-bind /etc/fonts /etc/fonts \
    --ro-bind /etc/resolv.conf /etc/resolv.conf \
    --ro-bind /etc/ssl /etc/ssl \
    --ro-bind /etc/ca-certificates /etc/ca-certificates \
    --ro-bind ${XDG_RUNTIME_DIR}/pulse ${XDG_RUNTIME_DIR}/pulse \
    --symlink usr/bin /bin \
    --symlink usr/lib /lib \
    --symlink usr/lib64 /lib64 \
    --dev /dev \
    --dev-bind /dev/dri /dev/dri \
    --proc /proc \
    --setenv HOME ${HOME} \
    --hostname RESTRICTED \
    --share-net \
    --die-with-parent \
    --new-session \
    -- \
    /usr/bin/firefox "${@}"

This works pretty well. However, if Firefox is already running and I call e.g. $ firefox "https://example.com" I get the following error:

Error message

It is clear to me why this happens: bwrap creates a second sandbox and tries to start Firefox a second time inside the new sandbox.

What I want to achieve: Add a tab to the already running Firefox inside the first sandbox.

Therefore I would have to run the $ firefox "https://example.com" command somehow inside the already existing sandbox. Is there a way to achieve this?

dirdi
  • 3,317

1 Answers1

3

Based on the fact that there is an official Firefox flatpak and Flatpak uses bwrap internally, there should be a way to get this to work. Note that the easiest path would probably be to just use Firefox via that flatpak, and maybe tweak its configuration.

That being said, I was curious and dug into the implementation details:

  • For security reasons, brwap can only create new namespaces, but not enter existing ones. The only way around this is to run some kind of job dispatcher in the sandbox that can receive the commands - which is basically what Firefox does, so let's see how that is implemented.

  • The remoting mechanism for notifying an already running instance uses DBus when running on Linux (see source code here and here). The sandbox for the new firefox process must therefore be able to talk to the sandbox of the existing firefox process via DBus. The official Firefox flatpak therefore explicitly grants access to the necessary DBus interfaces.

    brwap by itself only allows passing through the DBus socket as a whole, which is a security risk. Flatpak solves this by giving the main DBus Unix socket to a new xdg-dbus-proxy instance (which is running in yet another sandbox) which exposes a "filtered" DBus Unix socket - the latter of which is then actually given to the application sandbox.

Based on the Firefox flatpak and me poking around the internals of flatpak, the invocation of xdg-dbus-proxy should look something like this:

xdg-dbus-proxy \
    unix:path=/var/run/user/$UID/bus \
    /run/user/$UID/.dbus-proxy/session-bus-proxy-$RANDOMID \
    --filter \
    --own="org.mpris.MediaPlayer2.firefox.*" \
    --own="org.mozilla.firefox.*" \
    --own="org.mozilla.firefox_beta.*"

A few notes:

  1. Not tested yet.
  2. $RANDOMID should be an alphanumeric string that is unique between different invocations of your firefox script.
  3. The socket /run/user/$UID/.dbus-proxy/session-bus-proxy-$RANDOMID should be passed into your bwrap invocation using something like this:
    /usr/bin/bwrap ... \
      --ro-bind /run/user/$UID/.dbus-proxy/session-bus-proxy-$RANDOMID:/run/sandbox/bus \
      --setenv DBUS_SESSION_BUS_ADDRESS unix:path=/run/sandbox/bus
    
  4. The flatpak implementation does a bit more than that, like making sure that everything terminates together when the main process is closed.
cr7pt0gr4ph7
  • 141
  • 7