运行Ruby块作为特定的OS用户?

时间:2010-12-28 17:57:32

标签: ruby-on-rails ruby

您可以作为不同的OS用户执行一段Ruby代码吗?

理想情况下,我想要的是:

user("christoffer") do
  # do something
end

可能的?

2 个答案:

答案 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打印为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}"