Firejail and Bubblewrap: Sandboxing Individual Applications

Firejail and Bubblewrap: Sandboxing Individual Applications

A program installed from your distribution’s repositories runs with your full user privileges. It can read your SSH keys, your browser profile, and every document you own.

That is normal on Linux and it is worth doing something about for software you do not fully trust.

Both tools here build on the same kernel features: namespaces, seccomp filters, and bind mounts, which is the same machinery containers use.

Firejail

Firejail ships hundreds of ready-made profiles.

sudo apt install firejail firejail-profiles

firejail firefox
firejail --list                       # what is currently sandboxed
ls /etc/firejail/ | head              # available profiles

Running firejail firefox applies /etc/firejail/firefox.profile, which restricts filesystem access, blocks device access, and applies a seccomp filter, all without configuration.

Applying it automatically:

sudo firecfg

That creates symlinks in /usr/local/bin so firefox invokes Firejail transparently. Convenient, and it means every application with a profile is sandboxed without you thinking about it.

Custom confinement

firejail --private=~/sandbox --net=none --nosound untrusted-binary
firejail \
  --noprofile \
  --private-tmp \
  --read-only=/ \
  --whitelist=~/Documents/work \
  --net=none \
  --nosound \
  --no3d \
  --caps.drop=all \
  --seccomp \
  suspicious-program

Useful options:

OptionEffect
--net=noneNo network at all
--privateEmpty temporary home directory
--private=DIRUse DIR as home
--whitelist=PATHOnly this path is visible
--blacklist=PATHHide this path
--read-only=PATHNo writes
--nosoundNo audio
--no3dNo GPU access
--caps.drop=allDrop all capabilities
--seccompFilter syscalls

Writing a profile:

# ~/.config/firejail/myapp.profile
include globals.local

noblacklist ~/.config/myapp

include disable-common.inc
include disable-devel.inc
include disable-programs.inc

caps.drop all
net none
nosound
no3d
seccomp
private-tmp

whitelist ~/Documents/myapp-data

The disable-*.inc includes are the useful part: disable-common.inc hides SSH keys, GPG keys, browser profiles, and credential files, which is the set that actually matters.

The setuid problem

ls -l /usr/bin/firejail
# -rwsr-xr-x 1 root root ... /usr/bin/firejail

That s means setuid root. Firejail runs with full root privileges whenever anyone invokes it, because creating namespaces has historically required privilege.

This makes Firejail itself attack surface. It has had multiple local privilege escalation vulnerabilities, where a flaw in argument or profile handling let a local user gain root through the tool meant to improve their security.

That is not a reason to avoid it outright, and it is a real consideration. You are adding a privileged binary to reduce the reach of unprivileged ones.

Remove the setuid bit if your kernel supports unprivileged user namespaces:

sudo chmod u-s /usr/bin/firejail

Some features stop working. It is the safer configuration where the remaining ones suffice.

bubblewrap

Minimal, unprivileged, no profiles.

sudo apt install bubblewrap

bwrap \
  --ro-bind /usr /usr \
  --ro-bind /lib /lib \
  --ro-bind /lib64 /lib64 \
  --ro-bind /etc/resolv.conf /etc/resolv.conf \
  --symlink usr/bin /bin \
  --proc /proc \
  --dev /dev \
  --tmpfs /tmp \
  --unshare-all \
  --die-with-parent \
  /bin/bash

You construct the environment explicitly. Nothing is visible unless you bind it, which is the opposite default from Firejail and is why it is harder to use and easier to reason about.

No setuid required. bubblewrap uses unprivileged user namespaces, so it holds no privilege you did not already have. This is its main advantage and the reason Flatpak uses it rather than Firejail.

If a bubblewrap sandbox is escaped, the attacker has your user’s privileges, which they already had. A Firejail escape can yield root.

--unshare-all        # every namespace
--unshare-net        # network only
--die-with-parent    # cleanup when the parent exits
--new-session        # prevent terminal injection attacks

--new-session matters more than it sounds. Without it, a process sharing your controlling terminal can inject keystrokes into it with TIOCSTI.

A reusable wrapper:

#!/usr/bin/env bash
# sandbox-run
exec bwrap \
  --ro-bind /usr /usr \
  --ro-bind /etc /etc \
  --symlink usr/bin /bin \
  --symlink usr/lib /lib \
  --symlink usr/lib64 /lib64 \
  --proc /proc \
  --dev /dev \
  --tmpfs /tmp \
  --bind "$PWD" /work \
  --chdir /work \
  --unshare-all \
  --share-net \
  --die-with-parent \
  --new-session \
  "$@"
./sandbox-run python3 untrusted_script.py

Current directory visible as /work, network available, nothing else reachable.

Which to use

Firejail for confining desktop applications quickly with good defaults. The profiles are maintained and cover most common software, and firecfg makes it automatic.

bubblewrap for building something specific, for scripts, and where you want no privileged component.

Flatpak for applications available that way, since it is bubblewrap with a permission model and a portal system on top. Our Flatpak permissions guide covers checking and tightening what an application can reach, and that is frequently the highest-value thing to do.

The three are not competing so much as layered. If an application is available as a Flatpak, confine it there. If it came from your distribution, Firejail or bubblewrap is what you have.

What a sandbox does not do

It does not prevent compromise. It limits what a compromised process can reach. Patching remains necessary.

It does not help under X11. Any application with X11 display access can read every other window’s input regardless of any sandbox, because that is how X11 works. This is one of the practical arguments for Wayland.

firejail --x11=xpra firefox     # partial mitigation, adds latency

Failures can be silent. A confined application may not see a file rather than reporting that it cannot. Test with a real workflow before assuming the confinement is both effective and non-breaking.

A sandbox with broad permissions confines nothing. A Firejail profile with home directory access protects nothing worth protecting, which is the same point the Flatpak filesystem=home problem illustrates.

The honest summary: sandboxing individual applications is worthwhile for anything you do not trust, it is not a substitute for updates or for good general practice, and the value comes entirely from the permissions being narrow.

Frequently Asked Questions

What is the difference between Firejail and bubblewrap?

Firejail is a complete sandboxing tool with hundreds of ready-made application profiles, installed setuid root so unprivileged users can create namespaces. bubblewrap is a minimal unprivileged tool that does one thing well and has no profile system, which is why Flatpak uses it as its foundation rather than using Firejail.

Why is Firejail being setuid root a concern?

A setuid root binary runs with full privileges regardless of who invokes it, so a bug in its argument handling can become a local privilege escalation. Firejail has had several such vulnerabilities, which is an uncomfortable property for a program whose purpose is improving security.

Do I still need these tools if I use Flatpak?

Usually not for Flatpak applications, since Flatpak already sandboxes them with bubblewrap. These tools matter for software installed from your distribution repositories or as a plain binary, which has no confinement at all by default.

Will sandboxing break my applications?

Sometimes, and the failure is often silent. A confined application may simply not see a file or a device rather than reporting an error, so test with the expected workflow before relying on the confinement. Firejail profiles are generally well tested, and custom rules need more care.

Can I sandbox a program without root?

Yes, with bubblewrap, provided your kernel allows unprivileged user namespaces. Some distributions restrict this for security reasons, and where they do, bubblewrap needs either the restriction lifted or its own setuid helper, which reintroduces the concern.

Is a sandbox a substitute for keeping software updated?

No. A sandbox limits what a compromised program can reach, and it does not prevent the compromise. Confinement and patching address different parts of the problem, and treating either as a replacement for the other leaves a real gap.