diff --git a/CHANGELOG.md b/CHANGELOG.md index 154c099..9e73b37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,16 @@ IMPROVEMENTS: BUG FIXES: + - Figure out the real executable paths for whitelisted commands on the sudo + wrapper script instead of hardcoding Ubuntu paths [[GH-304]] / [[GH-305]] - Attach to containers using the `MOUNT` namespace when attempting to fetch container's IP [[GH-300]] - Escape space characters for synced folders [[GH-291]] - Use Vagrant's ruby on the sudoers file so that it works on systems that don't have a global ruby installation [[GH-289]] +[GH-304]: https://github.com/fgrehm/vagrant-lxc/issues/304 +[GH-305]: https://github.com/fgrehm/vagrant-lxc/issues/305 [GH-300]: https://github.com/fgrehm/vagrant-lxc/issues/300 [GH-291]: https://github.com/fgrehm/vagrant-lxc/issues/291 [GH-289]: https://github.com/fgrehm/vagrant-lxc/issues/289 diff --git a/lib/vagrant-lxc/command/sudoers.rb b/lib/vagrant-lxc/command/sudoers.rb index 20c63e4..689ce08 100644 --- a/lib/vagrant-lxc/command/sudoers.rb +++ b/lib/vagrant-lxc/command/sudoers.rb @@ -40,130 +40,21 @@ module Vagrant end private - # REFACTOR: Make use ERB rendering after https://github.com/mitchellh/vagrant/issues/3231 - # lands into core + + # This requires vagrant 1.5.2+ https://github.com/mitchellh/vagrant/commit/3371c3716278071680af9b526ba19235c79c64cb def create_wrapper! wrapper = Tempfile.new('lxc-wrapper').tap do |file| - file.puts "#!/opt/vagrant/embedded/bin/ruby" - file.puts "# Automatically created by vagrant-lxc" - file.puts <<-EOF -class Whitelist - class << self - def add(command, *args) - list[command] << args - end - - def list - @list ||= Hash.new do |key, hsh| - key[hsh] = [] - end - end - - def allowed(command) - list[command] || [] - end - - def run!(argv) - begin - command, args = `which \#{argv.shift}`.chomp, argv || [] - check!(command, args) - puts `\#{command} \#{args.join(" ")}` - exit $?.to_i - rescue => e - STDERR.puts e.message - exit 1 - end - end - - private - def check!(command, args) - allowed(command).each do |checks| - return if valid_args?(args, checks) - end - raise_invalid(command, args) - end - - def valid_args?(args, checks) - return false unless valid_length?(args, checks) - check = nil - args.each_with_index do |provided, i| - check = checks[i] unless check == '**' - return false unless match?(provided, check) - end - true - end - - def valid_length?(args, checks) - args.length == checks.length || checks.last == '**' - end - - def match?(arg, check) - check == '**' || check.is_a?(Regexp) && !!check.match(arg) || arg == check - end - - def raise_invalid(command, args) - raise "Invalid arguments for command \#{command}, " << - "provided args: \#{args.inspect}" - end - end -end - -base = "/var/lib/lxc" -base_path = %r{\\A\#{base}/.*\\z} -templates_path = %r{\\A/usr/(share|lib|lib64|local/lib)/lxc/templates/.*\\z} - -## -# Commands from provider.rb -# - Check lxc is installed -Whitelist.add '/usr/bin/which', /\\Alxc-\\w+\\z/ - -## -# Commands from driver.rb -# - Container config file -Whitelist.add '/bin/cat', base_path -# - Shared folders -Whitelist.add '/bin/mkdir', '-p', base_path -# - Container config customizations and pruning -Whitelist.add '/bin/cp', '-f', %r{/tmp/.*}, base_path -Whitelist.add '/bin/chown', 'root:root', base_path -# - Template import -Whitelist.add '/bin/cp', %r{\\A.*\\z}, templates_path -Whitelist.add '/bin/cp', %r{\\A.*\\z}, templates_path -Whitelist.add '/bin/cp', %r{\\A.*\\z}, templates_path -Whitelist.add '/bin/chmod', '+x', templates_path -# - Template removal -Whitelist.add '/bin/rm', templates_path -# - Packaging -Whitelist.add '/bin/tar', '--numeric-owner', '-cvzf', %r{/tmp/.*/rootfs.tar.gz}, '-C', base_path, './rootfs' -Whitelist.add '/bin/chown', /\\A\\d+:\\d+\\z/, %r{\\A/tmp/.*/rootfs\.tar\.gz\\z} - -## -# Commands from driver/cli.rb -Whitelist.add '/usr/bin/lxc-version' -Whitelist.add '/usr/bin/lxc-ls' -Whitelist.add '/usr/bin/lxc-info', '--name', /.*/ -Whitelist.add '/usr/bin/lxc-create', '-B', /.*/, '--template', /.*/, '--name', /.*/, '**' -Whitelist.add '/usr/bin/lxc-destroy', '--name', /.*/ -Whitelist.add '/usr/bin/lxc-start', '-d', '--name', /.*/, '**' -Whitelist.add '/usr/bin/lxc-stop', '--name', /.*/ -Whitelist.add '/usr/bin/lxc-shutdown', '--name', /.*/ -Whitelist.add '/usr/bin/lxc-attach', '--name', /.*/, '**' -Whitelist.add '/usr/bin/lxc-attach', '-h' - -## -# Commands from driver/action/remove_temporary_files.rb -Whitelist.add '/bin/rm', '-rf', %r{\\A\#{base}/.*/rootfs/tmp/.*} - -# Watch out for stones -Whitelist.run!(ARGV) - EOF + template = Vagrant::Util::TemplateRenderer.new( + 'sudoers.rb', + :template_root => Vagrant::LXC.source_root.join('templates').to_s, + :cmd_paths => build_cmd_paths_hash + ) + file.puts template.render end wrapper.close wrapper.path end - # REFACTOR: Make use ERB rendering after https://github.com/mitchellh/vagrant/issues/3231 - # lands into core def create_sudoers!(user, command) sudoers = Tempfile.new('vagrant-lxc-sudoers').tap do |file| file.puts "# Automatically created by vagrant-lxc" @@ -184,6 +75,15 @@ Whitelist.run!(ARGV) }.flatten system "echo \"#{commands.join("; ")}\" | sudo sh" end + + def build_cmd_paths_hash + {}.tap do |hash| + %w( which cat mkdir cp chown chmod rm tar chown ).each do |cmd| + hash[cmd] = `which #{cmd}`.strip + end + hash['lxc_bin'] = Pathname(`which lxc-create`.strip).parent.to_s + end + end end end end diff --git a/templates/sudoers.rb.erb b/templates/sudoers.rb.erb new file mode 100644 index 0000000..dda3c11 --- /dev/null +++ b/templates/sudoers.rb.erb @@ -0,0 +1,110 @@ +#!/opt/vagrant/embedded/bin/ruby +# Automatically created by vagrant-lxc + +class Whitelist + class << self + def add(command, *args) + list[command] << args + end + + def list + @list ||= Hash.new do |key, hsh| + key[hsh] = [] + end + end + + def allowed(command) + list[command] || [] + end + + def run!(argv) + begin + command, args = `which #{argv.shift}`.chomp, argv || [] + check!(command, args) + puts `#{command} #{args.join(" ")}` + exit $?.to_i + rescue => e + STDERR.puts e.message + exit 1 + end + end + + private + def check!(command, args) + allowed(command).each do |checks| + return if valid_args?(args, checks) + end + raise_invalid(command, args) + end + + def valid_args?(args, checks) + return false unless valid_length?(args, checks) + check = nil + args.each_with_index do |provided, i| + check = checks[i] unless check == '**' + return false unless match?(provided, check) + end + true + end + + def valid_length?(args, checks) + args.length == checks.length || checks.last == '**' + end + + def match?(arg, check) + check == '**' || check.is_a?(Regexp) && !!check.match(arg) || arg == check + end + + def raise_invalid(command, args) + raise "Invalid arguments for command #{command}, " << + "provided args: #{args.inspect}" + end + end +end + +base = "/var/lib/lxc" +base_path = %r{\A#{base}/.*\z} +templates_path = %r{\A/usr/(share|lib|lib64|local/lib)/lxc/templates/.*\z} + +## +# Commands from provider.rb +# - Check lxc is installed +Whitelist.add '<%= cmd_paths['which'] %>', /\Alxc-\w+\z/ + +## +# Commands from driver.rb +# - Container config file +Whitelist.add '<%= cmd_paths['cat'] %>', base_path +# - Shared folders +Whitelist.add '<%= cmd_paths['mkdir'] %>', '-p', base_path +# - Container config customizations and pruning +Whitelist.add '<%= cmd_paths['cp'] %>', '-f', %r{/tmp/.*}, base_path +Whitelist.add '<%= cmd_paths['chown'] %>', 'root:root', base_path +# - Template import +Whitelist.add '<%= cmd_paths['cp'] %>', %r{\A.*\z}, templates_path +Whitelist.add '<%= cmd_paths['chmod'] %>', '+x', templates_path +# - Template removal +Whitelist.add '<%= cmd_paths['rm'] %>', templates_path +# - Packaging +Whitelist.add '<%= cmd_paths['tar'] %>', '--numeric-owner', '-cvzf', %r{/tmp/.*/rootfs.tar.gz}, '-C', base_path, './rootfs' +Whitelist.add '<%= cmd_paths['chown'] %>', /\A\d+:\d+\z/, %r{\A/tmp/.*/rootfs\.tar\.gz\z} + +## +# Commands from driver/cli.rb +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-version' +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-ls' +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-info', '--name', /.*/ +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-create', '-B', /.*/, '--template', /.*/, '--name', /.*/, '**' +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-destroy', '--name', /.*/ +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-start', '-d', '--name', /.*/, '**' +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-stop', '--name', /.*/ +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-shutdown', '--name', /.*/ +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-attach', '--name', /.*/, '**' +Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-attach', '-h' + +## +# Commands from driver/action/remove_temporary_files.rb +Whitelist.add '<%= cmd_paths['rm'] %>', '-rf', %r{\A#{base}/.*/rootfs/tmp/.*} + +# Watch out for stones +Whitelist.run!(ARGV)