tree: b22ba7263af3631f692288861f77f0bcbf38ce8a [path history] [tgz]
  1. bpf_dsl/
  2. integration_tests/
  3. seccomp-bpf/
  4. seccomp-bpf-helpers/
  5. services/
  6. suid/
  7. syscall_broker/
  8. system_headers/
  9. tests/
  10. BUILD.gn
  11. DEPS
  12. OWNERS
  13. README.md
sandbox/linux/README.md

The Linux Sandbox

The Linux sandbox provides an API for restricting the capabilities of a process. The overall design philosophy of the sandbox is documented elsewhere; this document explains how it works on Linux.

Overall Design

There are several different sandboxing mechanisms available on Linux:

  • setuid(2)
  • namespaces
  • seccomp(2) BPF
  • seccomp(2) legacy
  • selinux(7)
  • apparmor(7)
  • landlock(7)

Chromium chooses which mechanisms to use based on which kernel features are available. We also generally use multiple layers of sandboxing, to achieve both confinement for the process and reduction of the exposed kernel attack surface. Of these mechanisms, Chrome uses:

  • setuid(2) everywhere
  • namespaces where supported (modern Linux kernels)
  • seccomp(2) BPF where supported (modern Linux kernels)

And we used to use, but no longer use:

  • selinux(7)
  • apparmor(7)

setuid(2)

The setuid(2) sandbox takes advantage of the fact that privileged processes on Linux are allowed to create new namespaces (see namespaces(7)) and sandboxes the renderer by creating empty namespaces for it at launch time. It relies on a setuid binary, usually installed at /opt/google/chrome/chrome-sandbox, which:

  • Enters new PID and network namespaces, preventing the sandboxed process from directly accessing the network or seeing any other processes.
  • chroot()s into a “safe” directory (currently inside the process's own /proc directory) by spawning a privileged helper process which shares its fs state (using CLONE_FS) and having that helper chroot() it, which leaves the process in an empty, readonly root directory.
  • Marks itself as un-dumpable using prctl(2), which prevents any process without CAP_SYS_PTRACE from tracing it. In theory this would keep renderers from debugging each other, but in practice they are isolated from each other by PID namespaces anyway.
  • Uses capset(2) to drop all inherited capabilities.
  • Drops from root back to the uid/gid/etc of the user running the browser

In general, the setuid sandbox makes an effort to apply all these mitigations, but support for them varies between kernel versions, so the strength of the setuid sandbox is variable, with newer kernels providing better security.

The setuid sandbox is implemented in suid/.

If you need to disable it, you can use --disable-setuid-sandbox. You should also see docs/linux/suid_sandbox_development.md for advice on developing the setuid sandbox itself.

seccomp(2) BPF

On modern Linuxes, we use the filter mode of seccomp(2), which allows us to supply a program (written in a domain-specific language called “BPF”, see bpf(2) and bpfc(1)) which is evaluated every time the sandboxed process makes a syscall to figure out whether the syscall should be allowed. The seccomp filters are compiled and applied “early” in the syscall process, so this both constrains what the process can do and reduces attack surface of the kernel.

The seccomp sandbox is implemented in seccomp-bpf/, and our tools for working with the BPF DSL are in bpf_dsl/. The actual baseline policies we use are in seccomp-bpf-helpers/.

Since the seccomp sandbox has a filter that is applied to all syscalls being made, to use it you must have an exhaustive list of syscalls that could be made by the code being sandboxed - both code you did write and code you didn't write. Generating that list of syscalls can be difficult and so it is helpful to have very good test coverage which runs under the sandbox to ensure you are exercising any code paths that could lead to syscalls.

landlock(7)

We currently don‘t use Landlock, but we’d like to: 345514921.