Ubuntu下如何用Python以非root用户免密执行sudo命令
问题场景
我在开发一个Python项目时遇到了这样的问题:项目需要执行sudo命令查询MongoDB服务的运行状态,现有代码用sudo python3 app.py启动时能正常返回active(running)或inactive(dead)这类状态结果,但直接用python3 app.py运行时,终端会反复要求输入当前用户的密码,我希望实现免密执行这些特定的sudo命令。
我的现有代码如下:
cmd = "sudo service mongodb status > " + status_logs subprocess.call(cmd, shell=True) cmd = "grep \'" + search_tag + "\' " + status_logs status_string = str(subprocess.check_output(cmd, shell=True)) start = status_string.index(":") + len(":") end = status_string.index(')', start) status = status_string[start:end] status = status + ")" status = status.replace(" ", "")
附当前的/etc/sudoers文件内容:
This file MUST be edited with the 'visudo' command as root.Please consider adding local content in /etc/sudoers.d/ instead ofdirectly modifying this file.See the man page for details on how to write a sudoers file.Defaults env_reset
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin: /usr/bin:/sbin:/bin:/snap/bin"Host alias specificationUser alias specificationCmnd alias specificationUser privilege specificationroot ALL=(ALL:ALL) ALL
Members of the admin group may gain root privileges%admin ALL=(ALL) ALL
Allow members of group sudo to execute any command%sudo ALL=(ALL:ALL) ALL
See sudoers(5) for more information on "#include" directives:#includedir /etc/sudoers.d
解决方案:配置sudo免密权限
要实现免密执行service mongodb status命令,你需要修改sudo的权限配置,这里推荐两种安全的方式:
方式一:直接修改sudoers文件(需用visudo)
- 打开sudoers配置文件(必须用
visudo命令,它会做语法检查,避免配置错误导致sudo失效):sudo visudo - 在文件末尾添加一行内容,替换
your_username为你的实际用户名:your_username ALL=(ALL) NOPASSWD: /usr/sbin/service mongodb status注意:必须写
service命令的绝对路径,你可以用which service命令确认路径,通常是/usr/sbin/service。 - 保存并退出:按
Esc键,输入:wq后回车。
方式二:使用sudoers.d目录(更推荐,避免修改主配置文件)
sudo允许将自定义配置放在/etc/sudoers.d/目录下,这样不会污染主sudoers文件:
- 创建一个新的配置文件:
sudo touch /etc/sudoers.d/mongodb-status - 编辑这个文件:
sudo nano /etc/sudoers.d/mongodb-status - 在文件中添加同样的内容(替换
your_username):your_username ALL=(ALL) NOPASSWD: /usr/sbin/service mongodb status - 保存退出(按
Ctrl+O,回车,再按Ctrl+X),然后修改文件权限为sudo要求的0440:sudo chmod 0440 /etc/sudoers.d/mongodb-status
代码优化建议(可选)
你的现有代码使用了shell=True和临时日志文件,存在一定安全风险(如果search_tag来自外部输入可能引发命令注入),同时用字符串索引提取状态也不够健壮(服务输出格式变化时会出错)。推荐优化后的代码:
import subprocess import re def get_mongodb_status(): try: # 使用绝对路径的service命令,避免shell=True,直接捕获输出 result = subprocess.check_output( ['sudo', '/usr/sbin/service', 'mongodb', 'status'], stderr=subprocess.STDOUT, text=True ) # 用正则表达式匹配状态,更健壮 status_match = re.search(r':\s*(active\(running\)|inactive\(dead\))', result) if status_match: # 去除空格返回结果 return status_match.group(1).replace(" ", "") else: return "无法识别服务状态" except subprocess.CalledProcessError as e: return f"命令执行失败: {e.output.strip()}" # 调用示例 print(get_mongodb_status())
这个版本的代码更安全,也不需要临时文件,正则匹配的方式能更好地适应服务输出格式的微小变化。
内容的提问来源于stack exchange,提问作者S Andrew

