简体   繁体   English

在python中保护孩子与父母的通信

[英]Secure child to parent communication in python

My python program needs elevated privileges and is therefore started by root (with a setuid-binary-wrapper).我的 python 程序需要提升权限,因此由 root 启动(使用 setuid-binary-wrapper)。

To keep the attack surface (and the impact of coding errors) as minimal as possible, I decided to split my code into two parts: One part is to be executed as root and the other with regular user permissions.为了尽可能减少攻击面(以及编码错误的影响),我决定将代码分成两部分:一部分以root身份执行,另一部分以regular user权限执行。 The problem is, that the code is interdependent and therefore needs secure two-way communication .问题是,代码是相互依赖的,因此需要secure two-way communication

I do not know if this is the right approach (other ideas welcome), but I decided to go with two processes - one parent process with elevated privileges and one child process with the privileges of a regular user.我不知道这是否是正确的方法(欢迎其他想法),但我决定使用two processes -一个具有提升权限的父进程和一个具有普通用户权限的子进程。

The idea:想法:

  • The parent process is started by root and keeps its privileges父进程由root启动并保持其特权
  • The parent process spawns a child process which drops to a regular user (the child process cannot regain root privileges)父进程产生一个子进程,该子进程落入普通用户(子进程无法重新获得 root 权限)
  • The child process does most of the work, but if it needs to execute something with root rights, it tells the parent process to do this for it子进程完成大部分工作,但如果它需要以 root 权限执行某些操作,它会告诉父进程为它执行此操作

The questions:问题:

  • Is subprocess (.Popen) sufficient for this? subprocess进程 (.Popen) 是否足够? Would multiprocessing be better suited? multiprocessing会更适合吗?

  • How can the child process and the parent communicate in an interactive and secure way (Is subprocess.PIPE secure)?子进程和父进程如何以交互和安全的方式进行通信( subprocess.PIPE是否安全)?

  • Do you know of any simple code examples somewhere for this scenario?你知道这个场景的某个地方有什么简单的代码示例吗?

------------------------------------------------------------------------- -------------------------------------------------- -----------------------

Based on Gil Hamilton's suggestions, I came up with below code根据吉尔汉密尔顿的建议,我想出了以下代码

Some questions remain:一些问题仍然存在:

  • Is this secure?这安全吗? Do I need to drop additional stuff like file descriptors or is os.setuid(<unprivileged UID>) sufficient?我是否需要删除文件描述符之类的其他内容,或者os.setuid(<unprivileged UID>)足够?
  • In general, if a process drops to a specific user like this, is this user able to interfere with the dropped processes memory?一般来说,如果一个进程像这样掉线给一个特定的用户,这个用户是否能够干扰被丢弃的进程内存?

privileged.py : privileged.py

#!/bin/python
from multiprocessing import Process, Pipe
from unprivileged import Unprivileged

if __name__ == '__main__':
  privilegedProcessPipeEnd, unprivilegedProcessPipeEnd = Pipe()
  unprivilegedProcess = Process(target=Unprivileged(unprivilegedProcessPipeEnd).operate)
  unprivilegedProcess.start()

  print(privilegedProcessPipeEnd.recv())
  privilegedProcessPipeEnd.send("ok")
  print(privilegedProcessPipeEnd.recv())
  privilegedProcessPipeEnd.send("nok")

  privilegedProcessPipeEnd.close()
  unprivilegedProcessPipeEnd.close()
  unprivilegedProcess.join()

unprivileged.py : unprivileged.py

import os

class Unprivileged:

  def __init__(self, unprivilegedProcessPipeEnd):
    self._unprivilegedProcessPipeEnd = unprivilegedProcessPipeEnd

  def operate(self):
    invokerUid = os.getuid()

    if invokerUid == 0:
      # started by root; TODO: drop to predefined standard user
      # os.setuid(standardUid)
      pass
    else:
      # started by a regular user through a setuid-binary 
      os.setuid(invokerUid) # TODO: drop to predefined standard user (save invokerUid for future stuff)

    # os.setuid(0) # not permitted anymore, cannot become root again

    print("os.getuid(): " + str(os.getuid()))

    self._unprivilegedProcessPipeEnd.send("invoke privilegedFunction1")
    print(self._unprivilegedProcessPipeEnd.recv())
    self._unprivilegedProcessPipeEnd.send("invoke privilegedFunction2")
    print(self._unprivilegedProcessPipeEnd.recv())

    return

main.c (setuid-wrapper program): main.c (setuid-wrapper 程序):

#include <unistd.h>
#define SCRIPT_PATH "/home/u1/project/src/privileged.py"

int
main(int argc,
     char **argv) {
  return execv(SCRIPT_PATH, argv);
}

/* compile and run like this:
$ gcc -std=c99 main.c -o main
# chown root:root main
# chmod 6771 main
$ chmod +x /home/u1/project/src/privileged.py
$ ./main
*/

This can be done with Popen but it's a bit clumsy IMO because you have no control over the process transition with Popen .这可以通过Popen完成,但 IMO 有点笨拙,因为您无法控制Popen的流程转换。 If you're relying on the UID to attenuate privileges, you need to fork and then in the child, adjust your UID, before invoking other child code.如果您依赖 UID 来削弱特权,则需要在调用其他子代码之前先在子代码中fork ,然后调整您的 UID。

(No real reason you couldn't put your child code in a separate program that you invoke with Popen and have it adjust its UID as the first step, it just seems to me a strange way to structure it.) (没有真正的理由你不能把你的子代码放在一个单独的程序中,你用Popen调用并让它作为第一步调整它的 UID,在我看来这是一种奇怪的结构方式。)

I would recommend that you look at using the multiprocessing module.我建议您考虑使用multiprocessing模块。 That module makes it easy to create a new process (it will handle the fork for you).该模块使创建新进程变得容易(它将为您处理分叉)。 Then you can easily drop in code that adjusts the UID (see below) and then you can just run the child code within the same "code base".然后,您可以轻松放入调整 UID 的代码(见下文),然后您可以在同一“代码库”中运行子代码。 That is, you don't necessarily need to invoke a separate program.也就是说,您不一定需要调用单独的程序。

The multiprocessing module also provides its own Pipe object as well as a Queue object, both of which are inter-process communication mechanisms. multiprocessing模块还提供了自己的Pipe对象和Queue对象,两者都是进程间通信机制。 Both are secure -- in the sense that no external user can horn in on them (without already having root privilege).两者都是安全的——从某种意义上说,没有外部用户可以对它们进行攻击(没有 root 权限)。 But of course if your unprivileged child process is compromised, it can send whatever it wants to the parent so your privileged parent will still need to validate / audit its input.但是当然,如​​果您的非特权子进程受到损害,它可以向父进程发送它想要的任何内容,因此您的特权父进程仍然需要验证/审核其输入。

The documentation for the multiprocessing module provides several simple examples that should get you started. multiprocessing模块的文档提供了几个可以帮助您入门的简单示例。 Once created, using a pipe is as easy as reading and writing a file.一旦创建,使用管道就像读写文件一样简单。

As for adjusting the UID, that's simply a single call to os.setuid in the child before invoking the code you want to run as an unprivileged user.至于调整 UID,在调用您想要作为非特权用户运行的代码之前, os.setuid在子os.setuid中调用os.setuid即可。 Read the setuid(2) and credentials(7) manual pages for more information.阅读setuid(2)credentials(7)手册页了解更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM