Running Code as Another User
Problem
While writing a Ruby script that runs as root, you need to take some action on behalf of another user: say, run an external program or create a file.
Solution
Simply set Process.euid to the UID of the user. When you e done, set it back to its previous value (that is, roots UID). Heres a method Process.as_uid that runs a code block under a different user ID and resets it at the end:
module Process def as_uid(uid) old_euid, old_uid = Process.euid, Process.uid Process.euid, Process.uid = uid, uid begin yield ensure Process.euid, Process.uid = old_euid, old_uid end end module_function(:as_uid) end
Discussion
When a Unix process tries to do something that requires special permissions (like access a file), the permissions are checked according to the "effective user ID" of the process. The effective user ID starts out as the user ID you used when you started the process, but if you e root you can change the effective user ID with Process.euid=. The operating system will treat you as though you were really that user.
This comes in handy when you e administering a system used by others. When someone asks you for help, you can write a script that impersonates them and runs the commands they don know how to run. Rather than creating files as root and using chown to give them to another user, you can create the files as the other user in the first place.
Heres an example. On my system the account leonardr has UID 1000. When run as root, this code will create one directory owned by root and one owned by leonardr:
Dir.mkdir("as_root") Process.as_uid(1000) do Dir.mkdir("as_leonardr") %x{whoami} end # => "leonardr "
Here are the directories:
$ ls -ld as_* drwxr-xr-x 2 leonardr root 4096 Feb 2 13:06 as_leonardr/ drwxr-xr-x 2 root root 4096 Feb 2 13:06 as_root/
When you e impersonating another user, your permissions are restricted to what that user can do. I can remove the as_root directory as a nonroot user, because I created it as root:
Process.as_uid(1000) do Dir.rmdir("as_root") end # Errno::EPERM: Operation not permitted - as_root Dir.rmdir("as_root") # => 0
On Windows, you can do something like this by splitting your Ruby script into two, and running the second one through runas.exe:
# script_one.rb system unas /user:frednerk ruby script_two.rb
See Also
- Recipe 6.2, "Checking Your Access to a File"
- If you want to pass in the name of the user to impersonate, instead of their UID, you can adapt the technique shown in Recipe 23.10, "Killing All Processes for a Given User"
Категории