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

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: 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