0

I'm using the Paramiko Python module to connect to a remote server as user admin and then switch user to root.

here is the class I'm using

class ShellHandler:

    def __init__(self, host, user, psw):
        self.ssh = paramiko.SSHClient()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(host, username=user, password=psw, port=22)

        channel = self.ssh.invoke_shell()
        self.stdin = channel.makefile('wb')
        self.stdout = channel.makefile('r')
        self.shell = channel

    def __del__(self):
        self.ssh.close()

    def ftp(self):
        ftp_client = self.ssh.open_sftp()
        return ftp_client

    def execute(self, cmd):
        """

        :param cmd: the command to be executed on the remote computer
        :examples:  execute('ls')
                    execute('finger')
                    execute('cd folder_name')
        """
        cmd = cmd.strip('\n')
        self.stdin.write(cmd + '\n')
        finish = 'end of stdOUT buffer. finished with exit status'
        echo_cmd = 'echo {} $?'.format(finish)
        self.stdin.write(echo_cmd + '\n')
        shin = self.stdin
        self.stdin.flush()

        shout = []
        sherr = []
        exit_status = 0
        for line in self.stdout:
            if str(line).startswith("FINISHED"): # add "; echo FINISHED" to the end of commands that take much longer to excute
                break
            if str(line).startswith(cmd) or str(line).startswith(echo_cmd):
                # up for now filled with shell junk from stdin
                shout = []
            elif str(line).startswith(finish):
                # our finish command ends with the exit status
                exit_status = int(str(line).rsplit(maxsplit=1)[1])
                if exit_status:
                    # stderr is combined with stdout.
                    # thus, swap sherr with shout in a case of failure.
                    sherr = shout
                    shout = []
                break
            else:
                # get rid of 'coloring and formatting' special characters
                shout.append(re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]').sub('', line).
                             replace('\b', '').replace('\r', ''))

        # first and last lines of shout/sherr contain a prompt
        if shout and echo_cmd in shout[-1]:
            shout.pop()
        if shout and cmd in shout[0]:
            shout.pop(0)
        if sherr and echo_cmd in sherr[-1]:
            sherr.pop()
        if sherr and cmd in sherr[0]:
            sherr.pop(0)

        return shin, shout, sherr

here is my code to connect to the server

# grab IP / admin password
while True:
    ip_address = input("Enter Hostname/IP address for Server: ")
    if len(ip_address) > 0:
        break
    else:
        continue
while True:
    admin_pw = getpass.getpass("Enter password for user admin: ")
    if len(admin_pw) > 0:
        break
    else:
        continue
#verify IP / admin password
try:
    logging.info("initiate ssh connection to {}".format(ip_address))
    server = ShellHandler(ip_address, "admin", admin_pw)
    print("Connected to {} as user admin".format(ip_address))
    logging.info("Connected to {} as user admin".format(ip_address))
except Exception as e:
    logging.exception("Exception occurred")
    sys.exit("Incorrect IP/Password")
#grab and verify root password
while True:
    root_pw = getpass.getpass("Enter password for user root: ")
    if len(root_pw) == 0:
        continue
    try:
        logging.info("verify root password")
        keystrokes = server.shell
        logging.info("invoke shell")
        keystrokes.send("su root\n")
        logging.info("switch user")
        keystrokes.send("{}\n".format(root_pw))
        logging.info("root password was send via channel")
        cmd = server.execute("whoami")
        logging.info("who am i")
        logging.info("cmd output {}".format(cmd))
        if "root\n" in cmd[1]:
            print("Connected to {} as user root".format(ip_address))
            logging.info("Connected to {} as user root".format(ip_address))
            break
        else:
            logging.info("Incorrect Password")
            print("Incorrect Password")
            continue
    except Exception as e:
        logging.exception("Exception occurred")
        print("Incorrect Password")
        continue

the code works 90% of the time without any problems, but sometimes it gets stuck at the root password part - right after you enter the root password and hit enter, here is what that looks like on the console

enter image description here

when I check the log file, this is what I find:

2022-04-15 09:09:23,062 - INFO - Connected to *********** as user admin
2022-04-15 09:09:26,855 - INFO - verify root password
2022-04-15 09:09:26,855 - INFO - invoke shell
2022-04-15 09:09:26,855 - INFO - switch user
2022-04-15 09:09:26,856 - INFO - root password was send via channel
2022-04-15 09:09:43,080 - DEBUG - [chan 0] Unhandled channel request "keepalive@openssh.com"
2022-04-15 09:09:58,441 - DEBUG - [chan 0] Unhandled channel request "keepalive@openssh.com"
2022-04-15 09:10:13,801 - DEBUG - [chan 0] Unhandled channel request "keepalive@openssh.com"

it gets stuck at this line forever and it never times out.

my question is, what do I change to make this part timeout if it gets stuck, and allow the user to enter root password again. without having to terminate the program and run it again.

Adam
  • 53
  • 6
  • Ok, but where exactly does it hang in your code? + Btw, automating `su` password prompt is generally a bad idea. See [Executing command using "su -l" in SSH using Python](https://stackoverflow.com/q/51493317/850848). – Martin Prikryl Apr 15 '22 at 08:09
  • @MartinPrikryl from the log file, i can tell it hangs at cmd = server.execute("whoami") – Adam Apr 15 '22 at 08:11
  • @MartinPrikryl i know automating su is a bad idea, but I have to do it. i need to run some commands natively as user root - not with sudo. also, this server does not allow you to log in as user root directly for security reasons, you have to login as admin, and then switch user to root. – Adam Apr 15 '22 at 08:12
  • Where *exactly*??? The `ShellHandler.execute` has over 40 lines of code. + Direct root login with keys is the best solution, see https://superuser.com/q/1481318/213663 + Are you aware that the `su` won't have any effect on the `ftp` (sic) part of your API? – Martin Prikryl Apr 15 '22 at 09:43

0 Answers0