PolarSPARC

Bubblewrap - Pop Goes the Privilege!


Bhaskar S 05/22/2026


Overview


AI has dramatically accelerated the development and release of new tools - both beneficial and malicious - at an unprecedented pace. How do we evaluate these promising new tools safely, within a controlled, sandboxed environment ???

Well - one of the answers is the Bubblewrap sandboxing tool in Linux !!!

Bubblewrap is a low-privileged sandboxing tool used in production by many projects in Linux. Unlike heavy virtual machines or containers (Docker, Podman), Bubblewrap uses the following Linux kernel primitives directly:

Because Bubblewrap runs entirely in unprivileged user space, it is suitable for sandboxing untrusted code, AI agents, build systems, downloaded scripts, and developer tools that need internet access but should not touch your home directory.

Bubblewrap works by:

The critical insight is that Bubblewrap starts with a root filesystem (typically your current system root `/`) and then layers bind mounts on top. Everything NOT explicitly mounted or bound is invisible inside the sandbox.


Hands-on Bubblewrap


All the commands will be executed on a Ubuntu 24.04 LTS based Linux desktop.

To install Bubblewrap, execute the following command in a new terminal window:


$ sudo apt update; sudo apt install bubblewrap


Once the install succeeds, execute the following command in the terminal window:


$ bwrap --version


At the time of this article, the following was the output:


Output.1

bubblewrap 0.9.0

To list all the avialable command-line options, execute the following command in the terminal window:


$ bwrap --help


The following would be a typical output:


Output.2

usage: bwrap [OPTIONS...] [--] COMMAND [ARGS...]

  --help                       Print this help
  --version                    Print version
  --args FD                    Parse NUL-separated args from FD
  --argv0 VALUE                Set argv[0] to the value VALUE before running the program
  --unshare-all                Unshare every namespace we support by default
  --share-net                  Retain the network namespace (can only combine with --unshare-all)
  --unshare-user               Create new user namespace (may be automatically implied if not setuid)
  --unshare-user-try           Create new user namespace if possible else continue by skipping it
  --unshare-ipc                Create new ipc namespace
  --unshare-pid                Create new pid namespace
  --unshare-net                Create new network namespace
  --unshare-uts                Create new uts namespace
  --unshare-cgroup             Create new cgroup namespace
  --unshare-cgroup-try         Create new cgroup namespace if possible else continue by skipping it
  --userns FD                  Use this user namespace (cannot combine with --unshare-user)
  --userns2 FD                 After setup switch to this user namespace, only useful with --userns
  --disable-userns             Disable further use of user namespaces inside sandbox
  --assert-userns-disabled     Fail unless further use of user namespace inside sandbox is disabled
  --pidns FD                   Use this pid namespace (as parent namespace if using --unshare-pid)
  --uid UID                    Custom uid in the sandbox (requires --unshare-user or --userns)
  --gid GID                    Custom gid in the sandbox (requires --unshare-user or --userns)
  --hostname NAME              Custom hostname in the sandbox (requires --unshare-uts)
  --chdir DIR                  Change directory to DIR
  --clearenv                   Unset all environment variables
  --setenv VAR VALUE           Set an environment variable
  --unsetenv VAR               Unset an environment variable
  --lock-file DEST             Take a lock on DEST while sandbox is running
  --sync-fd FD                 Keep this fd open while sandbox is running
  --bind SRC DEST              Bind mount the host path SRC on DEST
  --bind-try SRC DEST          Equal to --bind but ignores non-existent SRC
  --dev-bind SRC DEST          Bind mount the host path SRC on DEST, allowing device access
  --dev-bind-try SRC DEST      Equal to --dev-bind but ignores non-existent SRC
  --ro-bind SRC DEST           Bind mount the host path SRC readonly on DEST
  --ro-bind-try SRC DEST       Equal to --ro-bind but ignores non-existent SRC
  --bind-fd FD DEST            Bind open directory or path fd on DEST
  --ro-bind-fd FD DEST         Bind open directory or path fd read-only on DEST
  --remount-ro DEST            Remount DEST as readonly; does not recursively remount
  --exec-label LABEL           Exec label for the sandbox
  --file-label LABEL           File label for temporary sandbox content
  --proc DEST                  Mount new procfs on DEST
  --dev DEST                   Mount new dev on DEST
  --tmpfs DEST                 Mount new tmpfs on DEST
  --mqueue DEST                Mount new mqueue on DEST
  --dir DEST                   Create dir at DEST
  --file FD DEST               Copy from FD to destination DEST
  --bind-data FD DEST          Copy from FD to file which is bind-mounted on DEST
  --ro-bind-data FD DEST       Copy from FD to file which is readonly bind-mounted on DEST
  --symlink SRC DEST           Create symlink at DEST with target SRC
  --seccomp FD                 Load and use seccomp rules from FD (not repeatable)
  --add-seccomp-fd FD          Load and use seccomp rules from FD (repeatable)
  --block-fd FD                Block on FD until some data to read is available
  --userns-block-fd FD         Block on FD until the user namespace is ready
  --info-fd FD                 Write information about the running container to FD
  --json-status-fd FD          Write container status to FD as multiple JSON documents
  --new-session                Create a new terminal session
  --die-with-parent            Kills with SIGKILL child process (COMMAND) when bwrap or bwrap's parent dies.
  --as-pid-1                   Do not install a reaper process with PID=1
  --cap-add CAP                Add cap CAP when running as privileged user
  --cap-drop CAP               Drop cap CAP when running as privileged user
  --perms OCTAL                Set permissions of next argument (--bind-data, --file, etc.)
  --size BYTES                 Set size of next argument (only for --tmpfs)
  --chmod OCTAL PATH           Change permissions of PATH (must already exist)

The following are some of the most important options for filesystem isolation:


Option Description
--bind SRC DEST Bind mount host path SRC to the sandbox path DEST in read-write mode
--ro-bind SRC DEST Bind mount host path SRC to the sandbox path DEST in read-only mode
--proc DEST Mount a new proc filesystem to the sandbox path DEST
--dev DEST Mount a new dev filesystem to the sandbox path DEST
--tmpfs DEST Mount a new tmp filesystem to the sandbox path DEST

The following are some of the most important options for managing the namespace isolation:


Option Description
--unshare-all Unshare all namespace types by default
--unshare-pid Create a new pid namespace
--unshare-uts Create a new uts namespace (hostname)
--unshare-ipc Create a new ipc namespace
--unshare-net Create a new network namespace
--unshare-user Create a new user namespace
--share-net Retain the network namespace from the host

The following are the other important options for managing the sandbox isolation:


Option Description
--die-with-parent Child proces dies if parent process terminates
--dir DEST Create a dir at the sandbox path DEST
--chdir DEST Change working directory inside the sandbox to the path DEST
--clearenv Unset all environment variables in the sandbox
--setenv VAR VALUE Set the environment variable VAR to the VALUE
--hostname NAME Custom hostname NAME in the sandbox (requires --unshare-uts)

Time to get our hands dirty with practical examples !!!

To verify that a sandbox environment has no access to the home directory on the host, execute the following command in the terminal window:


$ bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --proc /proc --dev /dev --unshare-all --die-with-parent -- ls /home


The following would be the typical output:


Output.3

ls: cannot access '/home': No such file or directory

To verify that a sandbox environment has no access to the host password file (/etc/passwd), execute the following command in the terminal window:


$ bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --proc /proc --dev /dev --tmpfs /tmp --unshare-all --die-with-parent -- cat /etc/passwd


The following would be the typical output:


Output.4

cat: /etc/passwd: No such file or directory

To create a bare minimum sandbox environment, execute the following command in the terminal window:


$ bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /bin /bin --proc /proc --dev /dev --tmpfs /tmp --tmpfs /run --uid 1000 --gid 1000 --dir /home/sandbox --chdir /home/sandbox --unshare-all --hostname sandbox --clearenv --setenv HOME /home/sandbox --setenv USER sandbox --die-with-parent -- bash --norc --noprofile


The following would be the typical output:


Output.5

bash-5.2$ 

The above is the prompt from the sandbox environment.

To check if the sandbox envrionment has access to the home directory (/home/polarsparc) on the host, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ ls /home/polarsparc


The following would be the typical output:


Output.6

ls: cannot access '/home/polarsparc': No such file or directory
bash-5.2$ 

To exit the sandbox envrionment, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ exit


To create a sandbox environment that has read-only access to the home directory (/home/polarsparc) on the host, execute the following command in the terminal window:


$ bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /bin /bin --ro-bind /home/polarsparc /home/polarsparc --proc /proc --dev /dev --tmpfs /tmp --tmpfs /run --dir /home/sandbox --chdir /home/sandbox --unshare-all --hostname sandbox --setenv HOME /home/sandbox --setenv USER sandbox --die-with-parent -- bash --norc --noprofile


The following would be the typical output:


Output.7

bash-5.2$ 

The above is the prompt from the sandbox environment.

To list the contents of the home directory (/home/polarsparc) on the host, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ ls /home/polarsparc


The following would be the typical output:


Output.8

Applications  Desktop  Documents  Downloads  Music  MyProjects  Pictures  Public  Templates  Videos
bash-5.2$

To create a test file (test.txt) in home directory (/home/polarsparc) on the host, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ echo 'text' > /home/polarsparc/Downloads/test.txt


The following would be the typical output:


Output.9

bash: /home/polarsparc/Downloads/test.txt: Read-only file system
bash-5.2$

To exit the sandbox envrionment, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ exit


To verify that a sandbox environment has no access to the network, execute the following command in the terminal window:


$ bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /bin /bin --proc /proc --dev /dev --tmpfs /tmp --unshare-all --die-with-parent -- curl https://httpbin.org/get


The following would be the typical output:


Output.10

curl: (6) Could not resolve host: httpbin.org

To create a sandbox environment with network access, execute the following command in the terminal window:


$ bwrap --ro-bind /usr /usr --ro-bind /etc/resolv.conf /etc/resolv.conf --ro-bind /etc/ssl /etc/ssl --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /bin /bin --proc /proc --dev /dev --tmpfs /tmp --unshare-all --share-net --die-with-parent -- curl https://httpbin.org/get


The following would be the typical output:


Output.11

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/8.5.0", 
    "X-Amzn-Trace-Id": "Root=1-6a0e44a9-39ce695f4f8656734153dcc1"
  }, 
  "origin": "173.70.1.174", 
  "url": "https://httpbin.org/get"
}

To execute a python script in a sandbox environment, execute the following command in the terminal window:


$ bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /bin /bin --proc /proc --dev /dev --tmpfs /tmp --unshare-all --die-with-parent -- python3 -c "print('hello from sandbox')"


The following would be a typical output:


Output.12

hello from sandbox

To create a sandbox environment with a persistent home directory (/home/sandbox), execute the following commands in the terminal window:


$ mkdir -p $HOME/.local/share/sandbox

bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /bin /bin --proc /proc --dev /dev --tmpfs /tmp --bind $HOME/.local/share/sandbox /home/sandbox --chdir /home/sandbox --unshare-all --hostname sandbox --setenv HOME /home/sandbox --setenv USER sandbox --die-with-parent -- bash


The following would be the typical output:


Output.13

bash-5.2$ 

To create a test file (test.txt) in the sandbox home directory (/home/sandbox), execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ echo 'text' > /home/sandbox/test.txt


The following would be the typical output:


Output.14

bash-5.2$

To verify if a sandbox environment has access to any nvidia gpu, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ nvidia-smi


The following would be the typical output:


Output.15

NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
bash-5.2$

To exit the sandbox envrionment, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ exit


To check if the test file (test.txt) is visible on the host, execute the following command in the terminal window:


$ ls -l $HOME/.local/share/sandbox


The following would be a typical output:


Output.16

total 4
-rw-rw-r-- 1 polarsparc polarsparc 5 May 22 14:40 text.txt

To create a sandbox environment with a persistent home directory (/home/sandbox) as well as access to the nvidia gpu, execute the following commands in the terminal window:


$ mkdir -p $HOME/.local/share/sandbox

bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /bin /bin --proc /proc --dev /dev --dev-bind /dev/nvidia0 /dev/nvidia0 --dev-bind /dev/nvidiactl /dev/nvidiactl --dev-bind /dev/nvidia-uvm /dev/nvidia-uvm --dev-bind /dev/nvidia-uvm-tools /dev/nvidia-uvm-tools --ro-bind /sys/bus/pci /sys/bus/pci --ro-bind /sys/class/drm /sys/class/drm --tmpfs /tmp --bind $HOME/.local/share/sandbox /home/sandbox --chdir /home/sandbox --unshare-all --hostname sandbox --setenv HOME /home/sandbox --setenv USER sandbox --die-with-parent -- bash


The following would be the typical output:


Output.17

bash-5.2$ 

To verify if a sandbox environment has access to any nvidia gpu, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ nvidia-smi


The following would be the typical output:


Output.18

Fri May 22 19:04:48 2026       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.159.03             Driver Version: 580.159.03     CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4060 Ti     Off |   00000000:04:00.0  On |                  N/A |
|  0%   45C    P8             11W /  165W |     911MiB /  16380MiB |     22%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+
bash-5.2$

To exit the sandbox envrionment, execute the following command from the sandbox prompt in the terminal window:


bash-5.2$ exit


BOOM - with this we conclude the hands-on demonstration of creating a sandbox environment using Bubblewrap !!!


References

Bubblewrap



© PolarSPARC