How do you prompt for a sudo password using Ruby?

2019-02-17 02:13发布

Often I find myself needing to write scripts that have to execute some portions as a normal user and other portions as a super user. I am aware of one similar question on SO where the answer was to run the same script twice and execute it as sudo, however that is not sufficient for me. Some times I need to revert to being a normal user after a sudo operation.

I have written the following in Ruby to do this

#!/usr/bin/ruby
require 'rubygems'
require 'highline/import'
require 'pty'
require 'expect'

def sudorun(command, password)
  `sudo -k`
  PTY.spawn("sleep 1; sudo -u root #{command} 2>&1") { | stdin, stdout, pid |
  begin
    stdin.expect(/password/) {
    stdout.write("#{password}\n")
    puts stdin.read.lstrip
                              }
  rescue Errno::EIO
  end
 }
end

Unfortunately, using that code if the user enters the wrong password the script crashes. Ideally it should give the user 3 tries to get the sudo password right. How do I fix this?

I am running this on Linux Ubuntu BTW.

4条回答
神经病院院长
2楼-- · 2019-02-17 02:48

If it is possible, you could move the stuff you want executed as root to a seperate file and use the system() function to run it as sudo, including the sudo prompt etc:

system("sudo ruby stufftorunasroot.rb")

The system() function is blocking, so the flow of your program doesn't need to be changed.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-02-17 02:50
#!/usr/bin/ruby
# ... blabla, other code
# part which requires sudo:
system "sudo -p 'sudo password: ' #{command}"
# other stuff
# sudo again
system "sudo -p 'sudo password: ' #{command}"
# usually sudo 'remembers' that you just authenticated yourself successfuly and doesn't ask for the PW again...
# some more code...
查看更多
叼着烟拽天下
4楼-- · 2019-02-17 03:00

I do not know if this is what you want or need, but have you tried sudo -A (search the web or the man page for SUDO_ASKPASS which might have a value like /usr/lib/openssh/gnome-ssh-askpass or similar)? This is what I use when I need to present a graphical password dialogue to users in GUI environments.

Sorry if this is the wrong answer, maybe you really want to remain on the console.

查看更多
Emotional °昔
5楼-- · 2019-02-17 03:09

In my opinion, running a script that does stuff internally with sudo is wrong. A better approach is to have the user run the whole script with sudo, and have the script fork lesser-privileged children to do stuff:

# Drops privileges to that of the specified user
def drop_priv user
  Process.initgroups(user.username, user.gid)
  Process::Sys.setegid(user.gid)
  Process::Sys.setgid(user.gid)
  Process::Sys.setuid(user.uid)
end

# Execute the provided block in a child process as the specified user
# The parent blocks until the child finishes.
def do_as_user user
  unless pid = fork
    drop_priv(user)
    yield if block_given?
    exit! 0 # prevent remainder of script from running in the child process
  end
  puts "Child running as PID #{pid} with reduced privs"
  Process.wait(pid)
end

at_exit { puts 'Script finished.' }

User = Struct.new(:username, :uid, :gid)
user = User.new('nobody', 65534, 65534)

do_as_user(user) do
  sleep 1 # do something more useful here
  exit! 2 # optionally provide an exit code
end

puts "Child exited with status #{$?.exitstatus}"
puts 'Running stuff as root'
sleep 1

do_as_user(user) do
  puts 'Doing stuff as a user'
  sleep 1
end

This example script has two helper methods. #drop_priv takes an object with username, uid, and gid defined and properly reduces the permissions of the executing process. The #do_as_user method calls #drop_priv in a child process before yielding to the provided block. Note the use of #exit! to prevent the child from running any part of the script outside of the block while avoiding the at_exit hook.

Often overlooked security concerns to think about:

  • Inheritance of open file descriptors
  • Environment variable filtering
  • Run children in a chroot?

Depending on what the script is doing, any of these may need to be addressed. #drop_priv is an ideal place to handle all of them.

查看更多
登录 后发表回答