使用paramiko运行Sudo命令

12

我正在尝试使用python-paramiko在远程机器上执行一个sudo命令。

我尝试了以下代码:

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('192.168.0.104', username='cdc',password='123456')
stdin, stdout, stderr = ssh.exec_command("sudo dmesg")
stdin.write("123456\n")
stdin.flush()
print stdout.readlines()
ssh.close()

我本来以为这会将所有三个流绑定在一起,并使用输入流将密码传递给sudo。但是它并没有起作用,而是出现了以下错误:
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/paramiko/file.py", line 314, in write
    self._write_all(data)
  File "/usr/local/lib/python2.7/dist-packages/paramiko/file.py", line 439, in _write_all
    count = self._write(data)
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 1263,in _write
    self.channel.sendall(data)
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 796, in sendall
    raise socket.error('Socket is closed')
  error: Socket is closed

有什么问题,我该如何解决?

请查看我的回答 https://dev59.com/H2025IYBdhLWcg3wAg_p#28011904 - AlexS
5个回答

17
首先,在控制台中尝试使用ssh cdc@192.168.0.104 "sudo -S -p '' dmesg"。如果仍然失败,则可能需要检查sshd设置和sudoer设置。
如果它正常工作,请在行之间添加一些echo,这样我们就可以确切地知道何时抛出异常。我非常怀疑您是否应将sudo dmesg更改为sudo -S -p '' dmesg
您还可以尝试使用我的paramiko包装器。我可以顺畅地访问任何CentOS/SuSE节点并执行任何命令(具有/不具有sudo权限):
#!/usr/bin/python

from StringIO import StringIO
import paramiko 

class SshClient:
    "A wrapper of paramiko.SSHClient"
    TIMEOUT = 4

    def __init__(self, host, port, username, password, key=None, passphrase=None):
        self.username = username
        self.password = password
        self.client = paramiko.SSHClient()
        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        if key is not None:
            key = paramiko.RSAKey.from_private_key(StringIO(key), password=passphrase)
        self.client.connect(host, port, username=username, password=password, pkey=key, timeout=self.TIMEOUT)

    def close(self):
        if self.client is not None:
            self.client.close()
            self.client = None

    def execute(self, command, sudo=False):
        feed_password = False
        if sudo and self.username != "root":
            command = "sudo -S -p '' %s" % command
            feed_password = self.password is not None and len(self.password) > 0
        stdin, stdout, stderr = self.client.exec_command(command)
        if feed_password:
            stdin.write(self.password + "\n")
            stdin.flush()
        return {'out': stdout.readlines(), 
                'err': stderr.readlines(),
                'retval': stdout.channel.recv_exit_status()}

if __name__ == "__main__":
    client = SshClient(host='host', port=22, username='username', password='password') 
    try:
       ret = client.execute('dmesg', sudo=True)
       print "  ".join(ret["out"]), "  E ".join(ret["err"]), ret["retval"]
    finally:
      client.close() 

我想看到一个关于绕过更改sshd设置的sudo要求的解决方案。换句话说,假设我没有访问服务器sshd设置的权限,我该如何运行sudo命令? - rfportilla
@rfportilla 我的解决方案根本不需要更改sshd设置。使用我的代码,您可以像在本地一样运行任何shell命令。 - stanleyxu2005
如果你遇到这种情况,你可能需要检查sshd设置和sudoer设置。我认为常见的错误是ssh远程无法运行sudo,因为需要tty终端,会出现如下提示:sudo: "sorry, you must have a tty to run sudo." 你可以修改sudoers文件来解决此问题,但并非总是可行的。从客户端角度来看,你的解决方案并没有帮助到这个问题。我可能过早地将它评为反对,但现在我无法撤回,除非对答案进行更改(即使是微小的更改)。抱歉。 - rfportilla
@rfportilla 不用介意被踩了一下。你说得对,从客户端的角度来看,我的解决方案仍然需要一个 tty。换句话说,它并没有通过魔法提升权限,而是修复了原始问题中错误的命令使用。 - stanleyxu2005

11

很抱歉我没有时间提供详细的答案,但我已经成功地使用这个建议在paramiko上实现了sudo命令。

#!/usr/bin/env python
import paramiko
l_password = "yourpassword"
l_host = "yourhost"
l_user = "yourusername"
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(l_host, username=l_user, password=l_password)    
transport = ssh.get_transport()
session = transport.open_session()
session.set_combine_stderr(True)
session.get_pty()
#for testing purposes we want to force sudo to always to ask for password. because of that we use "-k" key
session.exec_command("sudo -k dmesg")
stdin = session.makefile('wb', -1)
stdout = session.makefile('rb', -1)
#you have to check if you really need to send password here 
stdin.write(l_password +'\n')
stdin.flush()
for line in stdout.read().splitlines():        
    print 'host: %s: %s' % (l_host, line)

使用这种方法,有没有办法在不解析输出的情况下获取执行命令的返回值? - Jesus Gomez

4

我知道这个问题有点老了,但我也想同时使用sudo和paramiko。我花了一些时间才找到这个解决方案。它可能不适用于每个人,但我认为值得添加。

def ssh_handler(hostname, username=USER, password=PASS, command=CMD): 
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname,
                username=username,
                password=password) 
    # set get_pty to True to get a pseudo terminal
    stdin, stdout, stderr = ssh.exec_command(prepare_command(command), get_pty=True)
    # write and flush the password to the remote terminal
    # note that the password will also be written to the terminal from where the script is executed.
    stdin.write(password+'\n')
    stdin.flush()

    response = stdout.read()   
    ssh.close()
    print(response)
             
     
def prepare_command(command):  
    if (not isinstance(command, basestring)): 
        command = ' ; '.join(command)  
    command = command.replace('"','\"') 
    command = 'sudo -s -- " '+command+' " \n'
    return command


# kind of a dumb example but you get the point 
mycmd = []; 
mycmd.append('cd /dir/this/user/doesnt/have/access/to')
mycmd.append('ls -las')
mycmd.append('cat file_in_dir.txt')

ssh_handler(server, command=mycmd)

这行代码的意义是什么? command = command.replace('"','\"') 为什么需要在双引号前面加上反斜杠? - jxmorris12
@jxmorris12 - 在字符串中转义了双引号。我大约5年前写的,所以不是完全确定,但如果你看以下这行代码,'sudo -s -- " '+command+' " \n',你会发现整个命令都被放在双引号之间传递给sudo。转义它们将允许处理...直接在cli中,例如 sudo -s -- " randomCommand --arg=\"pablo\" "(可能不会使用那些填充空格,但在这里更好地显示引号)。 - gloomy.penguin
这如何解决提供sudo密码的问题? - Murphy Meng
这个怎么可能不提供密码就能工作呢?# stdin.write(password+'\n') 我对这行代码感到困惑。 - Rahul Shaw
更新了答案,以在运行命令时提供sudo密码。 - Debargha Roy

0

-1

当我结合上述两个建议时,它对我起作用了:

  1. 将"-S -p ''"放入sudo命令中:"sudo -S -p '' dmesg"
  2. 在标准输入上设置密码: stdin.write(password+'\n') stdin.flush()

请阅读[答案]并记住这不是一个讨论论坛 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接