bwrap 1.0.0.pre.alpha3 → 1.0.0.pre.beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7a362da994cec63d76530772a6740d68e9cf442f4bb43dfc39579ce6c10c40e
4
- data.tar.gz: 0ec62bee38e4729320ec365490e94c03f1f33f84e570e186ac009b587162ea24
3
+ metadata.gz: 807b5065d9a5615be9910e52bf7beed12faf271a6aa533de71fe925d759d68c3
4
+ data.tar.gz: a3ed8130aac69442f2175b9035aa34392b462fcc3934e3e7a69081b2b936b8f7
5
5
  SHA512:
6
- metadata.gz: 65e1ad6f76b55f0b9fbabc2066eb10000ab286d6440070331dd8244f608f0de39e3ccf1f54b6e663727da32c5c587a4ae87717d5f8276444f835caaf3ff0618a
7
- data.tar.gz: 5298f24103bb9c4290906fed7e6a5925a85228482f17cb2516bb6a3a50ace7c3fcb2d17de00abd1bd994d7cdd97fe420451631f50fe8792c247bab577a20ea4e
6
+ metadata.gz: 66131023be01339b797c21615ce32b5aa639fd0599590724f9262f8393d2cc9226c1bfa87bfad37dba751bac15d6b6fc9efa3babd168cc2f40973f8a5729f9cd
7
+ data.tar.gz: fcf5fdd36a7728e84502e33efb86d43ce08d44732f0c070ecdaa54b5bbc15a35749238fd79084b688baedd605b98252c59fdaee61ef4434ea967deb00f10a577
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changes
2
2
 
3
+ ## 1.0.0-beta2 (02.02.2022)
4
+
5
+ * Added nscd feature
6
+ * Added gem_env_paths to ruby feature
7
+ * If Config#root is set, set working directory to /
8
+ * Execution#execvalue: Allow setting log: true
9
+ * Execution#execvalue: pass all kwargs as kwargs to execute()
10
+ * Output::Log: Don’t die if log file can’t be written to
11
+
12
+ ## 1.0.0-beta1 (12.12.2021)
13
+
14
+ * optimist gem is now optional dependency
15
+ * Added Config#env_paths and Config#add_env_path
16
+ * Added Config#command_inside_root
17
+ * Added Bwrap#run_inside_root convenience method
18
+ * Execution#command_available?: added env_path_var argument
19
+ * Execution#which: added env_path_var argument
20
+ * Be able to resolve /usr/bin/env to real executable
21
+ * Try to avoid duplicate library binds
22
+ * Added Config#extra_executables
23
+ * Added Config::Features::Bash
24
+
25
+ ## 1.0.0-alpha5 (29.11.2021)
26
+
27
+ * Execution#command_available?: support absolute paths
28
+ * Execution#which: support absolute paths
29
+ * Many miscellaneous fixes
30
+
31
+ ## 1.0.0-alpha4 (22.11.2021)
32
+
33
+ * Allow use without home directory set
34
+ * Bwrap#parse_command_line_arguments is no longer run when Bwrap is initialized
35
+ * Made pulseaudio optional
36
+ * Changed --share-net to be added only if requested
37
+ * Added Config#root= to specify path used as writable root
38
+ * Added Config#full_system_mounts to control whether library loaders are mounted inside chroot
39
+ * Added Config#libdir_mounts
40
+ * Added Config#features
41
+
3
42
  ## 1.0.0-alpha3 (14.11.2021)
4
43
 
5
44
  * Avoid frozen string literal modification
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,188 @@
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
+ # TODO: It may be that this should be renamed to “Binary” or ”Executable”, as this
12
+ # handles all binaries, not just libraries.
13
+ #
14
+ # @api private
15
+ class Library
16
+ include Bwrap::Execution::Path
17
+ include Bwrap::Output
18
+
19
+ # @see Bwrap::Args::Construct#command=
20
+ #
21
+ # @see (see Bwrap::Args::Construct#command=)
22
+ attr_writer :command
23
+
24
+ # Instance of {Bwrap::Config}.
25
+ attr_writer :config
26
+
27
+ # Instance of {Bwrap::Args::Environment}.
28
+ attr_writer :environment
29
+
30
+ attr_writer :executable_name
31
+
32
+ attr_writer :executable_path
33
+
34
+ # Ruby feature implementation specific class.
35
+ #
36
+ # @api private
37
+ class RubyBinds
38
+ # Instance of {Bwrap::Config}.
39
+ attr_writer :config
40
+
41
+ def initialize args
42
+ @args = args
43
+ end
44
+
45
+ def ruby_binds_for_features
46
+ return unless @config and @config.features.ruby.enabled?
47
+
48
+ @mounts = []
49
+
50
+ # Mount some common Ruby executables.
51
+
52
+ # This is most often /usr/bin.
53
+ bindir = Pathname.new RbConfig::CONFIG["bindir"]
54
+
55
+ path = bindir / "ruby"
56
+ if File.exist? path
57
+ @mounts << "--ro-bind" << path.to_s << path.to_s
58
+ end
59
+
60
+ gem_binds bindir
61
+
62
+ @args += @mounts
63
+ end
64
+
65
+ private def gem_binds bindir
66
+ return unless @config.features.ruby.gem_env_paths?
67
+
68
+ path = bindir / "gem"
69
+ return unless File.exist? path
70
+
71
+ @mounts << "--ro-bind" << path.to_s << path.to_s
72
+ end
73
+ end
74
+
75
+ def initialize args
76
+ @args = args
77
+ end
78
+
79
+ def extra_executables_mounts
80
+ return unless @config&.extra_executables
81
+
82
+ @config.extra_executables.each do |executable|
83
+ @executable_name = resolve_executable_name executable
84
+ @executable_path = resolve_executable_path @executable_name, not_inside_root: true
85
+
86
+ @args.append %W{ --ro-bind #{@executable_path} #{@executable_path} }
87
+
88
+ resolve_executable_libraries
89
+ end
90
+ end
91
+
92
+ # Convenience method to call {#resolve_executable_libraries}.
93
+ #
94
+ # Used by {#handle_system_mounts}.
95
+ def libs_command_requires
96
+ @executable_name = resolve_executable_name @command
97
+ @executable_path = resolve_executable_path @executable_name
98
+
99
+ # Actually add the executable to be bound to the sandbox.
100
+ unless @config&.command_inside_root
101
+ @args.append %W{ --ro-bind #{@executable_path} #{@executable_path} }
102
+ end
103
+
104
+ resolve_executable_libraries
105
+ end
106
+
107
+ # Does some inspection to find out libraries given executable needs in order to work.
108
+ #
109
+ # @warning scanelf does not play with spaces in names well. This method assumes that libraries
110
+ # have no spaces in names, though binaries can have.
111
+ #
112
+ # @todo Ensure scanelf is available (and throw proper error if it is not, telling to not use
113
+ # full_system_mounts option.)
114
+ def resolve_executable_libraries
115
+ trace "Resolving executable libraries of #{@executable_path}"
116
+
117
+ # TODO: Put this behind additional flag for extra control/sanity.
118
+ # Some executables are shell scripts and similar. For them we need to use the interpreter.
119
+
120
+ mime = Mime.new @executable_name, @executable_path
121
+ return unless mime.resolve_mime_type
122
+
123
+ # Then find out required libraries
124
+
125
+ library_mounts = []
126
+
127
+ library_object = ::Bwrap::Args::Library.new
128
+ libraries = library_object.libraries_needed_by mime.executable_path
129
+
130
+ # TODO: following is bad?
131
+ #library_object.needed_libraries(mime.executable_path).each do |library|
132
+ libraries.each do |library|
133
+ library_mounts << "--ro-bind" << library << library
134
+ end
135
+
136
+ @args.append library_mounts
137
+ end
138
+
139
+ # Some features, like {Bwrap::Config::Features::Nscd}, requires some binds
140
+ # in order to operate properly.
141
+ def binds_for_features
142
+ # NOTE: Still nothing here, as I think this is better for library binds than anything else.
143
+ # The nscd bind is better in another, more generic, place.
144
+ #
145
+ # Keeping this method because I think this really makes sense for structure, in future.
146
+
147
+ ruby_binds_for_features
148
+ end
149
+
150
+ # Used by {#libs_command_requires}.
151
+ private def resolve_executable_name command
152
+ if command.is_a? String
153
+ return command
154
+ end
155
+
156
+ # Array-like.
157
+ if command.respond_to? :at
158
+ return command.at(0)
159
+ end
160
+
161
+ raise "Can’t recognize type of given command. Type: #{command.class}"
162
+ end
163
+
164
+ # @warning Requires environment paths to be resolved beforehand.
165
+ #
166
+ # Used by {#libs_command_requires}.
167
+ private def resolve_executable_path executable_name, not_inside_root: nil
168
+ if @config&.command_inside_root.nil? or not_inside_root
169
+ return which executable_name
170
+ end
171
+
172
+ paths = @environment.env_paths.map do |path|
173
+ "#{@config.root}/#{path}"
174
+ end
175
+ env_path = paths.join ":"
176
+
177
+ which executable_name, env_path_var: env_path
178
+ end
179
+
180
+ private def ruby_binds_for_features
181
+ return unless @config.features.ruby.enabled?
182
+
183
+ binds = RubyBinds.new @args
184
+ binds.config = @config
185
+ binds.ruby_binds_for_features
186
+ end
187
+ end
188
+ 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,17 +54,60 @@ 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
69
+
70
+ @args.append bindir_mounts
71
+
72
+ if debug?
73
+ debug "Using following bindir mounts:\n" \
74
+ "#{bindir_mounts}\n" \
75
+ "(Odd is key, even is value)"
76
+ end
77
+
78
+ libdir_mounts
79
+
80
+ library_bind = construct_library_bind
81
+
82
+ binds_for_features
83
+ library_bind.binds_for_features
84
+ library_bind.extra_executables_mounts
85
+
86
+ return unless @config.full_system_mounts
87
+
88
+ library_bind.libs_command_requires
89
+ end
90
+
91
+ # These are something user can specify to do custom --ro-bind binds.
92
+ def custom_read_only_binds
93
+ return unless @config.ro_binds
94
+
95
+ binds = []
96
+ @config.ro_binds.each do |source_path, destination_path|
97
+ binds << "--ro-bind" << source_path.to_s << destination_path.to_s
98
+ end
99
+
100
+ @args.append binds unless binds.empty?
101
+ end
102
+
103
+ # Performs cleanup operations after execution.
104
+ def cleanup
105
+ Bwrap::Args::Library.clear_needed_libraries_cache
106
+ end
107
+
108
+ # Used by {#handle_system_mounts}.
109
+ private def libdir_mounts
110
+ return unless @config.libdir_mounts
50
111
 
51
112
  libdir_mounts = %w{
52
113
  --ro-bind /lib /lib
@@ -55,24 +116,26 @@ module Bwrap::Args::Bind
55
116
  --ro-bind /usr/lib64 /usr/lib64
56
117
  }
57
118
 
58
- system_mounts = bindir_mounts + libdir_mounts
59
119
  if debug?
60
- debug "Using following system mounts:\n" \
61
- "#{system_mounts}\n" \
120
+ debug "Using following libdir mounts:\n" \
121
+ "#{libdir_mounts}\n" \
62
122
  "(Odd is key, even is value)"
63
123
  end
64
- system_mounts
124
+
125
+ @args.append libdir_mounts
65
126
  end
66
127
 
67
- # 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
128
+ private def construct_library_bind
129
+ library_bind = Bwrap::Args::Bind::Library.new @args
130
+ library_bind.command = @command
131
+ library_bind.config = @config
132
+ library_bind.environment = @environment
70
133
 
71
- binds = []
72
- @config.ro_binds.each do |source_path, destination_path|
73
- binds << "--ro-bind" << source_path.to_s << destination_path.to_s
74
- end
134
+ library_bind
135
+ end
75
136
 
76
- binds
137
+ # Binds feature specific common directories.
138
+ private def binds_for_features
139
+ # Nya.
77
140
  end
78
- end
141
+ 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.