你能以不同的操作系统用户身份执行Ruby代码块吗?
我理想中希望的是这样的:
user("christoffer") do
# do something
end
可以的吗?
你能以不同的操作系统用户身份执行Ruby代码块吗?
我理想中希望的是这样的:
user("christoffer") do
# do something
end
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
请注意,这将需要您以root
身份运行Ruby,或者以设置为root
的用户ID运行,这会带来一些严重的安全隐患。
接受的答案确实会更改UID,但仅这样做可能会在创建文件或子进程时产生意想不到的结果。请尝试:
as_user 'bmc' do |user|
File.open('/tmp/out.txt', 'w')
end
你会发现该文件是由root用户创建的,这可能不是人们所期望的。
使用反引号运行命令时,行为不太可预测。以下命令的结果可能与人们所期望的不同:
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)
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}"