简体   繁体   English

将 sudo 与 Python 脚本一起使用

[英]Using sudo with Python script

I'm trying to write a small script to mount a VirtualBox shared folder each time I execute the script.每次执行脚本时,我都在尝试编写一个小脚本来挂载 VirtualBox 共享文件夹。 I want to do it with Python, because I'm trying to learn it for scripting.我想用 Python 来做,因为我正在尝试学习它来编写脚本。

The problem is that I need privileges to launch mount command.问题是我需要权限才能启动 mount 命令。 I could run the script as sudo, but I prefer it to make sudo by its own.我可以将脚本作为 sudo 运行,但我更喜欢它自己制作 sudo。

I already know that it is not safe to write your password into a.py file, but we are talking about a virtual machine that is not critical at all: I just want to click the.py script and get it working.我已经知道将密码写入 .py 文件是不安全的,但我们谈论的是一个根本不重要的虚拟机:我只想单击 .py 脚本并让它工作。

This is my attempt:这是我的尝试:

#!/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)

My python version is 2.6我的 python 版本是 2.6

Many answers focus on how to make your solution work, while very few suggest that your solution is a very bad approach .许多答案都集中在如何使您的解决方案起作用,而很少有人认为您的解决方案是一种非常糟糕的方法 If you really want to "practice to learn", why not practice using good solutions?如果你真的想“练习学习”,为什么不练习使用好的解决方案呢? Hardcoding your password is learning the wrong approach!硬编码您的密码正在学习错误的方法!

If what you really want is a password-less mount for that volume, maybe sudo isn't needed at all !如果你真的想要的是一个无密码的mount该卷,也许sudo是没有必要 So may I suggest other approaches?那么我可以建议其他方法吗?

  • Use /etc/fstab as mensi suggested.按照mensi的建议使用/etc/fstab Use options user and noauto to let regular users mount that volume.使用选项usernoauto让普通用户挂载该卷。

  • Use Polkit for passwordless actions: Configure a .policy file for your script with <allow_any>yes</allow_any> and drop at /usr/share/polkit-1/actions使用Polkit进行无密码操作:使用<allow_any>yes</allow_any>为您的脚本配置一个.policy文件, <allow_any>yes</allow_any> /usr/share/polkit-1/actions

  • Edit /etc/sudoers to allow your user to use sudo without typing your password.编辑/etc/sudoers以允许您的用户在不输入密码的情况下使用sudo As @Anders suggested, you can restrict such usage to specific commands, thus avoiding unlimited passwordless root priviledges in your account.正如@Anders 建议的那样,您可以将此类使用限制为特定命令,从而避免您的帐户中无限制的无密码 root 权限。 See this answer for more details on /etc/sudoers .有关/etc/sudoers更多详细信息,请参阅此答案

All the above allow passwordless root privilege, none require you to hardcode your password.以上所有都允许无密码 root 权限,不需要您对密码进行硬编码。 Choose any approach and I can explain it in more detail.选择任何方法,我可以更详细地解释它。

As for why it is a very bad idea to hardcode passwords, here are a few good links for further reading:至于为什么硬编码密码是一个非常糟糕的主意,这里有一些很好的链接供进一步阅读:

sudoPassword = 'mypass'
command = 'mount -t vboxsf myfolder /home/myuser/myfolder'
p = os.system('echo %s|sudo -S %s' % (sudoPassword, command))

Try this and let me know if it works.试试这个,让我知道它是否有效。 :-) :-)

And this one:还有这个:

os.popen("sudo -S %s"%(command), 'w').write('mypass')

To pass the password to sudo 's stdin:将密码传递给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 + '\n')[1]

Note: you could probably configure passwordless sudo or SUDO_ASKPASS command instead of hardcoding your password in the source code.注意:您可能可以配置无密码 sudo 或SUDO_ASKPASS命令,而不是在源代码中硬编码您的密码。

  • Use -S option in the sudo command which tells to read the password from 'stdin' instead of the terminal device.在 sudo 命令中使用 -S 选项,它告诉从“标准输入”而不是终端设备读取密码。

  • Tell Popen to read stdin from PIPE.告诉 Popen 从 PIPE 读取标准输入。

  • Send the Password to the stdin PIPE of the process by using it as an argument to communicate method.通过将密码用作通信方法的参数,将密码发送到进程的标准输入管道。 Do not forget to add a new line character, '\\n', at the end of the password.不要忘记在密码末尾添加一个新行字符“\\n”。

sp = Popen(cmd , shell=True, stdin=PIPE)
out, err = sp.communicate(_user_pass+'\n')   

subprocess.Popen creates a process and opens pipes and stuff. subprocess.Popen创建一个进程并打开管道和东西。 What you are doing is:你正在做的是:

  • Start a process sudo -S启动进程sudo -S
  • Start a process mypass启动进程mypass
  • Start a process mount -t vboxsf myfolder /home/myuser/myfolder启动进程mount -t vboxsf myfolder /home/myuser/myfolder

which is obviously not going to work.这显然是行不通的。 You need to pass the arguments to Popen.您需要将参数传递给 Popen。 If you look at its documentation , you will notice that the first argument is actually a list of the arguments.如果您查看其文档,您会注意到第一个参数实际上是一个参数列表。

I used this for python 3.5.我将它用于 python 3.5。 I did it using subprocess module.Using the password like this is very insecure .我是使用进程模块做到的。使用这样的密码是非常不安全的

The subprocess module takes command as a list of strings so either create a list beforehand using split() or pass the whole list later. subprocess模块将命令作为字符串列表,因此可以使用split()预先创建一个列表,或者稍后传递整个列表。 Read the documentation for moreinformation.阅读文档以获取更多信息。

#!/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()

有时需要回车:

os.popen("sudo -S %s"%(command), 'w').write('mypass\n')

Please try module pexpect.请尝试模块 pexpect。 Here is my code:这是我的代码:

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\n')
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 !")

To limit what you run as sudo, you could run要限制您以 sudo 身份运行的内容,您可以运行

python non_sudo_stuff.py
sudo -E python -c "import os; os.system('sudo echo 1')"

without needing to store the password.无需存储密码。 The -E parameter passes your current user's env to the process. -E参数将您当前用户的 env 传递给进程。 Note that your shell will have sudo priveleges after the second command, so use with caution!请注意,您的 shell 在第二个命令之后将具有 sudo 特权,因此请谨慎使用!

I know it is always preferred not to hardcode the sudo password in the script.我知道最好不要在脚本中对 sudo 密码进行硬编码。 However, for some reason, if you have no permission to modify /etc/sudoers or change file owner, Pexpect is a feasible alternative.但是,由于某种原因,如果您没有修改/etc/sudoers或更改文件所有者的权限,Pexpect 是一个可行的选择。

Here is a Python function sudo_exec for your reference:这是一个 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

It works in python 2.7 and 3.8:它适用于 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 +'\n').encode()) # write as bytes
proc.stdin.flush() # need if not bufsize=0 (unbuffered stdin)

without .flush() password will not reach sudo if stdin buffered.如果标准输入缓冲,没有.flush()密码将不会到达sudo In python 2.7 Popen by default used bufsize=0 and stdin.flush() was not needed.在python 2.7 Popen默认使用bufsize=0stdin.flush()是没有必要的。

For secure using, create password file in protected directory:为了安全使用,在受保护的目录中创建密码文件:

mkdir --mode=700 ~/.prot_dir
nano ~/.prot_dir/passwd.txt
chmod 600 ~/.prot_dir/passwd.txt 

at start your py-script read password from ~/.prot_dir/passwd.txt从 ~/.prot_dir/passwd.txt 开始你的 py-script 读取密码

with open(os.environ['HOME'] +'/.prot_dir/passwd.txt') as f:
    password = f.readline().rstrip()
import os 
os.system("echo TYPE_YOUR_PASSWORD_HERE | sudo -S TYPE_YOUR_LINUX_COMMAND")

Open your ide and run the above code.打开您的 ide 并运行上述代码。 Please change TYPE_YOUR_PASSWORD_HERE and TYPE_YOUR_LINUX_COMMAND to your linux admin password and your desired linux command after that run your python script.请将 TYPE_YOUR_PASSWORD_HERE 和 TYPE_YOUR_LINUX_COMMAND 更改为您的 linux 管理员密码和您想要的 linux 命令,然后运行您的 python 脚本。 Your output will show on terminal.您的 output 将显示在终端上。 Happy Coding:)快乐编码:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM