您可以作为不同的OS用户执行一段Ruby代码吗?
理想情况下,我想要的是:
user("christoffer") do
# do something
end
可能的?
答案 0 :(得分:7)
此代码可以执行您想要的操作。错误处理取决于您。 ; - )
require 'etc'
def as_user(user, &block)
u = Etc.getpwnam(user)
Process.fork do
Process.uid = u.uid
block.call(user)
end
end
puts("caller PID = #{Process.pid}")
puts("caller UID = #{Process.uid}")
as_user "bmc" do |user|
puts("In block as #{user} (uid=#{Process.uid}), pid is #{Process.pid}")
end
但请注意,它需要您将Ruby作为root
或setuid-to - root
运行,这会产生严重的安全隐患。
答案 1 :(得分:0)
可接受的答案确实会更改UID,但是在创建文件或子进程时,单独执行此操作会产生令人惊讶的结果。试试:
as_user 'bmc' do |user|
File.open('/tmp/out.txt', 'w')
end
您会发现该文件是作为root创建的,这不是人们期望的。
使用Backtics运行命令时,行为难以预测。以下结果可能与预期不符:
as_user 'puppet' do
puts `whoami`
puts `id`
puts `whoami; id`
end
在Linux系统上进行测试,第一个puts
打印为root
。 id
打印了以下内容:
uid=1052(chet) gid=0(root) euid=0(root) groups=0(root)
最后的puts
不同意:
puppet
uid=1052(chet) gid=0(root) groups=0(root)
要获得一致的行为,请确保还设置有效的UID:
def as_user(user, &block)
u = Etc.getpwnam(user)
Process.fork do
Process.uid = Process.euid = u.uid
block.call(user)
end
end
从子进程取回值可能很有用。加上一点IPC乐趣即可:
require 'etc'
def as_user(user, &block)
u = (user.is_a? Integer) ? Etc.getpwuid(user) : Etc.getpwnam(user)
reader, writer = IO.pipe
Process.fork do
# the child process won't need to read from the pipe
reader.close
# use primary group ID of target user
# This needs to be done first as we won't have
# permission to change our group after changing EUID
Process.gid = Process.egid = u.gid
# set real and effective UIDs to target user
Process.uid = Process.euid = u.uid
# get the result and write it to the IPC pipe
result = block.call(user)
Marshal.dump(result, writer)
writer.close
# prevent shutdown hooks from running
Process.exit!(true)
end
# back to reality... we won't be writing anything
writer.close
# block until there's data to read
result = Marshal.load(reader)
# done with that!
reader.close
# return block result
result
end
val = as_user 'chet' do
`whoami` + `id` + `whoami; id`
end
puts "back from wonderland: #{val}"