I'm using expect (v5.45.4-2build1) to automate generation of client certs using easy-rsa in ubuntu focal.
As per easy-rsa, to customize cert validity we need to pass EASYRSA_CERT_EXPIRE variable.
But for some reason, my expect script is not able to fetch this variable.
Here's the code for script.exp:
#!/usr/bin/expect -f
set timeout -1
#exp_internal 1
set MY_PASSPHRASE [lindex $argv 0];
set USERNAME [lindex $argv 1];
set ttl [lindex $argv 2];
send "export EASYRSA_CERT_EXPIRE=$ttl\r"
spawn /bin/bash
set MSG "Executing script.exp for $USERNAME and $ttl";
send "echo 'INFO $MSG'>> /var/log/app/my_app.log\r"
send "cd /etc/openvpn/easy-rsa\r"
send "export EASYRSA_CERT_EXPIRE=$ttl\r"
send "./easyrsa gen-req $USERNAME\r"
expect "*Enter PEM pass phrase:"
send -- "$MY_PASSPHRASE\r"
expect "*Verifying - Enter PEM pass phrase:"
send -- "$MY_PASSPHRASE\r"
expect "*Common Name *$USERNAME*"
send -- "\r"
expect "*Keypair and certificate request completed*"
send -- "\r"
exit 0
Strangely, this works fine if i run the script via expect script.exp.
But I want to use this in my perl script and it is not working from the perl script.
This is how I'm calling my expect script:
my $cmd_out_csr = system("expect script.exp $my_passphrase $username $ttl");
Just running, expect script.exp $my_passphrase $username $ttl from cmd works fine.
Any help is appreciated.
Thanks.
use the perl system command with a list of arguments:
system('expect', 'script.exp', $my_passphrase, $username, $ttl);
when creating the variable in the remote shell, send quotes around the value:
send "export EASYRSA_CERT_EXPIRE=\"$ttl\"\r"
These two things will protect the value of $ttl if it contains whitespace.
Another thought: expect (via Tcl) can cd and set environment variables. Instead of spawning a bash shell, spawn easyrsa directly:
#!/usr/bin/expect -f
set timeout -1
#exp_internal 1
lassign $argv MY_PASSPHRASE USERNAME ttl
set f [open /var/log/app/my_app.log a]
puts $f "INFO Executing [info script] for $USERNAME and $ttl"
close $f
cd /etc/openvpn/easy-rsa
set env(EASYRSA_CERT_EXPIRE) $ttl
spawn ./easyrsa gen-req $USERNAME
expect "*Enter PEM pass phrase:"
send -- "$MY_PASSPHRASE\r"
expect "*Verifying - Enter PEM pass phrase:"
send -- "$MY_PASSPHRASE\r"
expect "*Common Name *$USERNAME*"
send -- "\r"
expect "*Keypair and certificate request completed*"
send -- "\r"
expect eof
puts "Done."
Related
I am working on a project that requires some assistance.
I have automated most of the information required for the completion of this project but the only thing that is lagging is the running of local shell scripts on the remote machine.
As we are aware that no Linux command is recognized by the script that uses the 'expect' library.
Herein we have two use cases that I have tried:
1) Running the desired list of commands on the remote server using only one expect script which has both the script execution as well as pushing of output using scp to the local machine, here is a snippet of this code:
`chmod 777 localscript.sh
cat > script1.sh <<- "ALL"`
`#!/usr/bin/expect
set password [lindex $argv 0];
set ipaddress [lindex $argv 1];
set timevalue [lindex $argv 2];
set timeout $timevalue
spawn /usr/bin/ssh username#$ipaddress /bin/bash < ./localscript.sh
expect "assword:"
send "$password\r"
set timeout $timevalue
spawn /usr/bin/scp username#$2:"/path/from/source/*" /path/to/destination/folder/
expect "assword:"
send "$password\r"
interact
ALL
chmod 777 script1.sh
./script1.sh $password $2 $timevalue`
2) Running the desired list of commands on the remote server in a separate expect script and using scp to get files in a different script:
`cat > script1.sh <<- "ALL" `
`#!/usr/bin/expect
set password [lindex $argv 0];
set ipaddress [lindex $argv 1];
set timevalue [lindex $argv 2];
set timeout $timevalue
spawn /usr/bin/ssh username#$ipaddress /bin/bash < ./localscript.sh
expect "assword:"
send "$password\r"
interact
ALL
cat > script2.sh <<- "ALL2"`
`#!/usr/bin/expect
set password [lindex $argv 0];
set ipaddress [lindex $argv 1];
set timevalue [lindex $argv 2];
set timeout $timevalue
spawn /usr/bin/scp username#ipaddress:"/path/from/source/*" /path/to/destination/folder/
expect "assword:"
send "$password\r"
interact
ALL2
chmod 777 localscript.sh script1.sh script2.sh
./script1.sh $password $2 $timevalue
sleep 5
./script2.sh $password $2 $timevalue`
I believe the above codes should both be valid in their own respect however, the output for the same seem to be quite unexpected:
1) Both the commands ssh and scp are being executed almost simultaneously after password is entered hence, it is not giving localscript enough time to do its job, here's the output I see:
spawn /usr/bin/ssh username#1.2.3.4 /bin/bash < ./localscript.sh
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password: spawn /usr/bin/scp
username#1.2.3.4:"/home/some/file/*" /another/file/
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password:
scp: /home/some/file/*: No such file or directory
Please note: This functionality is working fine without the involvement of expect
2) Here we are executing ssh and scp separately, however, it seems like it is unable to recognize that the file localscript.sh exists:
spawn /usr/bin/ssh username#1.2.3.4 /bin/bash < ./localscript.sh
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password:
bash: localscript.sh: No such file or directory
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password:
scp: /home/some/file/*: No such file or directory
Any feedback on the same would be appreciated, I think the first approach might be a feasible solution, except the fact that spawn is too fast and none of the 'sleep' or 'after' commands are helping/working. I think the second approach is also valid however it seems like there is a different way of running a local script on a remote server than the usual way we do on Linux when using 'expect'.
Sorry for so much elaboration, I am hoping to be out of my misery soon :)
Indeed the timeout you are setting is not working as you expect it to. Both scripts are spawned, and the expect "assword:" after each spawn is actually catching and reacting to the same password prompt.
expect is actually more sophisticated than a cursory glance would lead you to believe. Each spawn should return a PID, which you can use with your expect to look for output from a specific process.
expect can also be broken down into multiple parts, and have the ability to define subroutines. Here are some more advanced use examples https://wiki.tcl-lang.org/10045
In this specific case I would suggest waiting for the scp to complete before spawning the next process.
expect {
"assword:" {
send "$password\r"
exp_continue # Keep expecting
}
eof {
puts -nonewline "$expect_out(buffer)"
# eof so the process should be done
# It is safe to execute the next spawn
# without exp_continue this expect will
# break and continue to the next line
}
}
I am running this script to copy pattern files in local but it is asking for password even passing the hardcode value.
Script:
PASSWORD="xyz"
sftp -oport=1002 user#host:/dir/archive/file*.txt /di/data/
<< EOF
$PASSWORD
quit
EOF
Got error:
-bash-4.1$ sh sftp_with_password.sh
Connecting to user#host...
Password Authentication
An expect script could do the job.
For instance copy-sftp.exp:
#!/usr/bin/expect
set password [lindex $argv 0]
spawn sftp -oport=1002 user#host:/dir/archive/file*.txt /di/data/
expect "Password"
send "$password\r"
send "quit\r"
Then you can execute it:
expect copy-ftp.exp $PASSWORD
Thanks to Gonzalo Matheu I've managed to do a send-sftp.exp using a proxy which send a big file to a sftp :
#!/usr/bin/expect
set timeout -1
set password [lindex $argv 0]
set file [lindex $argv 1]
spawn sftp -o "ProxyCommand /usr/bin/connect -H <proxy-host>:<proxy-port> %h %p" <sftp-Username>#<sftp-HOST>
expect "password:"
send "$password\r"
expect "sftp>"
send "put $file\r"
expect "sftp>"
send "quit\r"
Then I execute it :
expect send-sftp.exp $PASSWORD /PATH/TO/MYFILE.zip
set timeout allows the transfer not to be stoped by expect's wait timeout, you can adapt it to your needs ;)
Iam trying to exceute a command after logging in to a linux RHEL box using expect and interact.
Below is script
#!/usr/bin/expect
set timeout 100
set temp [lindex $argv 0]
spawn ssh userid#10.20.30.40
expect "Password:"
send "password\n";
interact
expect "*3.2*"
send "./p.sh\n";
Its successfully logging in to the box but after that its not excecuting the command.
This is the actual output of the commnad after login , which iam trying to exceute.
Using keyboard-interactive authentication.
Password:
Last login: Sun Mar 22 11:04:01 2015 from com
-sh-3.2$ pbrun pbapp wasapp=ksh
Please note home directories are intended only for user/application profiles.
$
These are the errors i received
-sh-3.2$ exit
logout
Connection to 10.20.30.40 closed.
expect: spawn id exp7 not open
while executing
"expect "*$""
(file "./testWas.sh" line 8)
when i try
expect "*$"
exec "pwd"
-sh-3.2$ exit
logout
Connection to 10.20.30.40 closed.
couldn't execute " pwd ": no such file or directory
while executing
"exec { pwd }"
(file "./testWas.sh" line 8)
Edit:
Thanks to red #glenn jackman
iam able to excute pbrun commands after login..
But after excecuting the pbrun command script is exiting
#!/usr/bin/expect
set timeout 100
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set command [lindex $argv 3]
spawn ssh $username#$host expect "Password:"
send "$password\n";
expect -re {\$ $} ; # put here string from your server prompt
send "./p.sh\n";
expect -re {\$ $} ;
send "pwd\n";
This is the content of p.sh
Only first line of the script is executing..
-sh-3.2$ cat p.sh
pbrun pbapp wsapp=ksh
pwd
clear
-sh-3.2$
There is a similar unanswered question
How to run "pbrun pbapp wasapp=ksh" command using SSH java client?
interact tells expect that you are going into manual mode, where you, the human, is in control of the spawned command. I see you then typed exit which ended the ssh session. Since the spawned command ended, the interact command ended and control returned to the script. The next command dies because the spawned command is not running.
Simply put, remove interact:
#!/usr/bin/expect
set timeout 100
set temp [lindex $argv 0]
spawn ssh userid#10.20.30.40
expect "Password:"
send "password\r" # a carriage return more exactly represents
# "hitting enter"
expect -re {\$ $} # this regular expression matches the end of the prompt
send "./p.sh\r"
if { you want to interact manually with the ssh session } {
interact
} else {
expect -re {\$ $} # if p.sh exits the ssh session, remove this line
send "exit\r" # and this one too.
expect eof
}
I am new to Linux Shell Scripting.I need to execute a shell script that gets username, password, host-name, command-to-be-executed as 1,2,3,4 parameters respectively. I used expect command but it prompts for password again.The main theme of this script is to execute a command from a remote server. Password security issues is not a problem. The main issue is that I must not be prompted for password input. I have to run this for about 80 servers, so I cant provide password for each and every time. Please help me solving this.
My script:
echo username = $1
echo Password = $2
echo Host-Name = $3
echo Command to Be executed = $4
expect -c "spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no $1#$3 $4; expect \"*assword:*\"; send "$2"; interact
#expect eof
"
My Output:
username = root
Password = root#123
Host-Name = host-name
Command to Be executed = ls
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no root#host-name ls
root#host-name's password:
~
first your index will start at 0 not 1 so username = $0
also you need to set your variables like so: (this is the expect script i use on sever hundred nodes)
#!/usr/bin/expect
set user [lindex $argv 0]
set password [lindex $argv 2]
set ip [lindex $argv 2]
set command [lindex $argv 3]
spawn ssh -o StrictHostKeyChecking=no "$user\#$ip"
expect "assword:"
send "$password\r";
expect "$ "
send "$command\r"
expect "$ "
send "exit\r"
I have to pass a path at runtime as the abc.sh script will prompt for it .. I am trying following expect script to accomplish this,
#!/usr/bin/expect -f
set force_conservative 0
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
spawn abc.sh
match_max 100000
expect -exact "Please enter path : "
send -- $path
expect -exact "\r
expect eof
Need your help – how I can pass $path values at run time calling like this ?
expect script.exp $path
Thanks,
Expect Answer
Use send -- $argv in your expect script because $argv holds the command line arguments.
As an example, I've this handy expect script that interacts with ssh commands when admins stupidly disable public key authentication :
#!/usr/bin/expect -f
set timeout -1
set send_human {.6 .1 1.6 .1}
eval spawn $argv
match_max 100000
expect {
-re "LOGIN#(\[0-9A-Za-z_\\-\\.\]+)'s password: "
{ sleep 0.6 ; send -- "PASSWORD" ; sleep 0.1 }
interact
Use constructs like [lrange $argv 0 $argc] if you need only part of the $argv
Perl Answer
Use system 'script.exp' $path to call script.exp from a perl script, assuming use chmod, etc., roughly like DeVadder said sans the quotes. Or use exec if your perl script wishes to terminate. Run man perlfunc to read about perl commands.