Python脚本以sudo启动后如何切换普通用户执行再切回root权限
答案
完全可以实现,且不会触发sudo超时问题,也不需要修改系统sudo配置。
实现原理
- 通过sudo启动的root进程会自动继承
SUDO_UID、SUDO_GID两个环境变量,存储了发起sudo的普通用户的UID和GID。 - root权限的进程可以自由修改有效用户ID(euid)、有效用户组ID(egid):临时降权时切换为普通用户的UID/GID,需要高权限时再切回0即可。整个过程进程本身的root权限不会丢失,完全不需要重新验证sudo密码,自然不会触发超时问题。
修改后的完整脚本
#!/usr/bin/env python import os import sys import pwd def update(): if os.geteuid() != 0: print("The --config function must run as root.") raise SystemExit(0) print("About to upgrade your OS") os.system("apt-get -y update; apt-get -y dist-upgrade") def build(): if os.geteuid() == 0: print("This function should not be run as root.") raise SystemExit(0) if not os.path.isdir("source"): os.system("git clone https://path.to/source/source.git") else: os.system("(chdir source; git pull)") os.chdir("source") os.system("make") def install(): "Install software & start service once everything has been tested." if os.geteuid() != 0: print("The --install function must run as root.") raise SystemExit(0) os.chdir("source") os.system("make install") os.system("systemctl daemon-reload") os.system("systemctl enable software.service") if __name__ == "__main__": # 校验运行身份 if os.geteuid() != 0: print("请使用sudo启动本脚本") raise SystemExit(1) # 获取发起sudo的普通用户信息 sudo_uid = int(os.environ.get("SUDO_UID", -1)) sudo_gid = int(os.environ.get("SUDO_GID", -1)) if sudo_uid < 0 or sudo_gid < 0: print("未检测到普通用户信息,请不要在root shell下直接执行本脚本,需通过sudo调用") raise SystemExit(1) sudo_user = pwd.getpwuid(sudo_uid) ordinary_home = sudo_user.pw_dir root_home = os.environ.get("HOME", "/root") # 1. root权限执行update print("===== 运行update环节 =====") update() # 2. 降权为普通用户执行build print("\n===== 运行build环节 =====") # 切换顺序必须先改GID再改UID os.setegid(sudo_gid) os.seteuid(sudo_uid) os.environ["HOME"] = ordinary_home build() # 3. 恢复root权限执行install print("\n===== 运行install环节 =====") # 切换顺序必须先改UID再改GID os.seteuid(0) os.setegid(0) os.environ["HOME"] = root_home install() print("\n所有环节执行成功")
注意事项
- 权限切换顺序不能乱:降权时先改GID再改UID,升权时先改UID再改GID,否则会因为权限不足修改失败。
- 脚本自动同步了HOME环境变量,避免build环节生成的文件归属root,导致后续操作权限异常。
- 执行时仅需要运行一次命令即可:
sudo ./script.py,不需要传任何参数。
内容的提问来源于stack exchange,提问作者shouldbeq931
相关产品推荐
相关产品推荐

