将 sudo 与 Python 脚本结合使用
- 2025-01-07 08:44:00
- admin 原创
- 146
问题描述:
我正在尝试编写一个小脚本,以便每次执行该脚本时挂载 VirtualBox 共享文件夹。我想用 Python 来做这件事,因为我正在尝试学习它来编写脚本。
问题是我需要权限才能启动 mount 命令。我可以用 sudo 运行该脚本,但我更喜欢自己使用 sudo。
我已经知道将密码写入.py 文件并不安全,但我们正在讨论的是一个根本不重要的虚拟机:我只想单击.py 脚本并使其运行。
这是我的尝试:
#!/usr/bin/env python
import subprocess
sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'
subprocess.Popen('sudo -S' , shell=True,stdout=subprocess.PIPE)
subprocess.Popen(sudoPassword , shell=True,stdout=subprocess.PIPE)
subprocess.Popen(command , shell=True,stdout=subprocess.PIPE)
我的python版本是2.6
解决方案 1:
许多答案都集中在如何使您的解决方案有效,而很少有人认为您的解决方案是一种非常糟糕的方法。如果您真的想“实践学习”,为什么不练习使用好的解决方案呢?硬编码您的密码是学习错误的方法!
mount
如果您真正想要的是该卷的无密码,那么可能根本sudo
不需要!那么我可以建议其他方法吗?
/etc/fstab
按照mensi 的建议使用。使用选项user
和noauto
让普通用户安装该卷。用于无密码操作:使用以下命令为您的脚本
Polkit
配置文件并放入.policy
`<allow_any>yes</allow_any>`/usr/share/polkit-1/actions
编辑
/etc/sudoers
以允许您的用户使用sudo
而无需输入密码。正如@Anders所建议的,您可以将此类使用限制为特定命令,从而避免您的帐户中存在无密码的无限根权限。有关 的更多详细信息,请参阅此答案/etc/sudoers
。
以上所有方法都允许无密码 root 权限,无需您硬编码密码。选择任何一种方法,我都可以更详细地解释。
至于为什么硬编码密码是一个非常糟糕的想法,下面有几个不错的链接可供进一步阅读:
为什么编程时不应该硬编码密码
如何保守秘密(硬编码密码的替代方案)
哪种方式更安全?硬编码凭证还是将其存储在数据库中?
使用硬编码凭证是一种危险的编程错误:CWE
硬编码密码仍然是一个关键的安全漏洞
解决方案 2:
sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'
p = os.system('echo %s|sudo -S %s' % (sudoPassword, command))
尝试一下并告诉我是否有效。:-)
还有这个:
os.popen("sudo -S %s"%(command), 'w').write('mypass')
解决方案 3:
将密码传递给sudo
的标准输入:
#!/usr/bin/env python
from subprocess import Popen, PIPE
sudo_password = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'.split()
p = Popen(['sudo', '-S'] + command, stdin=PIPE, stderr=PIPE,
universal_newlines=True)
sudo_prompt = p.communicate(sudo_password + '
')[1]
注意:您可能可以配置无密码的 sudo 或SUDO_ASKPASS
命令,而不是在源代码中硬编码您的密码。
解决方案 4:
在 sudo 命令中使用 -S 选项,它告诉从“stdin”而不是终端设备读取密码。
告诉 Popen 从 PIPE 读取 stdin。
将密码作为参数发送到通信方法,发送到进程的 stdin PIPE。不要忘记在密码末尾添加换行符“\n”。
sp = Popen(cmd , shell=True, stdin=PIPE)
out, err = sp.communicate(_user_pass+'
')
解决方案 5:
subprocess.Popen
创建一个进程并打开管道等。您要做的就是:
启动进程
sudo -S
启动进程
mypass
启动进程
mount -t vboxsf myfolder /home/myuser/myfolder
这显然行不通。您需要将参数传递给 Popen。如果您查看其文档,您会注意到第一个参数实际上是参数列表。
解决方案 6:
我在 python 3.5 中用过这个。我使用subprocess模块来实现。像这样使用密码非常不安全。
subprocess模块将命令作为字符串列表,因此可以使用split()预先创建列表,或稍后传递整个列表。阅读文档了解更多信息。
#!/usr/bin/env python
import subprocess
sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'.split()
cmd1 = subprocess.Popen(['echo',sudoPassword], stdout=subprocess.PIPE)
cmd2 = subprocess.Popen(['sudo','-S'] + command, stdin=cmd1.stdout, stdout=subprocess.PIPE)
output = cmd2.stdout.read.decode()
解决方案 7:
有时需要回车:
os.popen("sudo -S %s"%(command), 'w').write('mypass
')
解决方案 8:
请尝试模块 pexpect。这是我的代码:
import pexpect
remove = pexpect.spawn('sudo dpkg --purge mytool.deb')
remove.logfile = open('log/expect-uninstall-deb.log', 'w')
remove.logfile.write('try to dpkg --purge mytool
')
if remove.expect(['(?i)password.*']) == 0:
# print "successfull"
remove.sendline('mypassword')
time.sleep(2)
remove.expect(pexpect.EOF,5)
else:
raise AssertionError("Fail to Uninstall deb package !")
解决方案 9:
要限制以 sudo 身份运行的内容,您可以运行
python non_sudo_stuff.py
sudo -E python -c "import os; os.system('sudo echo 1')"
无需存储密码。该-E
参数将您当前用户的环境传递给进程。请注意,您的 shell 在第二个命令后将具有 sudo 权限,因此请谨慎使用!
解决方案 10:
import os
os.system("echo TYPE_YOUR_PASSWORD_HERE | sudo -S TYPE_YOUR_LINUX_COMMAND")
打开您的 ide 并运行上述代码。请将 TYPE_YOUR_PASSWORD_HERE 和 TYPE_YOUR_LINUX_COMMAND 更改为您的 Linux 管理员密码和所需的 Linux 命令,然后运行您的 Python 脚本。您的输出将显示在终端上。祝您编码愉快 :)
解决方案 11:
我知道最好不要在脚本中硬编码 sudo 密码。但是,出于某种原因,如果您没有权限修改/etc/sudoers
或更改文件所有者,Pexpect 是一个可行的替代方案。
以下是一个 Python 函数sudo_exec
供您参考:
import platform, os, logging
import subprocess, pexpect
log = logging.getLogger(__name__)
def sudo_exec(cmdline, passwd):
osname = platform.system()
if osname == 'Linux':
prompt = r'[sudo] password for %s: ' % os.environ['USER']
elif osname == 'Darwin':
prompt = 'Password:'
else:
assert False, osname
child = pexpect.spawn(cmdline)
idx = child.expect([prompt, pexpect.EOF], 3)
if idx == 0: # if prompted for the sudo password
log.debug('sudo password was asked.')
child.sendline(passwd)
child.expect(pexpect.EOF)
return child.before
解决方案 12:
它适用于python 2.7和3.8:
from subprocess import Popen, PIPE
from shlex import split
proc = Popen(split('sudo -S %s' % command), bufsize=0, stdout=PIPE, stdin=PIPE, stderr=PIPE)
proc.stdin.write((password +'
').encode()) # write as bytes
proc.stdin.flush() # need if not bufsize=0 (unbuffered stdin)
如果 stdin 缓冲,则没有.flush()
密码将无法到达sudo
。在 python 2.7 中Popen
默认使用bufsize=0
并且stdin.flush()
不需要。
为了安全使用,在受保护的目录中创建密码文件:
mkdir --mode=700 ~/.prot_dir
nano ~/.prot_dir/passwd.txt
chmod 600 ~/.prot_dir/passwd.txt
在启动你的 py 脚本时从 ~/.prot_dir/passwd.txt 读取密码
with open(os.environ['HOME'] +'/.prot_dir/passwd.txt') as f:
password = f.readline().rstrip()
解决方案 13:
您可以使用SSHScript。以下是示例代码:
## filename: example.spy
sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'
$$echo @{sudoPassword} | sudo -S @{command}
或者,只需一行(几乎与在控制台上运行相同)
## filename: example.spy
$$echo mypass | sudo -S mount -t vboxsf myfolder /home/myuser/myfolder
然后在控制台上运行它
sshscript example.spy
其中“sshscript”是 SSHScript 的 CLI(由 pip 安装)。
解决方案 14:
我正在采用的解决方案是,因为开发电脑上环境文件中纯文本格式的密码是可以的,并且 repo 和 gitlab 运行器中的变量被屏蔽了。
使用 .dotenv 将传递到本地机器上的 .env,不要将 .env 提交到 git。在 gitlab 变量中添加相同的变量
.env 文件包含:
PASSWORD=superpass
from dotenv import load_dotenv
load_dotenv()
subprocess.run(f'echo {os.getenv("PASSWORD")} | sudo -S rm /home//folder/filetodelete_created_as_root.txt', shell=True, check=True)
这在本地和 gitlab 中均可行。无需向 repo 提交明文密码。
是的,您可以争辩说运行 sudo 命令 w shell true 有点疯狂,但如果您有从 docker w root 写入主机的文件,并且您需要以编程方式删除它们,那么这是可行的。