Handling set new password prompt with Paramiko

Why this post?

I hit an issue at one of our client recently – beginning from the latest version of the application, the remote server will prompt for a new password in the first login attempt, the password that comes out of the box will take you to the set new password prompt where you need to set a new password, re-type the new password & then login again with the new password. While this change is a really good security feature, it led to our automation tests failing.
I was able to fix this, but while solving it, I realized that a solution using Paramiko was not easily available, I had to google for a few issues with my proposed solutions. I am sharing it to help another tester who might face this same problem.


My solution:

I created a method to invoke an interactive shell with the Paramiko SSH client to:

    1. check for existing password prompt(to confirm new password prompt)
    2. verify new password prompt
    3. enter a new password
    4. re-enter the new password again
    5. get the confirmation message to assist debug

NOTE: I am assuming you are working with a partially working code already. I am sharing the function alone hence. If you are new to Paramiko and wish to understand it better before solving this issue, refer our blog on Paramiko

def handle_new_password_prompt():
    """
    Check if new password is prompted and set it
 
    :param:
    :return client: SSH client object
    """
    client = None
    host = your_host
    user_name = your_user_name
    port = your_port
    old_pwd = your_old_password
    new_pwd = your_new_password
 
    try:
	# create an ssh client object
	client = paramiko.SSHClient()
	logging.getLogger("paramiko").setLevel(logging.ERROR)
	client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
	# connect to the remote server with the old password
	client.connect(host, username=user_name, password=old_pwd, port=port, timeout=30)
 
	# invoke an shell
	shell_obj = ssh_client.invoke_shell()
 
        # get the shell output, get the complete output with the while loop
        out = ''
        # sleep is essential, recv_ready returns False without sleep
        time.sleep(1)
 
        while shell_obj.recv_ready():
            out += shell_obj.recv(2048)
 
        # get the last line of the output
        out = out.split('\n')[-1]
 
        ########### step #1 ############
        # verify if the last line of the output prompts existing password
        if out.strip() == '(current) UNIX password:':
            shell_obj.send(old_pwd+'\n')
	    print("Old password when prompted was entered successfully")
 
        ########### step #2 ############
        # check prompt for new password
        out = ''
        time.sleep(1)
        while shell_obj.recv_ready():
            out += shell_obj.recv(2048)
 
        ########### step #2 ############
        # if new password prompted, Enter new password
        if out.strip() == 'New password:':
            ########### step #3 ############
            shell_obj.send(new_pwd+'\n')
	    print("New password when prompted was entered successfully")
 
        ########### step #4 ############
        # check re-enter new password prompt
        out = ''
        time.sleep(1)
        while shell_obj.recv_ready():
            out += shell_obj.recv(2048)
 
        # if re-enter new password prompted, Enter the new password again
        if out.strip() == 'Retype new password:':
            shell_obj.send(new_pwd+'\n')
	    print("Re-enter new password when prompted was entered successfully")
 
        ########### step #5 ############
        # get the output after setting password - useful debug statement
        out = ''
        time.sleep(1)
        while shell_obj.recv_ready():
            out += shell_obj.recv(2048)
        if out:
	    print("Confirmation after setting new password is - {}".format(out))
 
	shell_obj.close()
 
    except Exception as e:
	print("Unable to set new password due to - {}".format(str(e)))
 
    else:
	client.connect(host, username=user_name, password=new_pwd, port=port, timeout=30)
 
    finally:
	return client

I have posted the whole snippet as one big function to explain the flow of actions, I would suggest you break it down into smaller methods.
This is what I learned while working on this fix :
1. recv_ready() method requires the while loop to read the complete data buffer
2. time.sleep(1) although is mundane is required(reference – paramiko issue) for the shell_obj to be ready to receive data
3. using recv_ready() in the end to read the output after setting the new password is very useful.


This solution worked for me, I was able to run my automation tests with this fix. I hope this information is as useful to you as it was to me.
If you have tried a different solution and it worked for you, great! let us know your solution in a comment.


Leave a Reply

Your email address will not be published.