How do you prompt for a sudo password using Ruby?
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.
Ruby CLI: Prompt for root password and continue executing script as root?
This works. It just uses exec to call itself (test_script
in this case) again. But be very careful to make sure that it doesn't run infinitely by adding a condition which will call exit
.
#!/usr/bin/env ruby
if ARGV[0] == "--second"
puts "...called again and exiting."
exit
end
puts "Calling self again..."
exec "sudo ./test_script --second"
Passing variable from Ruby as a password for shell
For security reasons, sudo doesn't accept passwords on standard input by default. You should configure your sudoers file with a NOPASSWD:
tag for the commands you want to execute without prompting, and invoke sudo with the -n
flag to ensure that your script doesn't get hung up waiting for input.
If you insist on passing in passwords, see if your sudo supports the -S
flag, which (on my system) says:
-S The -S (stdin) option causes sudo to read the password from
the standard input instead of the terminal device. The
password must be followed by a newline character.
How to net-ssh sudo su in Ruby
sudo
supports the -c
option, which passes a command to the sub-shell. Here are some of the sudo
flags that might be useful to you:
-c, --command=COMMAND
pass a single COMMAND to the shell with -c
--session-command=COMMAND
pass a single COMMAND to the shell with -c and do not create a new session
-m, --preserve-environment
do not reset environment variables
-s, --shell=SHELL
run SHELL if /etc/shells allows it
So, using something like sudo su someuser -c 'ls;date'
, you'll execute the commands ls
and date
as someuser
. Give it a try at the command-line on that host to get a feel for what you can do, then apply it to your SSH session.
See man sudo
for more information.
Also, just as a coding tip, you can reduce:
if data =~ /\[sudo\]/ || data =~ /Password/i
to:
if (data[/\[sudo\]|Password/i])
Related Topics
Why Are All Rails Helpers Available to All Views, All the Time? How to Disable This
Total Newbie: Instance Variables in Ruby
Why Do Two Strings Separated by Space Concatenate in Ruby
Ruby: Problem Installing Eventmachine Under Windows 7
Idiomatic Object Creation in Ruby
How to Count Duplicates in Ruby Arrays
How to Run All Tests With Minitest
Rake "Already Initialized Constant Wfkv_" Warning
"Gem Install Rails" Fails With Dns Error
Ruby Run Shell Command in a Specific Directory
Convert Unicode Codepoint to String Character in Ruby
Validation Failed: Upload File Has an Extension That Does Not Match Its Contents
Pass a Variable into a Partial, Rails 3
Why Does Adding "Sleep 1" in an After Hook Cause This Rspec/Capybara Test to Pass