Bernard Bernard - 4 years ago 177
Ruby Question

How do you prompt for a sudo password using Ruby?

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

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 |
stdin.expect(/password/) {
rescue Errno::EIO

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.

Answer Source

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)

# 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
    yield if block_given?
    exit! 0 # prevent remainder of script from running in the child process
  puts "Child running as PID #{pid} with reduced privs"

at_exit { puts 'Script finished.' }

User =, :uid, :gid)
user ='nobody', 65534, 65534)

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

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

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download