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.
I help engineer high quality software. I started out as a manual tester at Cognizant Technology Solutions where I worked on a healthcare project. I quit CTS due to personal reasons and decided to try freelancing as a trainer. I mentored aspiring engineers on employability skills for a while. I liked exploring applications as a hobby and was always on the lookout for better testing jobs that had a better balance of exploratory testing, scripted testing and automation. I joined Qxf2 in 2015. I was introduced to Python, my first programming language, at Qxf2. Over the years I have had the opportunity to learn JavaScript and Shell scripting(can I even call it a language?) and a little bit of C. Python is my favourite, I am amazed by its simplicity and support for libraries. I like to: watch football – I support Arsenal Football Club, play football and read books
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.
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.