以特定的操作系统用户身份运行Ruby代码块?

3

你能以不同的操作系统用户身份执行Ruby代码块吗?

我理想中希望的是这样的:

user("christoffer") do
  # do something
end

可以的吗?

这是在一个POSIX操作系统上运行的(Linux或Mac),对吗? 另外,你为什么想要这样做? - Maz
Ubuntu服务器没问题。我有一个RoR应用程序,它使用几个不同用户的git存储库。为了推送和拉取,我想以特定用户身份运行git脚本,以便我可以使用该用户的ssh密钥。 - Christoffer
2个回答

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

请注意,这将需要您以root身份运行Ruby,或者以设置为root的用户ID运行,这会带来一些严重的安全隐患。


我该如何在Windows上完成同样的事情呢? :) - Feytality

2

接受的答案确实会更改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打印了rootid打印如下:

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}"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接