将pipe命令和sudo与Python子流程组合时出现问题
我正试图利用Python模块将pipe命令和sudo与Python子流程组合时出现问题,python,subprocess,Python,Subprocess,我正试图利用Python模块子流程在Mac上自动执行终端命令。具体来说,我正在运行一个在我的机器上创建的。但是,该命令需要root权限和管道: echo " rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080 " | sudo pfctl -ef - 为了通过子流程将我的根密码传递给shell命令,我按照下面的代码示例创建了一个脚本: from subprocess import PIPE, Po
子流程
在Mac上自动执行终端命令。具体来说,我正在运行一个在我的机器上创建的。但是,该命令需要root权限和管道:
echo "
rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080
" | sudo pfctl -ef -
为了通过子流程
将我的根密码传递给shell命令,我按照下面的代码示例创建了一个脚本:
from subprocess import PIPE, Popen
p = Popen(['echo', '"rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080"\n'], stdin=PIPE, stderr=PIPE, universal_newlines=True)
p2 = Popen(['sudo', '-S']+['pfctl', '-ef', '-'], stdin=p.stdout, stderr=PIPE, universal_newlines=True)
return p2.communicate('my_root_password\n')[1]
请注意,为了实现echo
输出到命令pfctl-ef-
的管道,我创建了两个Popen
对象,并将第一个对象的stdout
传递给第二个对象的stdin
参数,如,我使用Popen.communicate
将root密码写入stdin
但是,上面的脚本不起作用,因为终端仍然提示我输入根密码。奇怪的是,当使用不带管道的命令时,例如运行sudo pfctl-s nat
(显示当前端口映射设置),我能够成功地将根密码写入stdin:
上面的代码可以工作,因为映射配置显示时没有任何密码提示
如何更改我的第一个Python脚本,以便在使用Popen.communicate
将密码写入stdin后,不会提示我输入根密码
我在macOS Sierra 10.12.5上运行此代码,我认为这只是管道未正确连接的简单情况。您没有为第一个进程的
stdout
指定管道,因此从外观上看,输出只是打印到终端,然后进程完成
当第二个进程启动时,它将提示输入密码,并且据我所知,将正确接收密码。但是,communicate
方法随后关闭输入并等待过程完成。据我所知,第一个进程的输出永远不会到达第二个进程,这就是脚本无法工作的原因。与其创建一个单独的echo
过程,为什么不通过communicate
发送所需的所有文本数据呢
另一个问题(我没有MAC要检查)是sudo
直接将提示打印到终端(即通过/dev/tty
而不是stdout
)。在我的sudo
(在Debian上)版本中,添加-S
选项会使其将提示打印到stderr
。但是,在MAC上,-S
选项似乎无法实现这一点。请尝试使用-p'
禁用提示
把所有东西放在一起,这应该是可行的:
from subprocess import PIPE, Popen
from getpass import getpass
password = getpass()
cmd = ['sudo', '-k', '-S', '-p', '', 'pfctl', '-ef', '-']
p = Popen(cmd, stdin=PIPE, stderr=PIPE, universal_newlines=True)
text = password + '\n'
text += 'rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080\n'
p.communicate(text)
安全说明
此答案已更新为不使用纯文本密码。请参阅下面的评论,以了解为什么这是一个坏主意的好例子!还要注意的是,使用Python在内存中存储密码并不是完全安全的,就像内存被交换到磁盘一样,这将包括明文形式的密码。对于较低级别的语言,
mlock
系统调用将用于防止任何包含密码的内存被交换。不是答案,但为什么不删除sudo并以root用户身份运行脚本?@mVChr这是个好主意,但我实际上计划将其作为一个程序的一部分运行,该程序查询用户密码以进行更改,在本例中,更新端口映射规则。sudo
从stdin
读取您的密码,您的stdin
连接到其他进程stdout
,sudo
无法读取您的密码。@这很有道理。我有没有办法解决这个问题。用户必须在sudoers文件中。或2。使用子流程。致电非常感谢!但是,这样做是可行的,它只在每次在新的终端窗口中运行代码时更新配置文件。你知道为什么会这样吗?我假设这只是pfctl
的一个功能,但是,Mac端口管理器可能与此有关吗?啊,可能是因为sudo
在第二轮不需要密码,所以密码会被发送到pfctl
。尝试将-k
选项添加到sudo
,这将确保它始终要求输入密码。再次感谢!现在可以了!一旦系统允许我(在21小时内)就会奖励我。
from subprocess import PIPE, Popen
from getpass import getpass
password = getpass()
cmd = ['sudo', '-k', '-S', '-p', '', 'pfctl', '-ef', '-']
p = Popen(cmd, stdin=PIPE, stderr=PIPE, universal_newlines=True)
text = password + '\n'
text += 'rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080\n'
p.communicate(text)