使用paramiko ssh通道以root权限运行远程程序

时间:2013-04-18 14:18:00

标签: linux ssh paramiko

我想使用root权限远程执行程序tcp_sender ,以下函数用于进行ssh连接

    def connect(hostname):
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(hostname, username='usr', pkey=paramiko.RSAKey.from_private_key(open('id_rsa'), 'psw'), timeout = 240.0)
            return ssh

然后我有3个解决方案:

解决方案 A)

    ssh = connect(hostname)
    chan = ssh.invoke_shell()
    chan.send('sudo ./tcp_sender\n')

使用此解决方案,远程tcp_sender未执行,我使用ps -ef|grep "tcp_sender"检查,没有进程

我试过chan.send('sudo ./tcp_sender > log 2>&1\n') 在日志中,它说:

sudo: no tty present and no askpass program specified

解决方案 B)

    ssh = connect(hostname)
    (stdin, stdout, stderr) = ssh.exec_command("[ -f tcp_sender ]  && echo 1 || echo 0")
    res = stdout.readlines()
    print hostname,res[0]
    if res[0] == '0\n':
            UnusedHostFile.write(hostname+'no tcp_sender exists\n')
    else:
            chan = ssh.invoke_shell()
            chan.send("sudo chmod 777 tcp_sender\n")
            # if a tcp_sender is runnning, kill it
            chan.send('x=`ps -ef|grep "tcp_sender"|grep -v "grep"|awk \'{print $2}\'`; [ -n "${x}" ] && sudo kill -9 $x\n')
            time.sleep(4)
            while not chan.recv_ready():
                    time.sleep(1)
            buf = ''
            buf +=chan.recv(9999)
            print buf
            chan.send('sudo ./tcp_sender\n')

使用此解决方案,我只是添加一些不相关的行,然后远程tcp_sender正在运行,如:

bash-4.0# ps -ef|grep "sender"
root      9348  9325  0 Apr07 ?        00:00:00 sudo ./tcp_sender
root      9349  9348  0 Apr07 ?        00:00:00 ./tcp_sender

但是,它无法正常运行(如预期的那样)。在tcp_sender中,有一个fork(),也许是由于这个原因?

我试过chan.send('sudo ./tcp_sender > log 2>&1\n') 在日志中,它是空的。因为我的printf程序中有许多与错误检查相关的tcp_sender,我认为日志中应该有printf个结果,但它是空的。

此外,我注意到一种现象,如果我kill -9 9348,所有这两个过程都结束了。 但是对于下一个解决方案C,过程9349将被移交给系统init进程1。

解决方案 C):

使用此解决方案,我可以正确运行远程tcp_sender。但是python脚本将被远程程序阻止,直到它退出。我不希望我的脚本等待远程退出。

    log = open('log','a+')
    ssh = connect(hostname)
    (stdin, stdout, stderr) = ssh.exec_command("[ -f tcp_sender ] && echo 1 || echo 0")
    res = stdout.readlines()
    print hostname,res[0]
    if res[0] == '0\n':
            UnusedHostFile.write(hostname+"tcp_sender doesn't exists\n")
    else:
            chan = ssh.invoke_shell()
            chan.send("sudo chmod 777 tcp_sender\n")
            chan.send('x=`ps -ef|grep "tcp_sender"|grep -v "grep"|awk \'{print $2}\'`; [ -n "${x}" ] && sudo kill -9 $x\n')
            time.sleep(4)
            while not chan.recv_ready():
                    time.sleep(1)
            buf = ''
            buf +=chan.recv(9999)
            print buf
            chan.send('sudo ./tcp_sender\n')
            #chan.send('sudo whoami\n')
            time.sleep(2)
            (stdin, stdout, stderr) = ssh.exec_command("ps -ef|grep 'tcp_sender'|grep -v 'grep'|wc -l")
            res = stdout.readlines()
            while res[0].strip() != '0':
                    time.sleep(3)
                    (stdin, stdout, stderr) = ssh.exec_command("ps -ef|grep 'tcp_sender'|grep -v 'grep'|wc -l")
                    res = stdout.readlines()
                    print res[0].strip()
            while not chan.recv_ready():
                    time.slepp(1)
            buf = ''
            buf += chan.recv(9999)
            log.write(hostname+': '+''.join(str(elem) for elem in buf)+'\n\n')
    log.close()

那么这种现象的潜在原因是什么? 谁能提出一些建议?谢谢!

2 个答案:

答案 0 :(得分:1)

你正在混合你应该分开的东西。

首先,在远程端编写一个脚本,usr(=您提供的用户名paramiko)可以执行,并且可以使用tcp_sender正确启动sudo而无需询问用于密码等。

在脚本中,使用sudo

作为后台进程启动nohup
nohup sudo ./tcp_sender

nohup确保新的子进程正确分离,以便在连接丢失/切断时保持活动状态。

当此脚本有效时,使用ssh.exec_command('script')

启动新脚本

推理:使用shell和聪明的Python代码驱动shell可能就像你输入命令一样。但它总是很脆弱,难以测试 - 它是God object的变体。

相反,将您的问题分解为可以独立开发和测试的小而明显的问题。你有三个问题需要解决:

  1. tcp_sender本身。
  2. 正在开始tcp_sender
  3. 远程启动
  4. 所以使用三种不同的工具来解决它们。

答案 1 :(得分:0)

同意其他评论 - 您正在混合可以单独解决的问题。许多人(包括我自己)都犯了这个错误。

Paramiko功能强大且智能:它可以导航并响应SSH用户名和密码提示。但这是一个特例。大多数情况下,当你需要在Paramiko中响应PROMPT时,你基本上等待然后在假定的提示下拍摄文本。这太乱了。

这也与你的真实问题毫无关系。

您要做的是编辑/ etc / sudoers文件,以便您的自动化测试用户或组能够使用NOPASSWD运行您想要的精确命令。

假设我想在主机“ServerB”上远程grep /var/log/auth.log。虽然grep可以由任何用户运行,但是已知此系统上的auth.log只能由root用户读取。 SO:

1)我的测试用户是“scott”,组成员为“adm。请参阅/ etc / groups和/ etc / passwd。基本内容。

2)/ etc / sudoers: %adm ALL =(ALL)NOPASSWD:/ bin / grep

3)从远程系统运行: $ ssh scott @ ServerB“sudo grep Accepted /var/log/auth.log” 2013-10-14T21:17:54 + 00:00 proc4-01-us1 sshd [28873]:从x.x.x.x端口接受scott的公钥56799 ssh2 2013-10-14T21:19:16 + 00:00 proc4-01-us1 sshd [29367]:从x.x.x.x端口56804 ssh2接受scott的公钥 2013-10-14T21:19:21 + 00:00 proc4-01-us1 sshd [29519]:来自x.x.x.x端口56805 ssh2的scott的公接密钥

砰,你已经完成了。

NOTES 请使用您在sudoers中指定的脚本的绝对文件系统路径。

请使用SSH密钥。如果您愿意,可以使用密钥+密码。 (请记住,Paramiko可以回答登录提示)但这也意味着在密码中存储密码......

请考虑安全性。如果您将此权限附加到特殊用户,那么您在这里并没有真正减少安全性。当然,我描述的方法比将sudo密码硬编码到脚本中更安全。

除测试外,请勿使用NOPASSWD:ALL。明确指定允许的内容。

请考虑为这些用户可以运行的内容添加限制。例如,如果我总是从EC2框运行此测试,我只允许该用户从该EC2 IP连接。对话方面,我可以通过在“授权”键中添加前缀来限制用户可以运行哪些命令“(即,如果我想避免运行rsync服务器全部时间,则限制该用户只能运行rsync命令)例子)。