1

我想以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') 了,在日志中,它是空的。因为我的程序中有很多错误检查相关printftcp_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()

那么造成这种现象的潜在原因是什么?谁能给点建议?谢谢!

4

2 回答 2

1

您正在混合您可能应该分开的东西。

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

在脚本中,sudo使用以下命令作为后台进程启动nohup

nohup sudo ./tcp_sender

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

当此脚本工作时,使用启动新脚本ssh.exec_command('script')

推理:使用 shell 和驱动 shell 的聪明的 Python 代码可能会做你想做的事,就像你在输入命令一样。但它总是很脆弱,很难测试——它是上帝对象的变体。

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

  1. tcp_sender本身。
  2. 开始tcp_sender
  3. 远程启动它

所以使用三种不同的工具来解决它们。

于 2013-04-18T15:06:05.657 回答
0

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

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

这也与您的实际问题完全无关。

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

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

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] :从 xxxx 端口 56799 ssh2 2013-10-14T21:19:16+00:00 proc4-01-us1 sshd[29367] 接受 scott 的公钥:从 xxxx 端口 56804 ssh2 2013-10-14T21:19 接受 scott 的公钥: 21+00:00 proc4-01-us1 sshd[29519]:接受来自 xxxx 端口 56805 ssh2 的 scott 公钥

砰,你完成了。

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

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

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

除非在测试中,否则不要使用 NOPASSWD:ALL。明确指定允许的内容。

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

于 2013-10-15T17:12:38.993 回答