create a file under a directory in multiple machines through some script? - linux

I have to create a file functon.txt under a particular directory with hello world in it in lots of machine. This is what I was doing so far manually one by one logging into each box and creating the file. That directory is own by root so I have to make sure that new file is also owned by root user.
david#machineA:~$ sudo su
[sudo] password for david:
root#machineA:/home/david# cd /opt/Potle/ouyt/wert/1
root#machineA:/opt/Potle/ouyt/wert/1# vi functon.txt
root#machineA:/opt/Potle/ouyt/wert/1# ssh david#machineB
david#machineB:~$ sudo su
[sudo] password for david:
root#machineB:/home/david# cd /opt/Potle/ouyt/wert/1
root#machineB:/opt/Potle/ouyt/wert/1# vi functon.txt
root#machineB:/opt/Potle/ouyt/wert/1# ssh david#machineC
.....
Now I have to do this in around 200 machines. Is there any way I can do these things through some script? I am ok typing passwords multiple times if I have to but I don't want to manually login into those box and do all the other steps by hand.
I have a file hosts.txt which contains each machine line by line. I can read this file line by line and do above things but I am not sure how?
This is just one time exercise for me so any easy or simple way should be fine. I can even hardcode my password in the script to do this job. What is the best way to accomplish this task?

After installing Ansible:
ansible -i /path/to/hosts.txt -m ping -u david --ask-pass all
See if you can ping the machines successfully. If it is successful, then try the following with 2 machines (create another txt file with just 2 machines and pass it to -i option). Then you can run this for all machines. If the directory does not exist, the command will fail and you will see the failed machines in summary.
ansible -i /path/to/hosts.txt -m copy -a "src=/path/to/functon.txt dest=/opt/Potle/ouyt/wert/1/functon.txt" -u david --ask-pass --become --become-user root --ask-become-pass all
I didn't test this. So use caution.
-i: input host(s)
-m: module
-a: module arguments
-u: user
--ask-pass: Ask for user password
--become: become another user
--become-user: new user
--ask-become-pass: Ask for become user password

You can use expect to automate SSH copy / SSH login :
#!/usr/bin/expect
set password [lindex $argv 1]
spawn scp -P 22 [lindex $argv 2] [lindex $argv 0]
expect "*password:*"
send -- "$password\r"
send -- "\r"
expect eof
The expect command will wait for the string you give in arguments to be received.
You can iterate over your hosts from hosts.txt and run this script like this for each one :
./create_config.sh david#machineA:/opt/Potle/ouyt/wert/1/ somePassword functon.txt
If you dont have possibility to do SSH copy but only SSH, you can still send command with expect :
#!/usr/bin/expect
set password [lindex $argv 1]
spawn ssh -p 22 [lindex $argv 0]
expect "*password:*"
send -- "$password\r"
send -- "\r"
# expect the command prompt : change this if needed
expect "*$*"
# execute some commands
send -- "echo 'some text to write to some file' > ~/some_file.txt\r"
# exit vm
send -- "exit\r"
expect eof
You can run this with :
./create_config.sh david#machineA somePassword

You could use sshfs: mount a machine, do what you want, unmount and pass to the next.

Related

Expect if condition with ssh password

I am currently trying to create a script with error handling.
Basically the script tests the ssh connection with this command :
ssh -o BatchMode=yes $machine uname -a
There is 3 potential situation that i want to handle :
SSH works just fine without password
SSH is blocked because the machine isn't in the known_hosts file in .ssh
SSH is blocked because the machine isn't in the known_hosts file in .ssh AND it requires a password to continue (which means the id_rsa.pub isn't in the authorized_keys file in .ssh
I have on main script that is calling an expect script here is what the main script looks like :
ssh -o BatchMode=yes ${machine} uname -a &> temp-file.txt 2>&1
# Here we test the ssh connection just once and we store the output of the command in a temp file
if [ $? -eq 255 ]
# If the ssh didn't work
then
if grep -q "Host key verification failed." temp-file.txt
# If the error message is "Host key verification failed."
then
expect script-expect-knownhosts.exp ${machine} 2>&1 >/dev/null
And here is the script-expect-knownhosts.exp file in which i tried to make a condition :
#!/usr/bin/expect -f
set machine [lindex $argv 0]
# Here we state that the first argument used with the command will be the $machine variable
set prompt "#|%|>|\$ $"
set timeout 60
spawn ssh $machine
# We do a ssh on the machine
set prompt "#|%|>|\$ $"
expect {
"Are you sure you want to continue connecting (yes/no)? " {send "yes\r";exp_continue}
# If he asks for a yes/no answer, then answer yes to add the machine to the known_hosts file
-exact "Password: " {send -- "^C";exp_continue}
# If he asks for a password, then send a CTRL + C
-re $prompt {send "exit\r";exp_continue}
# If the prompt shows up (if after the yes/no question, we don't need to put a password in) then type exit
}
So here is what happens when i execute the expect script with a machine in case number 2 (works just fine):
spawn ssh machine
Are you sure you want to continue connecting (yes/no)? yes
machine:~ # exit
deconnection
Connection to machine closed.
And here is what happens when i execute the expect script with a machine in case number 3 :
spawn ssh machine
Are you sure you want to continue connecting (yes/no)? yes
Password:
And it stays stuck on Password until i manually do a CTRL + C
In cases 2 and 3 you don't need exp_continue because you are stopping the connection process.
For case 2, I don't think you really want to send a control-C. When you do this interactively, typing control-C has the effect of sending a signal to kill the process you are interacting with. What you really want is to stop the ssh process, so instead of send -- "^C";exp_continue you should just do close.

How to ssh from one server to multiple server and switch to root and then change root password

From server A, i want to ssh multiple server using a non root user as direct root login is disabled to all server.
then i need to su - to switch to root
perform some normal operations, like changing directory, listing file etc.
and at the end change the root password using passwd command
I have expect installed on all servers and i am able to ssh from server A to any of the other servers but stuck in switching to root user and performing other operations as listed above.
#!/bin/bash
ssh user#ip<<'ENDSSH'
./su2root
#random operations
pwd
whoami
#random operations
./changepass
ENDSSH
su2root
#!/usr/bin/expect -f
spawn su -
set password "rootpass"
expect "assword:"
send "$password\r"
changepass
#!/usr/bin/expect -f
set newpassword "newrootpass"
spawn passwd
expect "*assword*"
send "$newpassword\r"
expect "*assword*"
send "$newpassword\r"
expect eof
exit 0
expecting to accomplish it using shell scripting only. Thanks in advance.
My initial comment on your question noted that you should be looking into running expect locally and spawning ssh sessions to the remote servers.
While not exactly answering your question, here is a simple expect script that logs into a remote system (one of my systems at home named valhalla), uses sudo -i to become root and executes the id command to show the user UID/GIDs. Note that I use ssh keys for logins which is why there is no expect for the initial login password.
The remote systems do not need expect in this example.
#!/usr/bin/expect
# vi: set ts=2 sw=2 noai ic showmode showmatch:
spawn ssh valhalla
expect "valhalla:"
send "sudo -i\r"
expect "assword for *"
send "XXXXXX\r"
expect "# "
send "id\r"
expect "(root)"
exit 0

how to use expect in linux? [duplicate]

I'm trying to use expect in a Bash script to provide the SSH password. Providing the password works, but I don't end up in the SSH session as I should. It goes back strait to Bash.
My script:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com'
expect "password"
send "$PWD\n"
EOD
echo "you're out"
The output of my script:
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
usr#$myhost.example.com's password: you're out
I would like to have my SSH session and, only when I exit it, to go back to my Bash script.
The reason why I am using Bash before expect is because I have to use a menu. I can choose which unit/device to connect to.
To those who want to reply that I should use SSH keys, please abstain.
Mixing Bash and Expect is not a good way to achieve the desired effect. I'd try to use only Expect:
#!/usr/bin/expect
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
# Use the correct prompt
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "my_password\r"
interact -o -nobuffer -re $prompt return
send "my_command1\r"
interact -o -nobuffer -re $prompt return
send "my_command2\r"
interact
Sample solution for bash could be:
#!/bin/bash
/usr/bin/expect -c 'expect "\n" { eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com; interact }'
This will wait for Enter and then return to (for a moment) the interactive session.
The easiest way is to use sshpass. This is available in Ubuntu/Debian repositories and you don't have to deal with integrating expect with Bash.
An example:
sshpass -p<password> ssh <arguments>
sshpass -ptest1324 ssh user#192.168.1.200 ls -l /tmp
The above command can be easily integrated with a Bash script.
Note: Please read the Security Considerations section in man sshpass for a full understanding of the security implications.
Add the 'interact' Expect command just before your EOD:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
expect "password"
send -- "$PWD\r"
interact
EOD
echo "you're out"
This should let you interact with the remote machine until you log out. Then you'll be back in Bash.
After looking for an answer for the question for months, I finally find a really best solution: writing a simple script.
#!/usr/bin/expect
set timeout 20
set cmd [lrange $argv 1 end]
set password [lindex $argv 0]
eval spawn $cmd
expect "assword:" # matches both 'Password' and 'password'
send -- "$password\r"; # -- for passwords starting with -, see https://stackoverflow.com/a/21280372/4575793
interact
Put it to /usr/bin/exp, then you can use:
exp <password> ssh <anything>
exp <password> scp <anysrc> <anydst>
Done!
A simple Expect script:
File Remotelogin.exp
#!/usr/bin/expect
set user [lindex $argv 1]
set ip [lindex $argv 0]
set password [lindex $argv 2]
spawn ssh $user#$ip
expect "password"
send "$password\r"
interact
Example:
./Remotelogin.exp <ip> <user name> <password>
Also make sure to use
send -- "$PWD\r"
instead, as passwords starting with a dash (-) will fail otherwise.
The above won't interpret a string starting with a dash as an option to the send command.
Use the helper tool fd0ssh (from hxtools, source for ubuntu, source for openSUSE, not pmt). It works without having to expect a particular prompt from the ssh program.
It is also "much safer than passing the password on the command line as sshpass does" ( - comment by Charles Duffy).
Another way that I found useful to use a small Expect script from a Bash script is as follows.
...
Bash script start
Bash commands
...
expect - <<EOF
spawn your-command-here
expect "some-pattern"
send "some-command"
...
...
EOF
...
More Bash commands
...
This works because ...If the string "-" is supplied as a filename, standard input is read instead...
sshpass is broken if you try to use it inside a Sublime Text build target, inside a Makefile. Instead of sshpass, you can use passh
With sshpass you would do:
sshpass -p pa$$word ssh user#host
With passh you would do:
passh -p pa$$word ssh user#host
Note: Do not forget to use -o StrictHostKeyChecking=no. Otherwise, the connection will hang on the first time you use it. For example:
passh -p pa$$word ssh -o StrictHostKeyChecking=no user#host
References:
Send command for password doesn't work using Expect script in SSH connection
How can I disable strict host key checking in ssh?
How to disable SSH host key checking
scp without known_hosts check
pam_mount and sshfs with password authentication

What's the best way to mix remote expect scripts and local bash commands?

I'm automating tasks on a local and remote machine (behind a firewall). Once I'm done with tasks on the remote machine, I'd like the script to return to executing commands on the local machine.
#!/usr/bin/expect -f
set timeout -1
spawn ssh username#host
expect "Password: "
send "mypassword\r"
expect "username#host:~$"
...do some stuff...
send "exit\r"
expect eof
[then, once on the local machine, change directories and do other things]
What's the best way to append bash commands? I suppose I could start with bash, call expect within it, then simply return to bash once expect is done.
Expect is based on Tcl, so it can run the same commands. But if your goal is to run bash commands, the best bet is to run them from bash as a separate script, exactly as you propose in your last sentence.
It really depends on what your idea of ...do some stuff... is. Here's an example of something I recently did from my OSX w/s to an AWS instance
export all_status
init_scripts=($(ssh -q me#somehost 'ls /etc/init.d'))
for this_init in ${init_scripts[#]};do
all_status="${all_status}"$'\n\n'"${this_init}"$'\n'"$(ssh -q somehost \'sudo /etc/init.d/${this_init} status\')"
done
echo "$all_status" > ~/somehost_StatusReport.txt
unset all_status
Passing a command at the end of the ssh command will cause the command to be run on the remote host. Or you can scp a script to the remote host and run it with
ssh somehost '/home/me/myscript'
I met this situation recently too. I make a shell supexpect.sh which could login and execute command automatically. It will return to your local shell at the end.
#!/usr/bin/expect
#Usage:supexpect <host ip> <ssh username> <ssh password> <commands>
set timeout 60
spawn ssh [lindex $argv 1]#[lindex $argv 0] [lindex $argv 3]
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "[lindex $argv 2]\r" }
} "*?assword" { send "[lindex $argv 2]\r" }
send "exit\r"
expect eof
To execute:
./supexpect.sh 10.89.114.132 username password "ls -a;pwd;your_stuff_on_remote_host"
Note:
The prompt might need to adapt to your own system, and of course you need to pass execute permission to it.

SSH command behaving differently in expect script

I am creating a script that is executed from host-A to connect to a remote host-b, runs a bash script (which generates monitoring data from top etc.) on host-b, and appends the output back to a file on host-A. While I use ssh to connect, I will not be using ssh-keys as there are simply way too many servers in the environment.
My expect script:
"#!/usr/bin/expect
"#!/usr/bash
set timeout 10
set hostname [sample_hostname]
set user "(sample_user)"
set password "(sample_password)"
spawn ssh $user#$hostname /path/to/bash/script/script.sh | cat >> /tmp/file
expect "Password:"
send "$password\r";
expect "$"
====================================
The above script appends the output of the bash script into host-B /tmp/file instead of the desired host-A /tmp/file. How can I change to have script append to host-A instead of host-b?
P.S.
manually logging into host-A
cd to script location
run ssh user#hostname /path/to/script/script.sh | cat >> /tmp/file on host-A
cat /tmp/file
Above manual steps performs as desired; hence my idea that problem is with bash in expect.
Any ideas are appreciated!
Regards,
Alan
If your intention is to get just the output of that script, then proceed with the following way.
#!/usr/bin/expect
set timeout 10
set hostname host
set user dinesh
set password pwd
set script "/home/dinesh/mypgms/demo.sh"
spawn ssh $user#$hostname
expect "password:"
send "$password\r";
expect "\\\$"; # Matching the literal dollar prompt sign of terminal
send "$script\r"
expect -re "\r\n(.*)\\\$"; # Matching the output
# Getting first sub-match as result
puts "\nResult : --->$expect_out(1,string)"
This will give the output as
dinesh#dinesh-VirtualBox:~/stackoverflow$ ./alan
spawn ssh dinesh#host
dinesh#host's password:
Last login: Tue Apr 21 13:30:35 2015
[dinesh# ~]$ /home/dsivaji/mypgms/demo.sh
{\"INFO\":0}\0\r
{\"INFO\":0}\0\r
[dinesh#~]$
Result : --->{\"INFO\":0}\0\r
{\"INFO\":0}\0\r
[dinesh#~]
dinesh#dinesh-VirtualBox:~/stackoverflow$
Note that the result as
{\"INFO\":0}\0\r
{\"INFO\":0}\0\r
[dinesh#~]
Since we are matching upto the prompt $, it contains till that point. You can customize the regular expression based on the shell output also.
I'm assuming you are using the expect/set method beacuse of the password.
I found another application called sshpass once installed you can just run the command like this inside your bash.
sshpass -p PASSWORDHERE ssh user#hostname /path/to/bash/script/script.sh | cat >> /tmp/file
http://linux.die.net/man/1/sshpass
enjoy

Resources