Class: Sandbox::Bubblewrap

Inherits:
LinuxBackend show all
Extended by:
SystemCommand::Mixin, Utils::Output::Mixin
Defined in:
extend/os/linux/sandbox/bubblewrap.rb

Defined Under Namespace

Classes: self

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SystemCommand::Mixin

system_command, system_command!

Methods included from Utils::Output::Mixin

issue_reporting_message, odebug, odeprecated, odie, odisabled, ofail, oh1, oh1_title, ohai, ohai_title, onoe, opoo, opoo_outside_github_actions, opoo_without_github_actions_annotation, pretty_deprecated, pretty_disabled, pretty_duration, pretty_install_status, pretty_installed, pretty_uninstalled, pretty_unmarked, pretty_upgradable, pretty_warning

Methods inherited from LinuxBackend

full_write_isolation?, #writable_paths

Constructor Details

#initialize(profile) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • profile (SandboxProfile)


290
291
292
293
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 290

def initialize(profile)
  super
  @masked_read_paths = T.let([], T::Array[::Pathname])
end

Class Method Details

.available?Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


156
157
158
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 156

def available?
  state == :available
end

.configuration_command_messagesArray<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



196
197
198
199
200
201
202
203
204
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 196

def configuration_command_messages
  commands = configuration_commands
  SYSCTL_SETTINGS.each_with_index.flat_map do |setting, index|
    [
      "  #{commands.fetch(index)}",
      *setting.description.map { |line| "    #{line}" },
    ]
  end
end

.configuration_commandsArray<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



187
188
189
190
191
192
193
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 187

def configuration_commands
  SYSCTL_SETTINGS.map do |setting|
    command = "sudo sysctl -w #{setting.assignment}"
    command += " || true" if setting.optional
    command
  end
end

.configure!void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 207

def configure!
  unless executable
    ensure_installed!(install_from_tests: true)
    unless executable
      reset_state!
      return
    end
  end

  ohai "Configuring Bubblewrap..."
  command = [HOMEBREW_BREW_FILE.to_s, "setup-sandbox"]
  command.unshift("sudo") unless Process.euid.zero?
  raise ErrorDuringExecution.new(command, status: $CHILD_STATUS || 1) unless system(*command)

  reset_state!
end

.ensure_installed!(install_from_tests: false) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • install_from_tests (Boolean) (defaults to: false)


126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 126

def ensure_installed!(install_from_tests: false)
  return unless Homebrew::EnvConfig.sandbox_linux?
  return if ENV["HOMEBREW_TESTS"] && !install_from_tests
  return if ENV["HOMEBREW_INSTALLING_BUBBLEWRAP"]
  return if executable

  begin
    require "exceptions"
    require "formula"
    with_env(HOMEBREW_INSTALLING_BUBBLEWRAP: "1") do
      ::Formula["bubblewrap"].ensure_installed!(reason: "Linux sandboxing")
    end
    reset_state!
    return if executable
  rescue ::FormulaUnavailableError
    nil
  end

  return unless GitHub::Actions.env_set?
  return unless ENV.fetch("HOMEBREW_GITHUB_HOSTED_RUNNER", nil)
  return unless which("apt-get")

  ohai "Installing Bubblewrap..."
  command = ["apt-get", "install", "--yes", "bubblewrap"]
  command.unshift("sudo") unless Process.euid.zero?
  system(*command)
  reset_state!
end

.executable::Pathname?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 103

def executable
  executable_candidate_paths.each do |path|
    begin
      candidate = ::Pathname.new(File.expand_path(executable_name, path))
    rescue ArgumentError
      next
    end

    next if !candidate.file? || !candidate.executable?
    next unless executable_usable?(candidate)

    return candidate
  end

  nil
end

.executable!::Pathname

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



121
122
123
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 121

def executable!
  executable || raise("Bubblewrap is required to use the Linux sandbox.")
end

.executable_candidate_paths::PATH

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



98
99
100
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 98

def executable_candidate_paths
  PATH.new(HOMEBREW_PATHS, system_paths, ORIGINAL_PATHS, ENV.fetch("PATH"), HOMEBREW_ORIGINAL_BREW_FILE.dirname)
end

.executable_nameString

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



83
84
85
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 83

def executable_name
  EXECUTABLE
end

.executable_usable?(candidate) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


88
89
90
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 88

def executable_usable?(candidate)
  !File.stat(candidate).setuid?
end

.failure_reasonString?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 225

def failure_reason
  case state
  when :config_disabled, :available
    nil
  when :missing
    "Bubblewrap is required to use the Linux sandbox but was not found."
  when :setuid
    "A rootless Bubblewrap executable is required to use the Linux sandbox, " \
    "but all found `bwrap` executables are setuid."
  when :unavailable
    "Bubblewrap is installed but cannot create a rootless sandbox."
  else
    "The Linux sandbox is not available."
  end
end

.install_commandString?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



242
243
244
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 242

def install_command
  INSTALL_COMMANDS.find { |package_manager, _| which(package_manager) }&.last
end

.nested_sandbox?Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Bubblewrap reports this specific namespace error when an outer Bubblewrap sandbox prevents Homebrew from creating another rootless sandbox. The shared avoid_nested_sandboxing? only calls this once the $HOMEBREW_AVOID_NESTED_SANDBOXING opt-in is set.

Returns:

  • (Boolean)


165
166
167
168
169
170
171
172
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 165

def nested_sandbox?
  return false unless Homebrew::EnvConfig.sandbox_linux?

  bubblewrap = executable
  return false unless bubblewrap

  Utils.popen_read(bubblewrap.to_s, *TEST_ARGS, err: :out).include?(NESTED_ERROR)
end

.reset_state!void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



182
183
184
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 182

def reset_state!
  @state = T.let(nil, T.nilable(Symbol))
end

.stateSymbol

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



175
176
177
178
179
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 175

def state
  return :config_disabled unless Homebrew::EnvConfig.sandbox_linux?

  @state ||= T.let(compute_state, T.nilable(Symbol))
end

.system_pathsArray<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



93
94
95
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 93

def system_paths
  SYSTEM_PATHS
end

Instance Method Details

#arguments(tmpdir) ⇒ Array<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 312

def arguments(tmpdir)
  args = T.let([
    "--unshare-user",
    "--unshare-ipc",
    "--unshare-pid",
    "--unshare-uts",
    "--unshare-cgroup-try",
    "--die-with-parent",
    "--new-session",
    "--ro-bind", "/", "/",
    "--dev", "/dev",
    "--proc", "/proc"
  ], T::Array[String])
  args << "--unshare-net" if deny_all_network?

  writable_paths.each do |path, type|
    prepare_writable_path(path, type)
    args += ["--bind", path, path]
  end

  denied_write_paths.each do |path|
    next unless File.exist?(path)

    args += ["--ro-bind", path, path]
  end

  denied_read_paths.each do |path|
    next unless File.exist?(path)

    args += if File.directory?(path)
      ["--bind", masked_read_path, path]
    else
      ["--ro-bind", File::NULL, path]
    end
  end

  args += ["--bind", tmpdir, tmpdir, "--chdir", tmpdir]

  args
end

#command(args, tmpdir) ⇒ Array<String, ::Pathname>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



307
308
309
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 307

def command(args, tmpdir)
  [self.class.executable!, *arguments(tmpdir), "--", *args]
end

#run(&block) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • block (T.proc.void)


296
297
298
299
300
301
302
303
304
# File 'extend/os/linux/sandbox/bubblewrap.rb', line 296

def run(&block)
  old_report_on_exception = T.let(Thread.report_on_exception, T.nilable(T::Boolean))
  Thread.report_on_exception = false
  super
ensure
  Thread.report_on_exception = old_report_on_exception unless old_report_on_exception.nil?
  @masked_read_paths.reverse_each { |path| FileUtils.rm_rf(path) }
  @masked_read_paths.clear
end