Skip to content

Commit 1f69ff7

Browse files
authored
Merge pull request #22315 from Homebrew/improve-linux-sandbox-checks
Improve Linux sandbox behaviour
2 parents d6527df + 378658d commit 1f69ff7

21 files changed

Lines changed: 605 additions & 148 deletions

File tree

.github/workflows/tests.yml

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,16 @@ jobs:
263263
if: matrix.name == 'tests (online)'
264264
uses: Homebrew/actions/cache-homebrew-prefix@main
265265
with:
266-
install: subversion curl
266+
install: bubblewrap curl subversion
267267
workflow-key: tests-tests-online
268268

269+
- name: Install brew tests Linux dependencies
270+
if: matrix.name == 'tests (Linux)'
271+
uses: Homebrew/actions/cache-homebrew-prefix@main
272+
with:
273+
install: bubblewrap
274+
workflow-key: tests-tests-linux
275+
269276
- name: Install brew tests macOS dependencies
270277
if: runner.os != 'Linux'
271278
uses: Homebrew/actions/cache-homebrew-prefix@main
@@ -274,21 +281,6 @@ jobs:
274281
workflow-key: tests-tests-macos
275282
uninstall: true
276283

277-
- name: Install brew tests Linux dependencies
278-
if: runner.os == 'Linux'
279-
run: |
280-
sudo apt-get update
281-
sudo apt-get install -y --no-install-recommends bubblewrap
282-
# Allow unprivileged user namespace cloning; rootless `bwrap` needs this
283-
# to create its user namespace.
284-
sudo sysctl -w kernel.unprivileged_userns_clone=1
285-
# Ensure the runner can allocate user namespaces instead of hitting a
286-
# per-user namespace limit.
287-
sudo sysctl -w user.max_user_namespaces=28633
288-
# Ubuntu runners may additionally restrict unprivileged user namespaces
289-
# through AppArmor; older kernels may not expose this sysctl.
290-
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
291-
292284
# brew tests doesn't like world writable directories
293285
- name: Cleanup permissions
294286
if: runner.os == 'Linux'

Library/Homebrew/build.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,11 +261,6 @@ def normalize_pod2man_outputs!(formula)
261261
ENV["HOMEBREW_INTERNAL_ALLOW_PACKAGES_FROM_PATHS"] = "1"
262262

263263
formula_path = ARGV.first
264-
# `build.rb` is handed a concrete formula file; keep reparsing from falling
265-
# back to the API inside the Linux sandbox.
266-
# TODO: remove this and fix the sandbox code instead.
267-
ENV["HOMEBREW_NO_INSTALL_FROM_API"] = "1" if ENV["HOMEBREW_SANDBOX_LINUX"] && formula_path&.end_with?(".rb")
268-
269264
args = Homebrew::Cmd::InstallCmd.new.args
270265
Context.current = args.context
271266

Library/Homebrew/env_config.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,10 @@ module EnvConfig
469469
"shadowed by other commands earlier on `$PATH`.",
470470
boolean: true,
471471
},
472+
HOMEBREW_NO_SANDBOX_LINUX: {
473+
description: "If set, disable the Linux sandbox.",
474+
boolean: true,
475+
},
472476
HOMEBREW_NO_UPDATE_REPORT_NEW: {
473477
description: "If set, `brew update` will not show the list of newly added formulae/casks.",
474478
boolean: true,
@@ -626,6 +630,7 @@ def env_method_name(env, hash)
626630
:HOMEBREW_CASK_OPTS_REQUIRE_SHA,
627631
:HOMEBREW_FORBID_PACKAGES_FROM_PATHS,
628632
:HOMEBREW_DOWNLOAD_CONCURRENCY,
633+
:HOMEBREW_SANDBOX_LINUX,
629634
:HOMEBREW_UPGRADE_AUTO_UPDATES_CASKS,
630635
:HOMEBREW_USE_INTERNAL_API,
631636
]).freeze, T::Set[Symbol])
@@ -769,6 +774,14 @@ def download_concurrency
769774
[concurrency, 1].max
770775
end
771776

777+
sig { returns(T::Boolean) }
778+
def sandbox_linux?
779+
return false if Homebrew::EnvConfig.no_sandbox_linux?
780+
781+
sandbox_linux = ENV.fetch("HOMEBREW_SANDBOX_LINUX", nil)
782+
sandbox_linux.present? && FALSY_VALUES.exclude?(sandbox_linux.downcase)
783+
end
784+
772785
sig { returns(T::Boolean) }
773786
def use_internal_api?
774787
return false if Homebrew::EnvConfig.no_install_from_api?

Library/Homebrew/extend/os/linux/dev-cmd/tests.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,20 @@ def os_files(files)
2626
sig { void }
2727
def check_test_environment!
2828
super
29-
return unless GitHub::Actions.env_set?
29+
return unless Homebrew::EnvConfig.sandbox_linux?
3030

3131
require "sandbox"
32-
with_env(HOMEBREW_SANDBOX_LINUX: "1") do
33-
return if ::Sandbox.available?
3432

35-
raise UsageError, "GitHub Actions Linux tests require a working rootless Bubblewrap sandbox."
33+
if GitHub::Actions.env_set?
34+
::Sandbox.configure!
35+
else
36+
::Sandbox.ensure_sandbox_installed!(install_from_tests: true)
3637
end
38+
return if ::Sandbox.available?
39+
40+
reason = ::Sandbox.failure_reason ||
41+
"`HOMEBREW_SANDBOX_LINUX` requires a working rootless Bubblewrap sandbox."
42+
raise UsageError, reason
3743
end
3844
end
3945
end

Library/Homebrew/extend/os/linux/diagnostic.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require "hardware"
77
require "os/linux/glibc"
88
require "os/linux/kernel"
9+
require "sandbox"
910

1011
module OS
1112
module Linux
@@ -25,6 +26,13 @@ def fatal_preinstall_checks
2526
].freeze
2627
end
2728

29+
sig { returns(T::Array[String]) }
30+
def fatal_build_from_source_checks
31+
(super + %w[
32+
check_linux_sandbox
33+
]).freeze
34+
end
35+
2836
sig { returns(T::Array[String]) }
2937
def supported_configuration_checks
3038
%w[
@@ -163,6 +171,48 @@ def check_kernel_minimum_version
163171
EOS
164172
end
165173

174+
sig { returns(T.nilable(String)) }
175+
def check_linux_sandbox
176+
return unless Homebrew::EnvConfig.sandbox_linux?
177+
178+
state = ::Sandbox.state
179+
return if [:disabled, :available].include?(state)
180+
181+
reason = ::Sandbox.failure_reason || "The Linux sandbox is not available."
182+
lines = case state
183+
when :missing
184+
[
185+
reason,
186+
"",
187+
"Install Bubblewrap and ensure a rootless `bwrap` executable is available on `PATH`.",
188+
]
189+
when :setuid
190+
[
191+
reason,
192+
"",
193+
"Homebrew's Linux sandbox requires a rootless `bwrap` executable.",
194+
"Install a non-setuid Bubblewrap or put it earlier on `PATH`.",
195+
]
196+
when :unavailable
197+
[
198+
reason,
199+
"",
200+
"Homebrew's Linux sandbox requires rootless Bubblewrap and unprivileged",
201+
"user namespaces. Check and update this system configuration:",
202+
*::Sandbox.configuration_command_messages,
203+
]
204+
else
205+
[reason]
206+
end
207+
208+
"#{[
209+
*lines,
210+
"",
211+
"As a final workaround, disable the Linux sandbox:",
212+
" export HOMEBREW_NO_SANDBOX_LINUX=1",
213+
].join("\n")}\n"
214+
end
215+
166216
sig { returns(T.nilable(String)) }
167217
def check_linuxbrew_core
168218
return unless Homebrew::EnvConfig.no_install_from_api?

0 commit comments

Comments
 (0)