bwrap 1.0.0.pre.alpha2 → 1.0.0.pre.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd1c029789733da3f76f30e1de8647ae2844ee70e7b3558944c9fda0e8c48cdd
4
- data.tar.gz: b2a46db9002fd1c9699ab4d763d1e98105c69539b1fcd05afeb9a9c887fe09f7
3
+ metadata.gz: db6e7253c0a896975954c0d649c68321958ade750f891f353ba30b9ea58880cb
4
+ data.tar.gz: 90f384499e8727660b9810711e0237ce604f8ef219ed415c210db16bcd764804
5
5
  SHA512:
6
- metadata.gz: 375d76049c7654c40258892a1d3a3e6d062ad80c75402560be71298572c58d5284151966c5b388c16f836999daea732d39fa1ab91a31f9ebd925476ef198fd75
7
- data.tar.gz: 0e31779606b0f8add41c8ff2c1420a39bfe84cad10dae64c1107161791a404569797c89ed3b8401c0976002203a025f078f603d145c7e9673d037e29cb0d5a0d
6
+ metadata.gz: 03e6873b068e52bc0c9fbbc072f9fc1a411696ca3c09aabcee05c8e660e5a210030c13fab84348aee720e7bd431600a0b18c3be124dfcb955ac95c250d83aeec
7
+ data.tar.gz: 4183575c47d740dd74c88995add7b87366299e75b4a8edaa89c20d422cd73cbd957b411e5bd51b9c9eabcd3881d8af1ba85922ce806b2387d75a0f30d8abd377
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changes
2
2
 
3
+ ## 1.0.0-beta1 (12.12.2021)
4
+
5
+ * optimist gem is now optional dependency
6
+ * Added Config#env_paths and Config#add_env_path
7
+ * Added Config#command_inside_root
8
+ * Added Bwrap#run_inside_root convenience method
9
+ * Execution#command_available?: added env_path_var argument
10
+ * Execution#which: added env_path_var argument
11
+ * Be able to resolve /usr/bin/env to real executable
12
+ * Try to avoid duplicate library binds
13
+ * Added Config#extra_executables
14
+ * Added Config::Features::Bash
15
+
16
+ ## 1.0.0-alpha5 (29.11.2021)
17
+
18
+ * Execution#command_available?: support absolute paths
19
+ * Execution#which: support absolute paths
20
+ * Many miscellaneous fixes
21
+
22
+ ## 1.0.0-alpha4 (22.11.2021)
23
+
24
+ * Allow use without home directory set
25
+ * Bwrap#parse_command_line_arguments is no longer run when Bwrap is initialized
26
+ * Made pulseaudio optional
27
+ * Changed --share-net to be added only if requested
28
+ * Added Config#root= to specify path used as writable root
29
+ * Added Config#full_system_mounts to control whether library loaders are mounted inside chroot
30
+ * Added Config#libdir_mounts
31
+ * Added Config#features
32
+
33
+ ## 1.0.0-alpha3 (14.11.2021)
34
+
35
+ * Avoid frozen string literal modification
36
+
3
37
  ## 1.0.0-alpha2 (14.11.2021)
4
38
 
5
39
  * Made gem to tell its license (ISC)
data/README.md CHANGED
@@ -31,3 +31,5 @@ Please see [API documentation](https://www.rubydoc.info/gems/bwrap) for usage in
31
31
  ## Contributing
32
32
 
33
33
  Bug reports and pull requests are welcome at https://git.sr.ht/~smar/ruby-bwrap.
34
+
35
+ Gerrit instance https://gerrit.smar.fi/admin/repos/ruby-bwrap can also be used.
@@ -2,7 +2,11 @@
2
2
 
3
3
  require "bwrap/version"
4
4
 
5
- # bwrap argument related operations.
5
+ # Classes that are used for building arguments.
6
+ #
7
+ # @note Classes inside here are kind of pseudo-internal API.
8
+ # In future, there may be some use for classes inside here, but for now they are
9
+ # only used internally.
6
10
  module Bwrap::Args
7
11
  # Nya.
8
12
  end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bwrap/execution/path"
4
+ require "bwrap/output"
5
+ require_relative "../library"
6
+ require_relative "mime"
7
+
8
+ class Bwrap::Args::Bind
9
+ # TODO: documentation
10
+ #
11
+ # @api private
12
+ class Library
13
+ include Bwrap::Execution::Path
14
+ include Bwrap::Output
15
+
16
+ # @see Bwrap::Args::Construct#command=
17
+ #
18
+ # @see (see Bwrap::Args::Construct#command=)
19
+ attr_writer :command
20
+
21
+ # Instance of {Bwrap::Config}.
22
+ attr_writer :config
23
+
24
+ # Instance of {Bwrap::Args::Environment}.
25
+ attr_writer :environment
26
+
27
+ attr_writer :executable_name
28
+
29
+ attr_writer :executable_path
30
+
31
+ def initialize args
32
+ @args = args
33
+ end
34
+
35
+ def extra_executables_mounts
36
+ return unless @config&.extra_executables
37
+
38
+ @config.extra_executables.each do |executable|
39
+ @executable_name = resolve_executable_name executable
40
+ @executable_path = resolve_executable_path @executable_name, not_inside_root: true
41
+
42
+ @args.append %W{ --ro-bind #{@executable_path} #{@executable_path} }
43
+
44
+ resolve_executable_libraries
45
+ end
46
+ end
47
+
48
+ # Convenience method to call {#resolve_executable_libraries}.
49
+ #
50
+ # Used by {#handle_system_mounts}.
51
+ def libs_command_requires
52
+ @executable_name = resolve_executable_name @command
53
+ @executable_path = resolve_executable_path @executable_name
54
+
55
+ # Actually add the executable to be bound to the sandbox.
56
+ unless @config&.command_inside_root
57
+ @args.append %W{ --ro-bind #{@executable_path} #{@executable_path} }
58
+ end
59
+
60
+ resolve_executable_libraries
61
+ end
62
+
63
+ # Does some inspection to find out libraries given executable needs in order to work.
64
+ #
65
+ # @warning scanelf does not play with spaces in names well. This method assumes that libraries
66
+ # have no spaces in names, though binaries can have.
67
+ #
68
+ # @todo Ensure scanelf is available (and throw proper error if it is not, telling to not use
69
+ # full_system_mounts option.)
70
+ def resolve_executable_libraries
71
+ trace "Resolving executable libraries of #{@executable_path}"
72
+
73
+ # TODO: Put this behind additional flag for extra control/sanity.
74
+ # Some executables are shell scripts and similar. For them we need to use the interpreter.
75
+
76
+ mime = Mime.new @executable_name, @executable_path
77
+ return unless mime.resolve_mime_type
78
+
79
+ # Then find out required libraries
80
+
81
+ library_mounts = []
82
+
83
+ library_object = ::Bwrap::Args::Library.new
84
+ libraries = library_object.libraries_needed_by mime.executable_path
85
+
86
+ # TODO: following is bad?
87
+ #library_object.needed_libraries(mime.executable_path).each do |library|
88
+ libraries.each do |library|
89
+ library_mounts << "--ro-bind" << library << library
90
+ end
91
+
92
+ @args.append library_mounts
93
+ end
94
+
95
+ # Used by {#libs_command_requires}.
96
+ private def resolve_executable_name command
97
+ if command.is_a? String
98
+ return command
99
+ end
100
+
101
+ # Array-like.
102
+ if command.respond_to? :at
103
+ return command.at(0)
104
+ end
105
+
106
+ raise "Can’t recognize type of given command. Type: #{command.class}"
107
+ end
108
+
109
+ # @warning Requires environment paths to be resolved beforehand.
110
+ #
111
+ # Used by {#libs_command_requires}.
112
+ private def resolve_executable_path executable_name, not_inside_root: nil
113
+ if @config&.command_inside_root.nil? or not_inside_root
114
+ return which executable_name
115
+ end
116
+
117
+ paths = @environment.env_paths.map do |path|
118
+ "#{@config.root}/#{path}"
119
+ end
120
+ env_path = paths.join ":"
121
+
122
+ which executable_name, env_path_var: env_path
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bwrap/execution"
4
+ require "bwrap/output"
5
+
6
+ class Bwrap::Args::Bind
7
+ # Inner class to clean up namespace for implementation specific reasons.
8
+ #
9
+ # @api private
10
+ class Mime
11
+ include Bwrap::Execution
12
+ include Bwrap::Output
13
+
14
+ # Name given to {#initialize}.
15
+ attr_reader :executable_name
16
+
17
+ # Either path given to {#initialize} or one parsed from shebang.
18
+ attr_reader :executable_path
19
+
20
+ def initialize executable_name, executable_path
21
+ @executable_name = executable_name
22
+ @executable_path = executable_path
23
+ end
24
+
25
+ # Used by {Bwrap::Args::Bind::Library#libs_command_requires}.
26
+ #
27
+ # @return false if caller should also return
28
+ def resolve_mime_type
29
+ mime_type = execvalue %W{ file --brief --mime-type #{@executable_path} }
30
+ trace "Mime type of #{@executable_path} is #{mime_type}"
31
+ return true unless mime_type[0..6] == "text/x-"
32
+
33
+ shebang = File.open @executable_path, &:readline
34
+ if shebang[0..1] != "#!"
35
+ warn "Executable #{@executable_name} was recognized as #{mime_type} but does not have " \
36
+ "proper shebang line. Skipping automatic library mounts."
37
+ return false
38
+ end
39
+
40
+ resolve_real_executable shebang
41
+
42
+ true
43
+ end
44
+
45
+ private def resolve_real_executable shebang
46
+ command_line = shebang.delete_prefix("#!").strip
47
+ real_executable, args = command_line.split " ", 2
48
+
49
+ if [ "/usr/bin/env", "/bin/env" ].include? real_executable
50
+ # First argument is name of the executable, resolved from PATH.
51
+ executable_name = args.split(" ", 2).first
52
+ real_executable = which executable_name
53
+ end
54
+
55
+ @executable_path = real_executable
56
+ end
57
+ end
58
+ end
@@ -1,32 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bwrap/execution"
4
+ require "bwrap/output"
3
5
  require_relative "args"
6
+ require_relative "bind/library"
4
7
 
5
8
  # Bind arguments for bwrap.
6
- module Bwrap::Args::Bind
9
+ class Bwrap::Args::Bind
10
+ include Bwrap::Execution
11
+ include Bwrap::Output
12
+
13
+ # Array of parameters passed to bwrap.
14
+ attr_writer :args
15
+
16
+ # @see Bwrap::Args::Construct#command=
17
+ #
18
+ # @see (see Bwrap::Args::Construct#command=)
19
+ attr_writer :command
20
+
21
+ # Instance of {Bwrap::Config}.
22
+ attr_writer :config
23
+
24
+ # Instance of {Bwrap::Args::Environment}.
25
+ attr_writer :environment
26
+
7
27
  # Arguments to bind /dev/dri from host to sandbox.
8
- private def bind_dev_dri
9
- %w{ --dev-bind /dev/dri /dev/dri }
28
+ def bind_dev_dri
29
+ @args.append %w{ --dev-bind /dev/dri /dev/dri }
10
30
  end
11
31
 
12
32
  # Arguments to bind /sys/dev/char from host to sandbox.
13
- private def bind_sys_dev_char
14
- %w{ --ro-bind /sys/dev/char /sys/dev/char }
33
+ def bind_sys_dev_char
34
+ @args.append %w{ --ro-bind /sys/dev/char /sys/dev/char }
15
35
  end
16
36
 
17
37
  # Arguments to bind /sys/devices/pci0000:00 from host to sandbox.
18
- private def bind_pci_devices
19
- %w{ --ro-bind /sys/devices/pci0000:00 /sys/devices/pci0000:00 }
38
+ def bind_pci_devices
39
+ @args.append %w{ --ro-bind /sys/devices/pci0000:00 /sys/devices/pci0000:00 }
20
40
  end
21
41
 
22
42
  # Arguments to bind home directory from sandbox directory (`#{@config.sandbox_directory}/home`)
23
43
  # as `/home/#{@config.user}`.
24
44
  #
25
45
  # @note Requires @config.user to be set.
26
- private def bind_home_directory
27
- unless @config.user
28
- raise "Tried to bind user directory without user being set."
29
- end
46
+ def bind_home_directory
47
+ return unless @config.user
30
48
 
31
49
  home_directory = "#{@config.sandbox_directory}/home"
32
50
 
@@ -36,43 +54,81 @@ module Bwrap::Args::Bind
36
54
 
37
55
  @environment["HOME"] = "/home/#{@config.user}"
38
56
 
39
- %W{ --bind #{home_directory} /home/#{@config.user} }
57
+ debug "Using #{home_directory} as /home/#{@config.user}"
58
+ @args.append %W{ --bind #{home_directory} /home/#{@config.user} }
40
59
  end
41
60
 
42
61
  # Arguments to read-only bind whole system inside sandbox.
43
- private def full_system_mounts
62
+ def handle_system_mounts
44
63
  bindir_mounts = []
45
64
  binaries_from = @config.binaries_from
46
65
  binaries_from.each do |path|
47
66
  bindir_mounts << "--ro-bind" << path << path
48
67
  end
49
- @environment["PATH"] = binaries_from.join(":")
68
+ @environment.add_to_path binaries_from
50
69
 
51
- libdir_mounts = %w{
52
- --ro-bind /lib /lib
53
- --ro-bind /lib64 /lib64
54
- --ro-bind /usr/lib /usr/lib
55
- --ro-bind /usr/lib64 /usr/lib64
56
- }
70
+ @args.append bindir_mounts
57
71
 
58
- system_mounts = bindir_mounts + libdir_mounts
59
72
  if debug?
60
- debug "Using following system mounts:\n" \
61
- "#{system_mounts}\n" \
73
+ debug "Using following bindir mounts:\n" \
74
+ "#{bindir_mounts}\n" \
62
75
  "(Odd is key, even is value)"
63
76
  end
64
- system_mounts
77
+
78
+ libdir_mounts
79
+
80
+ library_bind = construct_library_bind
81
+
82
+ library_bind.extra_executables_mounts
83
+
84
+ return unless @config.full_system_mounts
85
+
86
+ library_bind.libs_command_requires
65
87
  end
66
88
 
67
89
  # These are something user can specify to do custom --ro-bind binds.
68
- private def custom_read_only_binds
69
- return [] unless @config.ro_binds
90
+ def custom_read_only_binds
91
+ return unless @config.ro_binds
70
92
 
71
93
  binds = []
72
94
  @config.ro_binds.each do |source_path, destination_path|
73
95
  binds << "--ro-bind" << source_path.to_s << destination_path.to_s
74
96
  end
75
97
 
76
- binds
98
+ @args.append binds
99
+ end
100
+
101
+ # Performs cleanup operations after execution.
102
+ def cleanup
103
+ Bwrap::Args::Library.clear_needed_libraries_cache
104
+ end
105
+
106
+ # Used by {#handle_system_mounts}.
107
+ private def libdir_mounts
108
+ return unless @config.libdir_mounts
109
+
110
+ libdir_mounts = %w{
111
+ --ro-bind /lib /lib
112
+ --ro-bind /lib64 /lib64
113
+ --ro-bind /usr/lib /usr/lib
114
+ --ro-bind /usr/lib64 /usr/lib64
115
+ }
116
+
117
+ if debug?
118
+ debug "Using following libdir mounts:\n" \
119
+ "#{libdir_mounts}\n" \
120
+ "(Odd is key, even is value)"
121
+ end
122
+
123
+ @args.append libdir_mounts
124
+ end
125
+
126
+ private def construct_library_bind
127
+ library_bind = Bwrap::Args::Bind::Library.new @args
128
+ library_bind.command = @command
129
+ library_bind.config = @config
130
+ library_bind.environment = @environment
131
+
132
+ library_bind
77
133
  end
78
- end
134
+ end
@@ -5,60 +5,90 @@ require "tempfile"
5
5
  require "bwrap/output"
6
6
  require_relative "bind"
7
7
  require_relative "environment"
8
+ require_relative "features"
8
9
  require_relative "machine_id"
9
10
  require_relative "mount"
10
11
 
11
12
  # Constructs arguments for bwrap execution.
12
13
  class Bwrap::Args::Construct
13
14
  include Bwrap::Output
14
- include Bwrap::Args::Bind
15
15
  include Bwrap::Args::Mount
16
16
 
17
17
  attr_writer :config
18
18
 
19
+ # Command that is executed inside bwrap sandbox.
20
+ #
21
+ # @note This is not used for anything vital, but some things, like
22
+ # setting {Config#full_system_mounts=} uses this to resolve some
23
+ # additional data.
24
+ #
25
+ # @param value [Array, String] Command with arguments
26
+ attr_writer :command
27
+
19
28
  # Constructs arguments for bwrap execution.
20
29
  def construct_bwrap_args
21
- @environment = Bwrap::Args::Environment.new
22
- @environment.config = @config
23
- @machine_id = Bwrap::Args::MachineId.new
24
- @machine_id.config = @config
25
-
26
- [
27
- xauthority_args,
28
- @machine_id.machine_id,
29
- resolv_conf,
30
- full_system_mounts,
31
- custom_read_only_binds,
32
- create_user_dir,
33
- read_only_pulseaudio,
34
- dev_mount,
35
- bind_dev_dri,
36
- bind_sys_dev_char,
37
- bind_pci_devices,
38
- proc_mount,
39
- tmp_as_tmpfs,
40
- bind_home_directory,
41
- "--unshare-all",
42
- share_net,
43
- hostname,
44
- @environment.environment_variables,
45
- "--die-with-parent",
46
- "--new-session"
47
- ]
30
+ @args = []
31
+ create_objects
32
+
33
+ root_mount
34
+ xauthority_args
35
+ machine_id = @machine_id.machine_id
36
+ @args.append machine_id if machine_id
37
+ resolv_conf
38
+ @bind.handle_system_mounts
39
+ @features.feature_binds
40
+ @bind.custom_read_only_binds
41
+ create_user_dir
42
+ read_only_pulseaudio
43
+ dev_mount
44
+ @bind.bind_dev_dri
45
+ @bind.bind_sys_dev_char
46
+ @bind.bind_pci_devices
47
+ proc_mount
48
+ tmp_as_tmpfs
49
+ @bind.bind_home_directory
50
+ @args.append "--unshare-all"
51
+ share_net
52
+ hostname
53
+ @args.append @environment.environment_variables
54
+ @args.append "--die-with-parent"
55
+ @args.append "--new-session"
56
+
57
+ @args.compact
48
58
  end
49
59
 
50
60
  # Performs cleanup operations after execution.
51
61
  def cleanup
52
62
  @machine_id&.cleanup
63
+ @bind&.cleanup
64
+ end
65
+
66
+ # Used by {#construct_bwrap_args}.
67
+ private def create_objects
68
+ @bind = Bwrap::Args::Bind.new
69
+ @bind.args = @args
70
+ @bind.command = @command
71
+ @bind.config = @config
72
+
73
+ @environment = Bwrap::Args::Environment.new
74
+ @environment.config = @config
75
+ @bind.environment = @environment
76
+
77
+ @features = Bwrap::Args::Features.new
78
+ @features.args = @args
79
+ @features.config = @config
80
+
81
+ @machine_id = Bwrap::Args::MachineId.new
82
+ @machine_id.config = @config
53
83
  end
54
84
 
55
85
  # Arguments for generating .Xauthority file.
56
86
  private def xauthority_args
57
- return [] unless @config.xorg_application
87
+ return unless @config.xorg_application
58
88
 
59
89
  xauth_args = %W{ --ro-bind #{Dir.home}/.Xauthority #{Dir.home}/.Xauthority }
60
90
  debug "Binding following .Xauthority file: #{Dir.home}/.Xauthority"
61
- xauth_args
91
+ @args.append xauth_args
62
92
  end
63
93
 
64
94
  # Arguments to read-only bind /etc/resolv.conf.
@@ -68,25 +98,29 @@ class Bwrap::Args::Construct
68
98
  source_resolv_conf = source_resolv_conf.realpath
69
99
 
70
100
  debug "Binding #{source_resolv_conf} as /etc/resolv.conf"
71
- %W{ --ro-bind #{source_resolv_conf} /etc/resolv.conf }
101
+ @args.append %W{ --ro-bind #{source_resolv_conf} /etc/resolv.conf }
72
102
  end
73
103
 
74
104
  # Arguments to create `/run/user/#{uid}`.
75
105
  private def create_user_dir
76
106
  trace "Creating directory /run/user/#{uid}"
77
- %W{ --dir /run/user/#{uid} }
107
+ @args.append %W{ --dir /run/user/#{uid} }
78
108
  end
79
109
 
80
110
  # Arguments to bind necessary pulseaudio data for audio support.
81
111
  private def read_only_pulseaudio
112
+ return unless @config.audio.include? :pulseaudio
113
+
82
114
  debug "Binding pulseaudio"
83
- %W{ --ro-bind /run/user/#{uid}/pulse /run/user/#{uid}/pulse }
115
+ @args.append %W{ --ro-bind /run/user/#{uid}/pulse /run/user/#{uid}/pulse }
84
116
  end
85
117
 
86
118
  # Arguments to allow network connection inside sandbox.
87
119
  private def share_net
120
+ return unless @config.share_net
121
+
88
122
  verb "Sharing network"
89
- %w{ --share-net }
123
+ @args.append %w{ --share-net }
90
124
  end
91
125
 
92
126
  # Arguments to set hostname to whatever is configured.
@@ -94,7 +128,7 @@ class Bwrap::Args::Construct
94
128
  return unless @config.hostname
95
129
 
96
130
  debug "Setting hostname to #{@config.hostname}"
97
- %W{ --hostname #{@config.hostname} }
131
+ @args.append %W{ --hostname #{@config.hostname} }
98
132
  end
99
133
 
100
134
  # Returns current user id.
@@ -10,15 +10,46 @@ class Bwrap::Args::Environment < Hash
10
10
  # Instance of {Config}.
11
11
  attr_writer :config
12
12
 
13
- # Returns used environment variables.
13
+ # Returns used environment variables wrapped as bwrap arguments.
14
14
  def environment_variables
15
15
  if debug?
16
16
  debug "Passing following environment variables to bwrap:\n" \
17
17
  "#{self}"
18
18
  end
19
19
 
20
+ env_paths
21
+
20
22
  map do |key, value|
23
+ if key == "PATH" and value.respond_to? :join
24
+ value = value.join ":"
25
+ end
26
+
21
27
  [ "--setenv", key, value ]
22
28
  end
23
29
  end
30
+
31
+ # @return [Array] All environment paths added via {Config#add_env_path} and other parsing logic
32
+ def env_paths
33
+ if @config.env_paths.respond_to? :each
34
+ self["PATH"] ||= []
35
+
36
+ self["PATH"] |= @config.env_paths
37
+ end
38
+
39
+ self["PATH"]
40
+ end
41
+
42
+ # Adds given paths to PATH environment variable defined in the sandbox.
43
+ #
44
+ # @param elements [String, Array] Path(s) to be added added to PATH environment variable
45
+ def add_to_path elements
46
+ self["PATH"] ||= []
47
+
48
+ if elements.respond_to? :each
49
+ self["PATH"] += elements
50
+ else
51
+ # Expecting elements to be single path element as a string.
52
+ self["PATH"] << elements
53
+ end
54
+ end
24
55
  end