Roll your own bubblewrap sandboxing scripts in OCaml

I recently used a small bit of OCaml code to help generate bash scripts for sandboxing programs via bubblewrap and have found the suite to be fairly useful and saved a fair bit of typing.

The repo: https://github.com/darrenldl/sandboxing

Note: This is not a well packaged project right now, and has no versioning. I suggest forking the repo if you actually plan on using it. Might do proper packaging work at some point, however.

Syscall filter handling

Seccomp BPF compilation (for syscall filter) can also be handled via the suite in OCaml’s side. (If you’ve seen this project over at Discord before, then this part is the new addtion that was previously absent)

  • Context: right now bubblewrap only allows specification of syscall filtering via a compiled BPF file, this is handled by the suite as follows:
    OCaml code --generates–> C code --generates–> BPF filter

Firefox example

Some code for firefox is as follows

OCaml side

Firefox profile specification in bw-script-gen/src/profiles.ml

let make_firefox_profile ~(suffix : string option) : profile =
  let name = match suffix with None -> "firefox" | Some s -> "firefox-" ^ s in
  {
    name;
    cmd = "/usr/lib/firefox/firefox --ProfileManager";
    home_jail_dir = Some name;
    syscall_blacklist = default_syscall_blacklist;
    args =
      usr_share_common
      @ usr_lib_lib64_bin_common
      @ etc_common
      @ proc_dev_common
      @ tmp_run_common
      @ sound_common
      @ wayland_common
      @ dconf_common
      @ dbus_common
      @ set_up_jail_home ~tmp:false ~name
      @ [
        Bind ("$HOME/.mozilla", Some "/home/jail/.mozilla");
        Bind ("$HOME/.cache/mozilla", Some "/home/jail/.cache/mozilla");
        Unsetenv "DBUS_SESSION_BUS_ADDRESS";
        Setenv ("SHELL", "/bin/false");
        Setenv ("USER", "nobody");
        Setenv ("LOGNAME", "nobody");
        Setenv ("MOZ_ENABLE_WAYLAND", "1");
        Hostname "jail";
        Unshare_user;
        Unshare_pid;
        Unshare_uts;
        Unshare_ipc;
        Unshare_cgroup;
        New_session;
      ];
  }

C code

The generated C code for compiling the BPF file can be seen here: https://github.com/darrenldl/sandboxing/blob/master/seccomp-bpf/firefox.c

Bash script

The generated bash script:

#!/usr/bin/env bash

set -euxo pipefail

gcc "$(dirname $0)"/../seccomp-bpf/firefox.c -lseccomp -o "$(dirname $0)"/../seccomp-bpf/firefox.exe
"$(dirname $0)"/../seccomp-bpf/firefox.exe
mv firefox_seccomp_filter.bpf "$(dirname $0)"/../seccomp-bpf

mkdir -p "$HOME/jails/firefox"
mkdir -p "$HOME/jails/firefox/Downloads"

bwrap \
  --ro-bind "/usr/share/X11" "/usr/share/X11" \
  --ro-bind "/usr/share/icons" "/usr/share/icons" \
  --ro-bind-try "/usr/share/fontconfig" "/usr/share/fontconfig" \
  --ro-bind "/usr/share/fonts" "/usr/share/fonts" \
  --ro-bind "/usr/share/mime" "/usr/share/mime" \
  --ro-bind "/usr/share/ca-certificates" "/usr/share/ca-certificates" \
  --ro-bind "/usr/share/glib-2.0" "/usr/share/glib-2.0" \
  --ro-bind "/usr/lib" "/usr/lib" \
  --ro-bind "/usr/lib64" "/usr/lib64" \
  --tmpfs "/usr/lib/modules" \
  --tmpfs "/usr/lib/systemd" \
  --symlink "/usr/lib" "/lib" \
  --symlink "/usr/lib64" "/lib64" \
  --ro-bind "/usr/bin" "/usr/bin" \
  --symlink "/usr/bin" "/bin" \
  --symlink "/usr/bin" "/sbin" \
  --ro-bind "/etc/fonts" "/etc/fonts" \
  --ro-bind "/etc/machine-id" "/etc/machine-id" \
  --ro-bind "/etc/resolv.conf" "/etc/resolv.conf" \
  --proc "/proc" \
  --dev "/dev" \
  --tmpfs "/tmp" \
  --tmpfs "/run" \
  --ro-bind-try "/usr/share/gst-plugins-bad" "/usr/share/gst-plugins-bad" \
  --ro-bind-try "/usr/share/gst-plugins-base" "/usr/share/gst-plugins-base" \
  --ro-bind-try "/usr/share/gstreamer-1.0" "/usr/share/gstreamer-1.0" \
  --ro-bind "/run/user/$UID/pulse" "/run/user/$UID/pulse" \
  --ro-bind "/run/user/$UID/wayland-0" "/run/user/$UID/wayland-0" \
  --setenv "QT_QPA_PLATFORM" "wayland" \
  --bind "/run/user/$UID/dconf" "/run/user/$UID/dconf" \
  --ro-bind "/run/user/$UID/bus" "/run/user/$UID/bus" \
  --bind "$HOME/jails/firefox" "/home/jail" \
  --setenv "HOME" "/home/jail" \
  --bind "$HOME/.mozilla" "/home/jail/.mozilla" \
  --bind "$HOME/.cache/mozilla" "/home/jail/.cache/mozilla" \
  --unsetenv "DBUS_SESSION_BUS_ADDRESS" \
  --setenv "SHELL" "/bin/false" \
  --setenv "USER" "nobody" \
  --setenv "LOGNAME" "nobody" \
  --setenv "MOZ_ENABLE_WAYLAND" "1" \
  --hostname "jail" \
  --unshare-user \
  --unshare-pid \
  --unshare-uts \
  --unshare-ipc \
  --unshare-cgroup \
  --new-session \
  --seccomp 10 10<"$(dirname $0)"/../seccomp-bpf/firefox_seccomp_filter.bpf \
  /usr/lib/firefox/firefox --ProfileManager
2 Likes