i am trying to automate the log-in function for a device that i communicate with through serial. In order to reach the login: prompt i got to press enter while the device boots and then after sometime the login: prompt will show up, once it the program spots the 'login:' string it enter the username(or at least that's the plan). After entering the correct username the Password: prompt will show up, if i enter the correct password i successfully log-in to the device, if i enter the wrong password i have to start over(which means to enter the username again). Also if i fail to log-in in the first try the login: prompt changes to username:.
I have made this till now, but
import serial
import re
from time import sleep
import time
ser = serial.Serial('COM3', timeout=1)
ser.baudrate = 115200
def auto_login():
while True:
output = ser.read(10000).decode('utf-8', 'ignore')
testmode_command = ser.write("\r\n".encode())
print(output)
if "1 : press [Enter] for test mode / [Esc+Enter] for plain Linux" in output:
ser.write(testmode_command)
if " login:" in output:
break
def login_repeat():
login = b"root \r\n"
output = ser.read(10000).decode('utf-8', 'ignore')
print(output)
if " login:" in output:
ser.write(login)
if "Username:" in output:
ser.write(login)
def pass_word():
password = b"p \r\n"
time.sleep(0.1)
output = ser.read(10000).decode('utf-8', 'ignore')
print(output)
if "Password:" in output:
ser.write(password)
The result i am getting is :
Login incorrect
Username:
root
System starting up, please try later
Login incorrect
Username:
root
For some reason i looks like the enter is sent first the \r\n command instead of the username and then the command. Any idea how to resolve this?
Add time.sleep(0.1), before you send a command, like this :
time.sleep(0.1)
ser.write(b"root")
time.sleep(0.1)
ser.write('\r'.encode())
Just as a hunch, are you sure, you have no buffering issues. I don't know the serial module but it might be possible that the library sends the "Enter" together with the login information.
That would result in "Enter" as user name.
Quick searching brought up this answer: https://stackoverflow.com/a/12892221/4252584
You might try to explicitly flush the buffers.
On the other hand I am wondering why you get to the login prompt without prior "Enter" key on the serial line. Are you sure, you need the "Enter" key on the line?
Related
Thanks to Python Library i was able to use their example to telnet to Cisco switches, I am using this for learning purposes, specifically learning python.
However, although all the code seem generally easy to read, I am a bit confused as to the following:
1- why use the if statement below
2- why use the "\n" after the username and password write method
3- why am i not getting the output on my bash terminal when the changes are infact committed and successful
HOST = "172.16.1.76"
user = raw_input("Enter your Telnet username : ")
password = getpass.getpass()
tn = telnetlib.Telnet(HOST)
tn.read_until("Username: ")
tn.write(user + '\n') <----- 2
if password: <----- 1
tn.read_until("Password: ")
tn.write(password + "\n") <------2
tn.write("show run \n")
time.sleep(5)
output = tn.read_all() <----- 3
print output
print "=" * 30
print "Configuration Complete."
I am not sure as to why using the if statement above, typically once you input in the Username, you get the password prompt right afterward. why cant we just type :
tn.read_until("Username: ")
tn.write(user + '\n')
tn.read_until("Password: ")
tn.write(password + "\n")
As for the second point, why use the '\n' after the passwords and username in the write method if we going to hit enter after we add them anyway?
1: the line
password = getpass.getpass()
asks you for you password, if you leave it empty, password will contain the empty string which, in an if statement, is the same as False
the script doesn't know ahead of time if you have a password on your server or not, it simulates knowing by asking you first and if you don't input anything, it assumes it doesn't (otherwise it would get stuck on tn.read_until("Password: ") forever.
2: the '\n' simulates you hitting the return key. when you enter your password, for example 'password<RETURN>' the variable password will not contain a trailing newline (\n), this is why it is manually appended
3: this one i dont know, possibly 5 seconds isn't enough time to wait
After execute
tn = telnetlib.Telnet(HOST)
you have created a telnet channel from your machine to HOST. But you still need to communicate with HOST to push/send your commands and receive the outputs.
To push your commands to HOST, you need to execute tn.write("your_commands_or_input \n"), \n means newline/return, which tells your current commands need to be executed now. After the execution, HOST return the result, which will be caught by your telnet object "tn" and saved in its "local cache", you can search any keywords you expected in this cache by using tn.read_until method, if the expected keyword has been found, read_until will stop(always stop on the 1st found), and you can do anything you need(It's your turn now), else the read_until will keep waiting the output from HOST(Haven't you turn yet). Finally if you want to check all output have been cached, you can execute tn.read_all().
Remember some of the HOST using different login output, i.e Username vs username or Password vs password, you better to use regular expression to match them.
There is a python library on github, specifically for telneting to cisco devices.
pip install git+https://github.com/sergeyzelyukin/cisco-telnet.git
import ciscotelnet
with ciscotelnet.CiscoTelnet(host, verbose = False) as cisco:
if cisco.login(final_mode=CiscoTelnet.MODE_ENABLE, user="john", user_pass="12345678", enable_pass="cisco"):
# if cisco.login(final_mode=CiscoTelnet.MODE_ENABLE, line_pass="abcdef", enable_pass="cisco"):
print cisco.cmd("sh int status | inc Fa0/1")
print cisco.conf(["interface fast0/1", "descr blank", "load-interval 300"])
print cisco.wr()
We have some strange setup on our "shared" server that will not remember my git password for certain situations. I tried hard to fix the real issue; but at some point I gave up and created this python script:
#!/usr/bin/env python3
"""
pass4worder: a simply python script that runs a custom command; and "expects" that command to ask for a password.
The script will send a custom password - until the command comes back with EOF.
"""
import getpass
import pexpect
import sys
def main():
if len(sys.argv) == 1:
print("pass4worder.py ERROR: at least one argument (the command to run) is required!")
sys.exit(1)
command = " ".join(sys.argv[1:])
print('Command to run: <{}>'.format(command))
password = getpass.getpass("Enter the password to send: ")
child = pexpect.spawn(command)
print(child.readline)
counter = 0
while True:
try:
expectAndSendPassword(child, password)
counter = logAndIncreaseCounter(counter)
except pexpect.EOF:
print("Received EOF - exiting now!")
print(child.before)
sys.exit(0)
def expectAndSendPassword(child, password):
child.expect("Password .*")
print(child.before)
child.sendline(password)
def logAndIncreaseCounter(counter):
print("Sent password ... count: {}".format(counter))
return counter + 1
main()
This solution does the job; but I am not happy about how those prints look like; example:
> pass4worder.py git pull
Command to run: <git pull>
Enter the password to send:
<bound method SpawnBase.readline of <pexpect.pty_spawn.spawn object at 0x7f6b0f5ed780>>
Received EOF - exiting now!
b'Already up-to-date.\r\n'
I would rather prefer something like:
Already up-to-date
Received EOF - exiting now!
In other words: I am looking for a way so that pexect simply prints everything "as is" to stdout ... while still doing its job.
Is that possible?
(any other hints regarding my script are welcome too)
child.readline is a function so I think you actually wanted print(child.readline() ).
Change print(child.before) to print(child.before.decode() ). bytes.decode() converts bytes (b'string') to str.
How to Skip the login password prompt if not entered or password is wrong..
I have Below python fabric code.. which works fine but stucks with wrong passwords..
import sys
from fabric.api import *
env.skip_bad_hosts=True
env.command_timeout=60
env.user = 'test'
env.shell = "/bin/sh -c"
env.warn_only = True
env.password = 'mypass'
def read_hosts():
env.hosts = [line.strip() for line in sys.stdin.readlines()]
def cyberark():
with settings(warn_only=True):
output=sudo("/monitor.sh",shell=False)
When i run it, it stands there only until i break it manually...
[pc-karn] Executing task 'cyberark'
[pc-karn] sudo: /monitor.sh
[pc-karn] Login password for 'test':
Is there anyway to set the env where if the password given wrong with 2 consecutive sequence and it will go to the next host in line.
You can use this parameters -
with settings(abort_on_prompts=True):
This parameter terminate the program when prompt the user for input. You can read about it here.
I don't know if this solve your problem, as far as i know Fabric what you are looking for is not possible, but at least you know your program always terminate, and can fix the passwords issue.
When I want to dodge a password prompt but continue my script on fail, I use abort_on_prompts=True and catch the SystemExit exception that is raised by abort_on_prompt.
try :
with settings(abort_on_prompts=True):
output=sudo("/monitor.sh",shell=False)
except SystemExit as se :
print "What I want to do when it fails"
I am trying to write a python script that will automatically log in to a remote host via ssh and update a users password. Since ssh demands that it take its input from a terminal, I am using os.forkpty(), running ssh in the child process and using the parent process to send command input through the pseudo terminal. Here is what I have so far:
import os, sys, time, getpass, select, termios
# Time in seconds between commands sent to tty
SLEEP_TIME = 1
NETWORK_TIMEOUT = 15
#------------------------------Get Passwords------------------------------------
# get username
login = getpass.getuser()
# get current password
current_pass = getpass.getpass("Enter current password: ")
# get new password, retry if same as current password
new_pass = current_pass
first_try = True
while new_pass == current_pass:
if first_try:
first_try = False
else:
# New password equal to old password
print("New password must differ from current password.")
# Get new password
new_pass = getpass.getpass("Enter new password: ")
new_pass_confirm = getpass.getpass("Confirm new password: ")
while new_pass != new_pass_confirm:
# Passwords do not match
print("Passwords do not match")
new_pass = getpass.getpass("Enter new password: ")
new_pass_confirm = getpass.getpass("Confirm new password: ")
#------------------------------End Get Passwords--------------------------------
ssh = "/usr/bin/ssh" # ssh bin location
args = ["ssh", login + "#localhost", "-o StrictHostKeyChecking=no"]
#fork
pid, master = os.forkpty()
if pid == 0:
# Turn off echo so master does not need to read back its own input
attrs = termios.tcgetattr(sys.stdin.fileno())
attrs[3] = attrs[3] & ~termios.ECHO
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attrs)
os.execv(ssh, args)
else:
select.select([master], [], [])
os.write(master, current_pass + "\n")
select.select([master], [], [])
os.write(master, "passwd\n")
select.select([master], [], [])
os.write(master, current_pass + "\n")
select.select([master], [], [])
os.write(master, new_pass + "\n")
select.select([master], [], [])
os.write(master, new_pass + "\n")
select.select([master], [], [])
os.write(master, "id\n")
select.select([master], [], [])
sys.stdout.write(os.read(master, 2048))
os.wait()
The script prompts the user for his/her current and new passwords, then forks and sends appropriate responses to ssh login prompt and then passwd prompts.
The problem I am having is that the select syscalls are not behaving as I would expect. They don't appear to be blocking at all. I'm thinking that I am misunderstanding something about the way select works with the master end of a pty.
If I replace them all with time.sleep(1), the script works fine, but I don't want to have to rely on that solution because I can't always guarantee the network will respond in a short time, and I don't want to make it something rediculous that will take forever (I intend to use this to programatically log into several servers to update passwords)
Is there a way to reliably poll the master side of a pty to wait for the slave's output?
Note: I realize there are better solutions to this problem with things like sshpass and chpasswd, but I am in an environment where this cannot be run as root and very few utilities are available. Thankfully python is.
select doesn't read any data; it simply blocks until data is available to be read.
Since you don't read any data after the first select, there will still be data left in the buffer for you to read, so any subsequent select will not block.
You need to read the data in the buffer before calling select again. Doing this without blocking means that you will likely have to set the file to non-blocking mode (I don't know how to do that in Python).
A better way of providing the password over SSH would be to use the --stdin flag if your passwd supports it, and to run the command directly over SSH instead of through the created shell.
handle = subprocess.Popen(["ssh", login + "#localhost", "-o StrictHostKeyChecking=no", "passwd --stdin"])
handle.communicate("\n".join([oldpass, newpass, newpass, ""]))
Have a look in man ssh at the -f option. It may be what you need when launching ssh. It will block ssh until the password is typed and then fork by itself. You could probably use this feature to achieve what you want (but you may have to slightly change your current code because it will perform by itself what you currently try to embed in your script).
This option is generally used at the command line for starting a remote graphical program: once the password is typed, you can safely close the terminal and keep interacting with the remote process with its graphical interface. But I think using this feature here would lead to a much cleaner way than playing with low-level blocking features and similar things.
I would like to use Fabric to set the password for a user on a remote server.
Let's say I have a user named 'john' and I want to set a default password '123' for him.
Ideally I would like to do this:
run 'passwd john' on remote machine
detect when linux prompts for 'Enter new UNIX password:'
automatically enters the password
detect when linux prompts for 'Retype new UNIX password:'
automatically reenters the password
This is what I have tried:
result = run('passwd {}'.format(username))
The problem with this statement is that 'result' does not capture when Linux prompts for entering password. It only returns after the passwords are entered.
Is there a way to automate interactive prompts like this?
you can use prompts
password = '123'
with settings(prompts={
'Enter new UNIX password: ': password,
'Retype new UNIX password: ': password
}):
run('passwd {}'.format(username))
You can use fexpect for interactive prompts.