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.


3 thoughts on “Handling set new password prompt with Paramiko

  1. Alas, the solution doesn’t work for me on ZPE devices because it fails on the following line:
    client.connect(host, username=user_name, password=old_pwd, port=port, timeout=30)

    The problem is that Paramiko can’t establish connection until new password is provided, so it throws the following error without going further with the script.
    paramiko.ssh_exception.AuthenticationException: Authentication failed.

    1. Hi,
      We would like to know more about the error.
      Does your device not prompt for old password before asking you to set a new password?
      If that’s the case, you can ignore that step & start setting the new password.

  2. Worked very well for me! Trying to set up automatic server creation and configuration for Hetzner servers with ansible and this was the one step hindering me from accessing the new server. New to python as well, so this was not just a good solution but its also very readable! +1

Leave a Reply

Your email address will not be published. Required fields are marked *