Repository navigation
Linux: use the CEF sandbox where it can work - #3
Merged
Merged
Conversation
Add browser-sandbox.{hpp,cpp} with DecideSandbox (a pure function over a
SandboxProbe: platform supported, root, user namespaces, state of
chrome-sandbox) and ProbeSandbox, both stubs for now, and
tests/sandbox_decision.py, which compiles the decision with
tests/sandbox-decision.cpp and runs every case:
- no probe for the platform: off (as today)
- root: off, also with namespaces or a setuid helper (Chromium exits the
process when it runs sandboxed as root)
- user namespaces: the namespace sandbox, whatever the helper state
- no user namespaces: the setuid helper when it is setuid root; off when
it is absent or not setuid (Chromium aborts on a helper that is not
setuid)
All 9 cases fail against the stub.
Refs obs-rust/obs-studio#1092
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DecideSandbox: off without a platform probe and as root; the namespace sandbox when user namespaces work (Chromium then ignores the helper); otherwise the setuid helper when chrome-sandbox is setuid root, and off when it is absent or not setuid. The reason goes to the log. tests/sandbox_decision.py: 9/9. Refs obs-rust/obs-studio#1092 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ProbeSandbox (Linux): - user namespaces: a forked child does what Chromium's namespace sandbox does: unshare user, PID and network namespaces, then map its own uid and gid. A sysctl alone misses the AppArmor restriction (Ubuntu 24.04+) and seccomp filters (systemd RestrictNamespaces). The child uses only async-signal-safe calls. Skipped as root. - chrome-sandbox: next to libcef, or at CHROME_DEVEL_SANDBOX; setuid root means a regular file owned by root, setuid, executable, not on a nosuid mount. Other platforms: unsupported (the decision is off, as today). tests/sandbox_probe.py prints the probe and the decision in the current environment. CT104 (Debian 13 LXC, CEF 7871 chrome-sandbox), 9/9 as measured on #1092: root off; user namespaces; no user namespaces: off, off (not setuid), setuid helper. Refs obs-rust/obs-studio#1092 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BrowserInit sets settings.no_sandbox from the sandbox decision instead
of always true. The probe looks for chrome-sandbox in the directory of
libcef (dladdr on cef_version_info). The decision and its reason go to
the log:
[obs-browser]: CEF sandbox on: user namespaces are available
[obs-browser]: CEF sandbox off: OBS runs as root
When no sandbox can work, obs-browser runs as before (no_sandbox), so
nobody loses their browser sources; the fallback policy (a UI notice, a
"require the sandbox" setting) and the .deb packaging wait for the design
decision on #1092. Windows and macOS have no probe yet and stay as they
were.
CT104 (Debian 13 LXC, CEF 7871, obs-studio master 9f2253334, Browser
Source with a JavaScript page, read back through GetSourceScreenshot):
- root: off, renders; every CEF process has --no-sandbox (as today)
- normal user: on, renders; renderers and one utility process have their
own user and PID namespace and seccomp-bpf
- normal user, RestrictNamespaces=~user, chrome-sandbox not setuid: off,
renders. With no_sandbox = false alone, OBS aborted here ("The SUID
sandbox helper binary was found, but is not configured correctly").
Refs obs-rust/obs-studio#1092
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 22, 2026
Add SandboxFallback (a stub for now) and its cases to tests/sandbox-decision.cpp. The fallback is B in obs-rust/obs-studio#1092: a probe says the sandbox cannot work, so the browser runs without it and the user is warned. Without a probe (Windows, macOS for now) the sandbox is off as before, with no warning. - fallback: root; no user namespaces and a chrome-sandbox that is not setuid root; no user namespaces and no chrome-sandbox - no fallback: no probe for the platform; user namespaces; a setuid root chrome-sandbox The 3 fallback cases fail against the stub. Refs obs-rust/obs-studio#1092 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SandboxFallback is true when a probe says the sandbox cannot work. The plugin then logs the reason at error level, as decided for B in obs-rust/obs-studio#1092: OBS echoes error lines to stderr on Linux and macOS, so the line is in the log and on stderr once. The browser source continues without the sandbox. Without a probe (Windows, macOS for now) the line stays at info level, as before. The UI warning with the "Accept anyway" countdown follows in a separate change. tests/sandbox_decision.py: 15 of 15 pass. Refs obs-rust/obs-studio#1092 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vpacher
marked this pull request as ready for review
October 24, 2026 20:49
This was referenced Oct 25, 2026
chriscoveries
pushed a commit
to chriscoveries/obsrust-fork
that referenced
this pull request
Oct 25, 2026
…s) (obs-rust#2378) Moves plugins/obs-browser from 7bab463c9 to 4583599fe, with: - obs-rust/obs-browser#3: the CEF sandbox where it can work on Linux (obs-rust#1092) - obs-rust/obs-browser#5: os_event_init results checked in module load and ExecuteOnBrowser - obs-rust/obs-browser#6: WebAudio resumes after the audio service dies (obs-rust#2328) - obs-rust/obs-browser#7: browser audio that arrives too late to mix is dropped (obs-rust#2335) - obs-rust/obs-browser#8: the fallback warning when the sandbox can't work (obs-rust#2218) One bump instead of three (obs-rust#2333, obs-rust#2351 and ours), as agreed on obs-rust#2351. docs/features.md gets the two audio lines from obs-rust#2333 and obs-rust#2351 as they are, a line for the sandbox, and the browser on the fallback warning line, which obs-rust#2238 merged on 2026-10-25. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step A of the Linux sandbox work in obs-rust/obs-studio#1092 (the pre-check before
CefInitialize), and the log part of B.B was decided on 2026-10-24 (obs-rust/obs-studio#1092 (comment)): when no sandbox can work, the browser runs without it, the reason is logged and goes to stderr, and the user gets a warning with an "Accept anyway" countdown. This PR does the log and stderr part. The warning dialog follows in separate PRs (a shared frontend helper in obs-studio, then its use here). The .deb packaging (C) is separate too.
Change
browser-sandbox.{hpp,cpp}:DecideSandbox, a pure function over a probe (platform supported, root, user namespaces, state ofchrome-sandbox), andProbeSandboxfor Linux:chrome-sandboxnext to libcef (or atCHROME_DEVEL_SANDBOX): setuid root means owned by root, setuid, executable, and not on a nosuid mount.BrowserInitsetssettings.no_sandboxfrom the decision, and logs the decision and the reason. When no sandbox can work, it runs as before (no_sandbox), so nobody loses their browser sources. Windows and macOS have no probe yet and are unchanged.chrome-sandboxsetuid rootchrome-sandboxabsent or not setuidTests
67f32cfRED:tests/sandbox_decision.py, 9 cases, all fail against a stub.48aa373GREEN: 9/9 (macOS and Linux).d9faaa1RED:SandboxFallback, 6 more cases intests/sandbox_decision.py; the 3 fallback cases (root; no user namespaces with a helper that is not setuid; no user namespaces and no helper) fail against a stub.87ba6a2GREEN: 15/15.Fallback log line, real module (obs-rust/obs-studio
d5960eac6, CEF 8078, Debian 13 LXC):[obs-browser]: CEF sandbox off: OBS runs as root. Browser sources run without the sandbox.at error level, so OBS echoes it once to stderr. Renders.CEF sandbox on: user namespaces are availableat info level, as before. Renders.tests/sandbox_probe.pyprints the probe and the decision in the current environment. Debian 13 LXC container, CEF 7871chrome-sandbox: root and every helper state gives off; a normal user and every helper state gives namespaces; a normal user withRestrictNamespaces=~usergives off, off (not setuid) and the setuid helper.Real module (obs-rust/obs-studio master
9f2253334, CEF 7871, Browser Source with a JavaScript page, read back throughGetSourceScreenshot):CEF sandbox off: OBS runs as root, renders, every CEF process has--no-sandbox(as today).CEF sandbox on: user namespaces are available, renders. The renderers and one utility process have their own user and PID namespace and seccomp-bpf.RestrictNamespaces=~user:CEF sandbox off: no user namespaces, and chrome-sandbox is not setuid root, renders. Withno_sandbox = falsealone, OBS aborted here.Ubuntu 24.04.5 VM (kernel 6.8.0-147,
kernel.apparmor_restrict_unprivileged_userns = 1, AppArmor on). The test host is a minimal CEF 7871 program:CefInitializewithno_sandbox = false, a separate subprocess binary as withobs-browser-page, and this probe in the main process. The probe agrees with Chromium in every case:chrome-sandboxpresent, not setuid (as the .deb installs it)chrome-sandboxno_sandbox = true(as today)usernson the subprocess binary onlyusernson the main binaryOn stock Ubuntu 24.04,
unshare(CLONE_NEWUSER)itself succeeds and only theuid_mapwrite fails, so the probe has to map the uid, as it does.For C on #1092: the user namespaces are created by the process that hosts CEF. An AppArmor profile therefore has to cover the OBS executable, not
obs-browser-page.Refs obs-rust/obs-studio#1092
CEF 8078 (2026-10-24)
Linux x86_64 now uses CEF 8078 (obs-rust/obs-studio#1948). I ran the same tests again. The code of this PR did not change.
d5960eac6, CEF156.0.8+g426920c+chromium-156.0.8078.48, this PR merged onto the obs-browser pin7bab463(no conflicts). Browser source with a JavaScript page, read back throughGetSourceScreenshot:CEF sandbox off: OBS runs as root, renders, every CEF process has--no-sandbox.CEF sandbox on: user namespaces are available, renders. The renderers and one utility process have their own user and PID namespace and seccomp-bpf.RestrictNamespaces=~user:CEF sandbox off: no user namespaces, and chrome-sandbox is not setuid root, renders.kernel.apparmor_restrict_unprivileged_userns = 1), minimal CEF 8078 host: the probe agrees with Chromium in all five cases, with the same results as the table above.The decision and the probe do not depend on the CEF version.
🤖 Generated with Claude Code