我想使用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()
那么这种现象的潜在原因是什么? 谁能提出一些建议?谢谢!
答案 0 :(得分:1)
你正在混合你应该分开的东西。
首先,在远程端编写一个脚本,usr
(=您提供的用户名paramiko)可以执行,并且可以使用tcp_sender
正确启动sudo
而无需询问用于密码等。
在脚本中,使用sudo
nohup
nohup sudo ./tcp_sender
nohup确保新的子进程正确分离,以便在连接丢失/切断时保持活动状态。
当此脚本有效时,使用ssh.exec_command('script')
推理:使用shell和聪明的Python代码驱动shell可能就像你输入命令一样。但它总是很脆弱,难以测试 - 它是God object的变体。
相反,将您的问题分解为可以独立开发和测试的小而明显的问题。你有三个问题需要解决:
tcp_sender
本身。tcp_sender
所以使用三种不同的工具来解决它们。
答案 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命令)例子)。