Tcl Expect fails spawning SSH to server but SSH from command line works - ssh

I have some code that I'm using to connect to a server and perform some commands. The code is as follows:
#!/usr/bin/expect
log_file ./log_std.log
proc setPassword {oldPass newPass} {
send -- "passwd\r"
expect "* Old password:"
send -- "$oldPass\r"
expect "* New password:"
send -- "$newPass\r"
expect "* new password again:"
send -- "$newPass\r"
}
set server [lindex $argv 0]
spawn /bin/ssh perfgen#$server
# Increase buffer size to support large text responses
match_max 100000
# Conditionally expects a prompt for host authenticity
expect {
"*The authenticity of host*" {
send -- "yes\r"
}
}
What I find very strange is that when I SSH from my command line the SSH command works no problem. However, when I SSH from the shell script I get the following error:
spawn /bin/ssh perfgen#192.168.80.132
ssh: Could not resolve hostname 192.168.80.132
: Name or service not known
The same script runs against 3 servers, but 2 of the 3 servers always fail. However, if I try logging into the servers manually do do the work all three servers pass.
Any idea what might be happening here? I'm completely stumped. This code was working up until about 2 weeks ago and according to the server administrator nothing has changed on the server-side config.

Trimming any whitespace seemed to solve the issue:
set serverTrimmed [string trim $server]

Related

How do you pass serveral passwords in SCP using expect?

My purpose is control transferring files between two remote machines. If I could use key-pair it would be much easier but I can't choose the options but passing passwords.
Environments : Local(Mac), Remote_source(Linux), Remote_target(Linux)
Here it is my expect.sh. When I run it, source#source's password: pops up, then target#target's password: and nothing else. No files transferred, no stdout, no error message, just disconnected.
#!/usr/bin/expect
set timeout -1
spawn scp -3 scp://remote_source_user#host:port/home/source/test.txt scp://remote_target_user#host:port/home/target/
expect "remote_source_user#host's password:"
send "SOURCE_PASSWORD\r"
expect "remote_target_user#host's password:"
send "TARGET_PASSWORD\r"
expect eof
When I change the sequence the password (meaning send target password first rather than sending source password first.) it occurs error, so I guess it is working but I have got no idea what is going on internally.
I have tried sshpass but it is not working with several passwords.
I debugged it with -v flag and found out that Connection works perfectly but the problem is the directory.
# in debugging mode
Sink: scp: home/username/test.txt: No such file or directory
It seems not recognizing /home/username/test.txt at the beginning.
so the code should be like below:
#!/usr/bin/expect
set timeout -1
spawn scp -3 scp://remote_source_user#host:port/~/test.txt scp://remote_target_user#host:port/~/
expect "remote_source_user#host's password:"
send "SOURCE_PASSWORD\r"
expect "remote_target_user#host's password:"
send "TARGET_PASSWORD\r"
expect eof
Do not explicitly write your home directory /home/username/ but use /~/.
# The Result
debug1: Sending command: scp -v -r -f ~/test.txt
Sending file modes: C0000 00 test.txt
Sink: C0000 00 test.txt

Expect script not working and terminal closes immediately

I don't know what's wrong with the script. I set up a new profile on Iterm terminal to run the script, but it never works and closes immediately. Here's the script:
#!/usr/bin/expect -f
set timeout 120
set secret mysecret
set username asdf
set host {123.456.789.010}
set password password123
log_user 0
spawn oathtool --totp --base32 $secret
expect -re \\d+
sleep 400
set otp $expect_out(0,string)
spawn ssh -2 $username#$host
expect "*assword:*"
send "$password\n"
expect "Enter Google Authenticator code:"
send "$otp\n"
interact
First, test you ssh connection with:
ssh -v <auser>#<apassword>
That will validate the SSH session works.
Make sure to not use ssh -T ..., since you might need a terminal for expect commands to work.
Second, add at least an echo at the beginning of the script, to see if it is called:
puts "Script running\r"
Third, see if a bash script, with part of it using expect as in here, would work better in this case

How to check SSH credentials are working or not

I have a large number of devices around 300
I have different creds to them
SSH CREDS, API CREDS
So as I cannot manually SSH to all those devices and check the creds are working or not
I am thinking of writing a script and pass the device IP's to the script and which gives me as yes as a result if the SSH creds are working and NO if not working.
I am new to all this stuff! details will be appreciated!
I will run this script on a server from where I can ssh to all the devices.
Your question isn't clear as to what sort of credentials you use for connecting to each host: do all hosts have the same connection method, for instance?
Let's assume that you use ssh's authorised keys method to log in to each host (i.e. you have a public key on each host within the ~/.ssh/authorized_keys file). You can run ssh with a do nothing command against each host and look at the exit code to see if the connection was successful.
HOST=1.2.3.4
ssh -i /path/to/my/private.key user#${HOST} true > /dev/null 2>&1
if [ $? -ne 0]; then echo "Error, could not connect to ${HOST}"; fi
Now it's just a case of wrapping this in some form of loop where you cycle through each host (and choose the right key for each host, perhaps you could name each private key after the name or IP address of the target host). The script will go out all those hosts for which a connection was not possible. Note that this script assumes that true is available on the target host, otherwise you could use ls or similar. We pipe all output to /dev/null/ as we're only interested in the ability to connect.
EDIT IN RESPONSE TO OP CLARIFICATION:
I'd strongly recommend not using username/password for login, as the username and password will likely be held in your script somewhere, or even in your shell history, if you run the command from the command line. If you must do this, then you could use expect or sshpass, as detailed here: https://srvfail.com/how-to-provide-ssh-password-inside-a-script-or-oneliner/
The ssh command shown does not spawn a shell, it literally logs in to the remote server, executes the command true (or ls, etc), then exits. You can use the return code ($? in bash) to check whether the command executed correctly. My example shows it printing out an error message for non-zero return codes, but to print out YES on successful connection, you could do this:
if [ $? -eq 0]; then echo "${HOST}: YES"; fi

Using expect script to do an ssh from a remote machine

I am new to expect scripts and have a use case in which I need to do an ssh from a machine in which I have already done an ssh using expect script. This is my code snippet
#!/usr/bin/expect -f
set timeout 60
spawn ssh username#machine1.domain.com
expect "Password: "
send "Password\r"
send "\r" # This is successful. I am able to login successfully to the first machine
set timeout 60
spawn ssh username#machine2.domain.com #This fails
This takes a some amount of time and fails saying
ssh: connect to host machine2.domain.com port 22: Operation timed out. I understand that 22 is the default port on which ssh runs and I can manually override it by giving a -p option to ssh.
If I try to ssh independently without the expect script I get a prompt that asks me to enter (yes/no). From where is the correct port being picked up if I execute ssh directly without the expect script. If I do not need to enter the port number on shell why would it be needed to enter a port number if I am using an expect script.
That that point, you don't spawn a new ssh: spawn creates a new process on your local machine. You just send a command to the remote server
#!/usr/bin/expect -f
set timeout 60
spawn ssh username#machine1.domain.com
expect "Password: "
send "Password\r"
send "\r" # This is successful. I am able to login successfully to the first machine
# at this point, carry on scripting the first ssh session:
send "ssh username#machine2.domain.com\r"
expect ...

How can I account for connection failure using expect for ssh log-on automation?

I have a shell script that works fairly well for automating my ssh connections and for anything else that I would like to do via ssh. I'm very unsatisfied with the results, however, when host can't be found or if connection is refused. If the host cannot be found, upon timeout send prints my password onto the screen... no good. I've gotten around this by adding an infinite timeout < set timeout -1 >. When connection is refused; however, I get a message about how connection was refused and that there was an error sending, etc... and my password is printed as well. Is there a way to tell my script that if exact expect is not met then don't proceed to send, to just ctrl+c? The following is the relevant part of my shell script: (used in bash, by the way) Thanks in advance.
expect -c "
spawn ssh $USER#$HOST
expect -exact \"$USER#$HOST's password:\"
send \"$PASS\r\"
interact"
The answer is to expect the timeout keyword. If none of the patterns match, then the timeout condition occurs -- of course, you can't set the timeout value to -1: set it to a reasonable number of seconds.
Instead of cramming a large-ish script into the -c argument, put it into a file
#! /usr/local/bin/expect -f
set host [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
# or, foreach {host user password} $argv {break}
spawn ssh $user#$host
expect {
-re {password: $} {send "$password\r"}
timeout {error "ssh connection timed out!"}
}
interact