Module: Chrooter

Defined in:
lib/chrooter.rb

Overview

Chrooter allows for testing programs in both chroot/unveil and non chroot modes.

Class Method Summary collapse

Class Method Details

.chroot(user, pledge = nil, group = user, dir = Dir.pwd) ⇒ Object

If the current user is the super user, change to the given user/group, chroot to the given directory, and pledge the process with the given permissions (if given).

If the current user is not the super user, freeze $LOADED_FEATURES to more easily detect problems with autoloaded constants, and just pledge the process with the given permissions (if given).

This will reference common autoloaded constants in the rack and mail libraries if they are defined. Other autoloaded constants should be referenced before calling this method.

In general this should be called inside an at_exit block after loading minitest/autorun, so it will run after all specs are loaded, but before running specs.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/chrooter.rb', line 70

def self.chroot(user, pledge=nil, group=user, dir=Dir.pwd)
  # Work around autoload issues in libraries.
  # autoload is problematic when chrooting because if the
  # constant is not referenced before chrooting, an
  # exception is raised if the constant is raised
  # after chrooting.
  #
  # The constants listed here are the autoloaded constants
  # known to be used by any applications.  This list
  # may need to be updated when libraries are upgraded
  # and add new constants, or when applications start
  # using new features.
  if defined?(Rack)
    Rack::MockRequest if defined?(Rack::MockRequest)
    Rack::Auth::Digest::Params if defined?(Rack::Auth::Digest::Params)
    if defined?(Rack::Multipart)
      Rack::Multipart
      Rack::Multipart::Parser
      Rack::Multipart::Generator
      Rack::Multipart::UploadedFile
    end
  end
  if defined?(Mail)
    Mail::Address
    Mail::AddressList
    Mail::Parsers::AddressListsParser
    Mail::ContentTransferEncodingElement
    Mail::ContentDispositionElement
    Mail::MessageIdsElement
    Mail::MimeVersionElement
    Mail::OptionalField
    Mail::ContentTypeElement
  end

  if Process.euid == 0
    _drop_privs(user, group) do
      Dir.chroot(dir)
      Dir.chdir('/')
    end
    puts "Chrooted to #{dir}, running as user #{user}"
  else
    # Load minitest plugins before freezing loaded features,
    # so they don't break.
    Minitest.load_plugins

    # Emulate chroot not working by freezing $LOADED_FEATURES
    # This allows to more easily catch bugs that only occur
    # when chrooted, such as referencing an autoloaded constant
    # that wasn't loaded before the chroot.
    $LOADED_FEATURES.freeze
  end

  _pledge(pledge)
end

.unveil(user, pledge = nil, unveil = {}, group = user) ⇒ Object

If the current user is the super user, drop privileges to the given user first.

Use Pledge.unveil to limit access to the file system based on the unveil option. Then pledge the process with the given pledge permissions (if given).



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/chrooter.rb', line 32

def self.unveil(user, pledge=nil, unveil={}, group=user)
  require 'unveil'

  unveil = Hash[unveil]
  if defined?(Rack)
    unveil['rack'] = :gem
  end
  if defined?(Mail)
    unveil['mail'] = :gem
  end

  if Process.euid == 0
    _drop_privs(user, group)
    puts "Unveiled, running as user #{user}"
  end

  Pledge.unveil(unveil)

  _pledge(pledge)
end