SecureCRT - Python script, automated ssh2 tab not responding to commands being sent - python

I'm modifying a script in python to run in securecrt 8.5.2 in order to backup the running-config of some cisco ASR9K equipment I have in charge, but the script seems to end abruptly after the second sucessful ssh2 hop (2nd tab) and does not send the commands I scripted (the exit in this specifical example), here's the code I have, as I'd said it's a modified version of the one's in vandyke page for opening ssh2.
One important thing is that I have to tab each session of each individual routers, because it doesn't permit doing an ssh direct from the active cli, so I had to improvise and implement this "connect in TAB", I'm suspecting that the secureCRT doesn't know if it is in the new tab I've opened so, it doesn't know where to send the commands.
I was playing with the line 30, but it doesn't seem to have any effect. I was changing the expected text, but it doesn't seem to recognize the correct tab or doesn't read the correct one.
Personal Background: A complete beginner in the python language.
# $language = "python"
# $interface = "1.0"
# Connect to an SSH server using the SSH2 protocol. Specify the
# username and password and hostname on the command line as well as
# some SSH2 protocol specific options.
host = "X.X.X.a"
host2 = "X.X.X.b"
def main():
crt.Screen.Synchronous = True
# Prompt for a username and password instead of embedding it in a script...
#
usr = crt.Dialog.Prompt("Enter the user name for" + host, "Username", "", True)
passwd = crt.Dialog.Prompt("Enter TACACS+ for" + host, "Login", "", True)
# Build a command-line string to pass to the Connect method.
cmd = "/SSH2 /L %s /PASSWORD %s /C AES-128-CTR /M SHA1 %s" % (usr, passwd, host)
crt.Session.Connect(cmd)
crt.Screen.WaitForString("X.X.X.a#")
crt.Screen.Send("copy running-config tftp:\r")
crt.Screen.WaitForString("Host name or IP address (control-c to abort): []?")
crt.Screen.Send("tftpserver.com\r")
crt.Screen.WaitForString("Destination file name (control-c to abort): [running-config]?")
crt.Screen.Send("X.X.X.a_running_config\r")
crt.Screen.WaitForString("X.X.X.a")
cmd2 = "/SSH2 /L %s /PASSWORD %s /C AES-128 /M SHA1 %s" % (usr, passwd, host2)
crt.Session.ConnectInTab(cmd2)
crt.Screen.WaitForString("X.X.X.b#")
crt.Screen.Send("exit\r")
main()
crt.Session.ConnectInTab(cmd2)
It connects to the equipment in a new tab, but what I expect is that the script will keep doing the same it did for the host1 (X.X.X.a) and send the same boring stuff to the host2 (X.X.X.b) via ssh2 tab, and continue the itterative process until I do this for all the equipments I need.
Thanks for reading me.

Well it's not even funny easy was to solve this very sub-optimal code or script but it was not much of a problem, the only thing is that I had to dissconect to the previous session when I inyected all the commands, so in order to put the cursor on the new tab, the previous session must be dissconected first.
The solution?
crt.Session.Disconnect()

Related

SSHLibrary prompts for password

I have a problem with a test suite. I use robot framework and python.I created a function in python which executes a console command in a remote Linux client.
def purge_default_dns(device_ip):
ssh_val = "usr1#" + device_ip
command = "ctool dns | grep -v \"grep\" | awk \'{print $2}\'"
test = check_output(["ssh", ssh_val, "-p", "6022", command])
The check_output() function connects with device_ip and executes command. If I try to connect with a fully qualified domain name (ex. my.domain.io), then I get a prompt for password (which is empty). I press enter and command executes regular. Is there any parameter that passes for example Enter when password prompt comes up?
I tried ssh -e switch , I don't want to change ssh client , I just need a generic solution.
For example using paramiko library in the code below , I can create an paramiko SSHClient , which has a parameter for password and doesn't prompt anything. While I can't use paramiko right now , I need something else with SSHLirary to go around the problem.
def send_ssh_command(device_ip , command):
hostname = device_ip
password = ""
username = "usr1"
port = 6022
try:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
client.connect(hostname, port=port, username=username, password=password)
stdin , stdout , stderr = client.exec_command(command)
command_return_val = stdout.read()
finally:
client.close()
return command_return_val
Thank you.
To get this straight, the only solution you look for is to pass the password on the command line to the default OS ssh client, and do not/cannot install any libraries (paramiko, etc) that can help you achieve the same result through other means?
I'm asking this, because the robot framework's SSHLibrary provides this out of the box; you already have the python's solution with paramiko; and the general linux solution is to install the sshpass package, and use it to pass the value:
sshpass -p "YOUR_PASS" ssh -usr1#my.domain.io:6022
So if all of these are not an option, you are left with two alternatives - either hack something around SSH_ASKPASS - here's an article with a sample, or use expect to pass it - this one is what I'd prefer out of the two.
Here's a very good SO answer with an expect script wrapper around ssh. In your method, you will have to first create a file with its content, set an executable flag on it, and then call that file in check_output(), passing as arguments the password, 'ssh' and all its arguments.
Why You need to go with python , I am using below code in robotframework for the same:
[Arguments] ${host}=${APP_SERVER} ${username}=${APP_USERNAME} ${password}=${APP_PASSWORD}
Open Connection ${host} timeout=2m
Login ${username} ${password}
${out} ${err} ${rc}= Execute Command cd ${PATH};ctool dns | grep -v \"grep\" | awk \'{print $2}\' * return_stdout=True return_stderr=True return_rc=True
Should Be Equal ${rc} ${0}

How do I SSH to machine through a middle host using Paramiko?

Essentially, I need to access a computer, say machine A, which is only accessible via the internal network of my company. I used to be able to set up tcprelay port forwarding to accomplish this but that pipeline has been disabled due to some potential security flaws.
Let’s say my company general network is at
company#10.0.0.1
and the specific machine i want to work with is at
machine#10.0.0.3
Both accounts have password ‘password’
Via terminal and shell commands, I can just hop there using one single command:
https://askubuntu.com/a/311457
or, in steps, it would be:
[on my account] ssh company#10.0.0.1
[on my account] enter password
[on company network] ssh machine #10.0.0.3
[on company network] enter password again
And I’d be logged into the machine I need to communicate with.
However, after hacking away all afternoon I could not get this working with Paramiko. I tried setting up the connection then issuing a client.exec_command() but just cannot get a handle for the specific machine. The rest of my scripts relies on having a paramiko client that can receive commands and return responses, so it would be a very heavy overhead for me to go propagate all changes were I to switch to say fabric or subprocess.
The closest I got to was:
ssh.connect(’10.0.0.1', username=‘company', password=‘password’)
chan = ssh.get_transport().open_session()
chan.get_pty()
chan.exec_command(‘ssh machine#10.0.0.3’)
print chan.recv(1024)
which returned the ‘enter password’ prompt, but running chan.send(‘password’) just ends with a hang.
I’m pulling my hair out at this point and am just reading through the documentation hoping to find what concept I’m missing.
If anyone can give some advice I’d really appreciate it.
Thanks!
Alternative way is to avoid entering password when login to another machine.
This can be done by using ssh-keygen.
Login to first machine (A) with user 'first':
$ ssh-keygen -t rsa
--> Don't enter any passphrase when requested
--> Note down the line "Your public key has been saved in /home/first/.ssh/"
--> This file is the public key of machine 'A'
Now login to second machine(B) using ssh.
Then check for ~/.ssh folder. If no folder, create one.
Create a file with name 'authorized_keys' under ~/.ssh/authorized_keys
Copy the content of file from 'first' user to the file 'authorized_keys'.
is a file with 'id_rsa.pub' from 'first' user login (under /home/first/.ssh/id_rsa.pub)
Now you can login to second machine from first without entering password thru your script.
I worked on a project where it had to log in using username/password over SSH then do the same thing again to another host. I had no control over networks ACLs and SSH keys were not allowed for some reason. You'll need to add paramiko_expect. Here's how I got it to work:
import paramiko
from paramiko_expect import SSHClientInteraction
user1 = 'admin'
pass1 = 'admin'
user2 = 'root'
pass2 = 'root'
# not needed for this example, but included for reference
user_prompt = '.*\$ '
# will match root user prompt
root_prompt = '.*$ '
# will match Password: or password:
pass_prompt = '.*assword: '
# SSH to host1
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(
paramiko.AutoAddPolicy())
ssh_client.connect(hostname='host1', username=user1, password=pass1)
# Interact with SSH client
with SSHClientInteraction(ssh_client, display=True) as interact:
# Send the command to SSH as root to the final host
interact.send('ssh {}#host2'.format(user2)
# Expect the password prompt
interact.expect(pass_prompt)
# Send the root password
interact.send(pass2)
# Expect the root prompt
interact.expect(root_prompt)
ssh_client.close()
One caveat: if host1 has never connected to host2 using SSH it'll get a warning about host key checking and timeout. You can change the configuration on host1 or just SSH to host1 then from host1 SSH to host2 and type yes and press enter.

Changing Linux username or password with Python script

I'm writing a Python script that changes the username and password of a Linux account user - it's part of a larger internal web-gui system that queues up password change requests from apache2 (which can't run as root), and then changes the passwords itself. The python script itself obviously must run as root in order to change passwords.
The password change function is pretty straightforward:
def chpasswd(user, passwd):
if os.getuid() != 0:
syslog.syslog("Error: chpasswd.py must be run as root")
return
proc = Popen(
['/usr/sbin/chpasswd'],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
print "Changing: " + user + ':' + passwd
out, err = proc.communicate(user + ':' + passwd)
proc.wait()
print out
if proc.returncode != 0:
print "Error: Return code", proc.returncode, ", stderr: ", out, err
if out:
syslog.syslog("stdout: " + out)
if err:
syslog.syslog("stderr: " + err)
The print statements are just there for temporary debugging. This runs fine and doesn't report any errors - there's nothing on out or err; but for some reason the actual UNIX password simply isn't changed.
The script which invokes this function is listening on a locally bound TCP socket. When it receives a change password request (in the form of user:password - later to be encrypted but for now plaintext) it adds it to a queue and then invokes the chpasswd function.
So, typical usage would be:
# telnet localhost 7001
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword
When the server is running in a bash window (not as a daemon) it prints out:
# python chpasswd.py
Starting Password Server
New connection from: 127.0.0.1
Changing: jsmith:mynewpassword
The last statement, you can see, is the print statement in my chpasswd function.
But after doing the above, when I actually try to login as a user using the new password, I get:
$ su jsmith
Password:
su: Authentication failure
Is there some obvious thing I'm doing wrong here? My suspicion was that somehow the connection with Popen is not actually closing, or perhaps the single line user:password text is not being transmitted. So I tried doing something like:
out, err = proc.communicate(user + ':' + passwd + '\x04')
Notice the extra \x04 character at the end, indicating End Of Transmission. Adding this in still didn't get it to work however - the password remained unchanged.
I'm running this on Debian Wheezy, in case it makes any difference.
Update:
Investigating further, I can see that my chpasswd function actually is changing the password - if I cat the /etc/shadow file before and after connecting to my password server, I see there is a different hash.
It's just that when I try to authenticate using the plaintext password, it doesn't work. Therefore, my suspicion is that somehow, the communication with Popen is either adding additional characters, or losing characters somehow. Of course, since /etc/shadow is a hash, I can't figure out exactly what's going on here.
The problem in this particular instance was that telnet adds "\r\n" after you press return on entering text. Since your server was not stripping the data of whitespace this was preserved when changing the password.
It is possible to get telnet to not send the carriage return and newline characters by ending a line with the end-of-transmission character (EOT). You can do this by pressing Ctrl-D.
eg
$ telnet localhost 7001
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword^DChanging: jsmith:mynewpassword
Alternatively you can pipe the line into telnet
echo -n jsmith:mynewpassword | telnet localhost 7001
Obviously, you'll only want to do this for testing or the new password will end up in your shell history. (The -n argument suppresses the printing of newline characters by echo)
Or you might want to do away with telnet altogether and use netcat instead.
echo -n jsmith:mynewpassword | netcat localhost 7001

Python run mutiple ssh commands in the same session

My goal is to connect to SSH with python and authenticate which i can do with Paramiko or Fabric. But i would like to keep the session open after each execution and read the input/output. With paramiko i can only run 1 command before the session is closed and i am asked to authenticate again and the session hangs. And since fabric is using the paramiko library its giving me the same issue. For example if my directory structure is like this
-home
--myfolder1
--myfolder2
I would like to execute the below commands without having to re-authenticate because the sessions closes.
(make connection)
run cmd: 'pwd'
output: /home
run cmd: 'cd myfolder2'
run cmd: 'pwd'
output: /home/myfolder2
Is this possible with any module that is out there right now? Could it be made from scratch with native python? And also is this just not possible...?
Edit Added code. Without the new open_session it closes and i cannot run any command. After running the first command with this i will be prompted again to authenticate and it creates an infinite loop.
Edit2 If it closes after each command then there is no way this will work at all correct?
edit3 If i run this on a different server and exec_command with the paramikio.SSHClient it wont ask me to reauthenticate but if i 'cd somedir' and then 'pwd' it will output that i am back in the root directory of where i created.
class connect:
newconnection = ''
def __init__(self,username,password):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect('someserver', username=username,password=password,port=22,timeout=5)
except:
print "Count not connect"
sys.exit()
self.newconnection = ssh
def con(self):
return self.newconnection
#This will create the connection
sshconnection = connect('someuser','somepassword').con()
while True:
cmd = raw_input("Command to run: ")
if cmd == "":
break
try:
transport = sshconnection.get_transport()
transport.set_keepalive(999999)
chan = transport.open_session()
chan.settimeout(3)
chan.setblocking(0)
except:
print "Failed to open a channel"
chan.get_exception()
sys.exit()
print "running '%s'" % cmd
stdout_data = []
stderr_data = []
pprint.pprint(chan)
nbytes = 4096
chan.settimeout(5)
chan.get_pty()
chan.exec_command(cmd)
while True:
print "Inside loop " , chan.exit_status_ready()
time.sleep(1.2)
if chan.recv_ready():
print "First if"
stdout_data.append(chan.recv(nbytes))
if chan.recv_stderr_ready():
print "Recv Ready"
stderr_data.append(chan.recv_stderr(nbytes))
if chan.exit_status_ready():
print "Breaking"
break
print 'exit status: ', chan.recv_exit_status()
print ''.join(stdout_data)
This is possible by using the normal modules when you can concatenate the commands into one. Try
pwd ; cd myfolder2 ; pwd
as command. This should work but quickly becomes tedious when you have more complex commands which need arguments and horrible when the arguments contain spaces. The next step then is to copy a script with all the commands to the remote side and tell ssh to execute said script.
Another problem of this approach is that SSH doesn't return until all commands have executed.
Alternatively, you could build a "command server", i.e. a simple TCP server that listens for incoming connections and executes commands sent to it. It's pretty simple to write but also pretty insecure. Again, the solution is to turn the server into a (Python) script which reads commands from stdin and start that script remotely via SSH and then send commands.

How to add hostname to known_hosts using Python?

I am using this code to add server to the known_hosts:
subprocess.Popen(['sshpass', '-p', password, 'ssh', '-o', 'StrictHostKeyChecking=no', add_key], stdout=subprocess.PIPE).communicate()[0]
This adds hostname to the known_hosts but the server hangs as it tries to enter into the host. I just want add hostname to the known_hosts and continue with my other codes. How can I do that? Thanks
This should do the job. This solution use the pexpect library which is a great way to automate commands. You basically call add_known_hosts with the host, user, password that you want added. it will try to ssh to that host and either enters the password or responds to the add to known hosts connection with a yes and then enters the password. Finally it cleanly exits the connection by sending the exit command. You can modify this and not require a username and password and just answer yes to the continue connecting question and then end the ssh process instead of continuing with the password prompt.
import pexpect, time
def add_known_hosts(host, user, password):
child = pexpect.spawn('ssh %s#%s' % (user, host))
i = child.expect(['.* password:', '.* continue connecting (yes/no)?'])
if i == 1:
child.sendline('yes')
child.expect('.* password:')
child.sendline(password)
child.expect('%s#%s' % (user, host))
time.sleep(1)
child.sendline('exit')
add_known_hosts('myhost', 'myusername', 'mypassword')
debugging
from the comments below it seems you are facing issues using pexpect on your system. A good way to just to do a simple test to confirm pexpect is working correctly with ssh is to run the code below. Fill in host, user with your appropriate settings and then run the code, you should be able to interact with the ssh session. At this point you can build up from here and see exactly what text you are expecting to get from ssh and what text you want to send in response.
import pexpect
host, user = 'myhost', 'myusername'
child = pexpect.spawn('ssh %s#%s' % (user, host))
child.interact()
Never mind, Solved it myself. Here's what I did:
test = subprocess.Popen(['sshpass', '-p', password, 'ssh', '-o', 'StrictHostKeyChecking=no', add_key])
time.sleep(5.0)
test.kill()
Thanks!

Categories

Resources