如何在Python中创建处于新Linux Namespace的进程
Python实现Linux新Namespace子进程的方法
Python标准库确实没有像Go那样直接通过设置SysProcAttr的Cloneflags来创建新Namespace子进程的简便API,但可以通过两种方式实现类似效果:
方法一:subprocess结合os.unshare(推荐)
利用subprocess.Popen的preexec_fn参数,在子进程执行目标命令前调用os.unshare创建新Namespace,效果和Go的实现一致。
示例代码(创建新UTS Namespace并修改主机名):
import subprocess import os def run_in_namespace(cmd, namespace_flags): def preexec(): # 在子进程exec前创建新Namespace os.unshare(namespace_flags) return subprocess.Popen(cmd, preexec_fn=preexec) # 执行命令:修改新UTS Namespace内的主机名并打印 proc = run_in_namespace( ["bash", "-c", "hostname new-uts-namespace && hostname"], os.CLONE_NEWUTS ) proc.wait()
原理:preexec_fn会在子进程fork完成、启动目标命令前执行,此时调用os.unshare就能让后续命令运行在隔离的Namespace中。
方法二:ctypes直接调用clone系统调用
如果想更贴近Go的底层实现(直接调用clone系统调用创建新Namespace),可以用ctypes调用libc的clone函数:
示例代码:
import ctypes import os libc = ctypes.CDLL("libc.so.6", use_errno=True) CLONE_NEWUTS = 0x04000000 # UTS Namespace的flag def clone_child_task(arg): # 子进程内执行的逻辑 os.system("hostname new-uts-namespace && hostname") return 0 def create_clone_process(child_func, flags): # 定义C语言兼容的函数类型 CChildFunc = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p) c_child_func = CChildFunc(child_func) # 分配子进程栈(Linux要求栈向下增长,所以传入栈顶地址) stack_size = 1024 * 1024 stack = ctypes.create_string_buffer(stack_size) stack_top = ctypes.cast(ctypes.addressof(stack) + stack_size, ctypes.c_void_p) pid = libc.clone(c_child_func, stack_top, flags, None) if pid == -1: err_no = ctypes.get_errno() raise OSError(err_no, os.strerror(err_no)) return pid # 创建新UTS Namespace的子进程 pid = create_clone_process(clone_child_task, CLONE_NEWUTS) os.waitpid(pid, 0)
注意事项
- 执行上述代码需要CAP_SYS_ADMIN权限,可以通过root用户运行,或者给进程添加
cap_sys_admin能力(setcap cap_sys_admin+ep your_script.py)。 - 不同Namespace对应不同的flag常量,如PID Namespace是
os.CLONE_NEWPID,Network Namespace是os.CLONE_NEWNET,可按位或组合多个flag(如os.CLONE_NEWUTS | os.CLONE_NEWPID)。
内容的提问来源于stack exchange,提问作者Andrei
相关产品推荐
相关产品推荐

