2013-04-18 128 views
1

我要远程EXCUTE程序tcp_sender与根特权 运行远程程序与根特权,下面的函数是用于制造ssh连接使用的paramiko 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') 并且在日志中,它是空的。因为我在我的tcp_sender程序中有很多与错误检查相关的printf,我认为日志中应该有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() 

那么这种现象的潜在原因是什么? 任何人都可以给一些建议?谢谢!

回答

1

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

首先,在远程端编写一个脚本,usr(=你给paramiko用户名)可以执行并可以使用sudo正常启动tcp_sender不要求输入密码,等等。

在脚本中,使用nohup开始sudo作为后台进程:

nohup sudo ./tcp_sender 

的nohup可确保新的子进程适当分离,使其保持活着的时候连接丢失/剪切。

当这个脚本工作,使用ssh.exec_command('script')

推理启动新的脚本:这或许可以做到你想用一个外壳和巧妙的Python代码驱动外壳,就好像你键入的命令是什么。但它总是很脆弱,很难测试 - 它是God object的变体。

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

  1. tcp_sender本身。
  2. 开始tcp_sender
  3. 开始进行远程

所以用三个不同的工具来解决这些问题。

+0

谢谢你,我遵循你的程序,但'raise_on_stderr(ssh.exec_command(“bash tcp_exec.sh”))',我得到的经典错误'异常:sudo:没有tty存在并且没有指定askpass程序' – misteryes

+0

正如我所说:你必须配置你的系统,使得'sudo'不需要输入密码。我的猜测是,对于你的测试,'sudo'在本地缓存密码。 /www.cyberciti.biz/tips/allow-a-normal-user-to-run-commands-as-root.html –

+0

它不需要'sudo'的密码,问题是'tty'。 – misteryes

0

与其他评论一致 - 你正在混合可以分开解决的问题。许多人(包括我自己)都犯了这个错误。

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

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

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

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

1)我的测试用户是 “斯科特”,和组的“ADM见的/ etc /基团和/ etc/passwd的基本东西一个构件

2)的/ etc/sudoers的:。 %ADM ALL =(ALL)NOPASSWD:/ bin中/ grep的

3)从远程系统,我运行: $ SSH斯科特@服务器B “的grep须藤接受/var/log/auth.log” 2013-10 -14T21:17:54 + 00:00 proc4-01-us1 sshd [28873]:从xxxx端口接收publickey scott from xxxx端口56799 ssh2 2013-10-14T21:19:16 + 00:00 proc4-01-us1 sshd [ 29367]:接受publickey for scott from xxxx端口56804 ssh2 2013-10 -14T21:19:21 + 00:00 proc4-01-us1 sshd [29519]:从x.x.x.x端口接收publickey for scx xx.x 56805 ssh2

Bang,你完成了。

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

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

DO考虑安全性。如果您将此权限附加到特殊用户,您并不是真的在减少安全性。当然,我描述的方法比将sudo密码硬编码到脚本更安全。

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

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