1

I want to remotely excute a program tcp_sender with root priviledge ,the following function is for making a ssh connection

    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

then I have 3 solutions:

solution A)

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

with this solution, the remote tcp_sender is not executed, I checked using ps -ef|grep "tcp_sender", there is no process

I tried chan.send('sudo ./tcp_sender > log 2>&1\n') and in the log, it says:

sudo: no tty present and no askpass program specified

solution 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')

with this solution, I just add some un-relevant lines, then the remote tcp_sender is running, something like:

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

however, it can't run normally(as expected). In the tcp_sender, there is a fork(), maybe it is due to this?

I tried chan.send('sudo ./tcp_sender > log 2>&1\n') and in the log, it is empty. Because I have many error-checking related printf in my tcp_sender program, I think there should be printf results in the log, but it is empty.

In addition, I noticed a phenomenon, if I kill -9 9348, all these two processes are ended. But for the next solution C, the process 9349 will be handed over to system init process 1.

Solution C):

with this solution, I can run the remote tcp_sender correctly. But the python script will be blocked by the remote program until it exits. I don't want my script to wait that the remote exits.

    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()

so what are potential reasons for this phenomenon? can anyone give some advice? thanks!

misteryes
  • 2,167
  • 4
  • 32
  • 58

2 Answers2

1

You're mixing things that you should probably keep separate.

First, write a script on the remote side that usr (= username that you give paramiko) can execute and which can correctly start tcp_sender using sudo without asking for a password, etc.

In the script, start sudo as background process using nohup:

nohup sudo ./tcp_sender

nohup makes sure that the new child process is properly detached so it stays alive when the connection is lost/cut.

When this script works, start the new script using ssh.exec_command('script')

Reasoning: It's probably possible to do what you want using a shell and clever Python code that drives the shell as if you were typing the commands. But it will always be brittle, hard to test - it's a variant of the God object.

Instead, split your problem into small, distinct problems that you can develop and test independently. You have three problems to solve:

  1. tcp_sender itself.
  2. Starting tcp_sender
  3. Starting it remotely

so use three distinct tools to solve them.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • thank you, I followed your procedures, but `raise_on_stderr(ssh.exec_command("bash tcp_exec.sh"))`, I get classic error `Exception: sudo: no tty present and no askpass program specified` – misteryes Apr 18 '13 at 15:56
  • As I said: You must configure your system in such a way that `sudo` doesn't have to ask for a password. My guess is that for your tests, `sudo` locally caches the password. See here: http://www.cyberciti.biz/tips/allow-a-normal-user-to-run-commands-as-root.html – Aaron Digulla Apr 18 '13 at 16:05
  • it doesn't need a password for `sudo`, the problem is `tty`. – misteryes Apr 18 '13 at 16:09
  • @misteryes: No. As the error message says "no askpass program" was specified. So you *either* need a TTY (which you don't have) or you need an "askpass programm" or you need to configure `sudo` in such a way that it executes the command without needing a password (and hence no TTY or askpass program) – Aaron Digulla Apr 19 '13 at 07:22
  • Just in case, a TTY is a terminal/console, i.e. something that is somehow connected to a keyboard. http://en.wikipedia.org/wiki/Tty_(Unix) – Aaron Digulla Apr 19 '13 at 07:23
0

Agreed with other comment - you are mixing issues which can be solved separately. Many people (including myself) have made this error.

Paramiko is powerful, and smart: it can navigate and respond to SSH username and password prompts. But this is a special case. Most of the time when you need to respond to a PROMPT in Paramiko, you basically wait then shoot text at the assumed prompt. This is messy.

This also has absolutely nothing to do with your real problem.

What you want to do is edit /etc/sudoers file so that your automation test user or group is able to run the precise command you want using NOPASSWD.

Let's say I want to remotely grep /var/log/auth.log on host "ServerB". While grep can be run by any user, it's known that auth.log on this system is only readable by user root. SO:

1) My test user is "scott", and a member of group "adm. See /etc/groups and /etc/passwd. Basic stuff.

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

3) From a remote system, I run: $ ssh scott@ServerB "sudo grep Accepted /var/log/auth.log" 2013-10-14T21:17:54+00:00 proc4-01-us1 sshd[28873]: Accepted publickey for scott from x.x.x.x port 56799 ssh2 2013-10-14T21:19:16+00:00 proc4-01-us1 sshd[29367]: Accepted publickey for scott from x.x.x.x port 56804 ssh2 2013-10-14T21:19:21+00:00 proc4-01-us1 sshd[29519]: Accepted publickey for scott from x.x.x.x port 56805 ssh2

Bang, you're done.

NOTES DO use an absolute filesystem path to the script you specify in sudoers.

DO use SSH keys. You can use keys +passphrase if you like. (Remember, Paramiko can answer login prompts) But this also means storing your passphrase in the script...

DO consider security. You're not really lessoning security here if you attach this permission to a special user. Certainly the method I describe is more secure than hardcoding a sudo password into the script.

DON'T use NOPASSWD:ALL except in testing. Specify what is allowed explicitly.

DO consider adding restrictions to what these users can run. For example, if I'm always running this test from an EC2 box, I'd only allow that user to connect from that EC2 IP. Conversationally, I could restrict what commands can be run by a user by adding prefixes in "authorized"keys" (ie, restricting that user to only being able to run rsync command, if I wanted to avoid running an rsync server full-time for example).

Scott Prive
  • 801
  • 11
  • 16