Setting variables in expect script has no effect - macos

I'm trying to write a script to get a password from my Mac keychain and use it to add a password protected key to the keychain (using ssh-add). It's meant to be part of a collection of scripts that get sourced by zsh. Currently my scripts looks like this
cat <<EOF | expect -d
set password [exec /usr/bin/security find-generic-password -s SSH -w]
puts "$password"
spawn -noecho /usr/bin/ssh-add $::env(HOME)/.ssh/id_ecdsa
expect -timeout 3 "Enter passphrase for"
send "$password\r"
interact
EOF
The puts "$password" is purely there for debug purposes. And I get the following results
$ source .zshrc.d/090_ssh_keys
Password:
Enter passphrase for /Users/james.dominy/.ssh/id_ecdsa:
$ ssh-add -l
The agent has no identities.
As you can see, the password variable isn't being set; I've tried changing the variable name, and as a debugging effort, I've tried setting it to a number (set password 1) and to a string literal (set password "a") but those don't work either. However, if I cut and paste this into an interactive expect session, everything works as expected
expect1.1> set password [exec /usr/bin/security find-generic-password -s SSH -w]
**********
expect1.2> puts $password
**********
expect1.3> spawn -noecho /usr/bin/ssh-add $::env(HOME)/.ssh/id_ecdsa
89080
expect1.4> expect -timeout 3 "Enter passphrase for"
Enter passphrase for /Users/sirlark/.ssh/id_ecdsa: expect1.5> send "$password\r"
expect1.6> interact
Identity added: /Users/sirlark/.ssh/id_ecdsa (sirlark main ssh key)
expect1.7> %
Password obscured obviously, but this works. Why?

Related

Can I pass bash arguements to an expect environment?

I need to automate an openvpn connection to a server that requires me to enter a password.
I can do this with expect but I don't want to keep the password in plain text in the script.
I found encpass to help encrypt the password which I just need to source and get it to get the encrypted version of the password.
The problem comes when I try to pass the unencrypted password to expect. From what I understand, expect and bash are 2 different environments and bash cannot run expect. What I have so far is the following:
#!/usr/bin/env bash
source encpass.sh
password=$(get_secret)
{
/usr/bin/expect <<EOF
spawn openvpn /home/pi/client.ovpn
expect "Enter Private Key Password:"
send $password
interact
EOF
}
The end result is I run this and it starts the VPN and the script enters the password in the prompt.
If there is a simpler way of doing it, please let me know.
I have tried to automate it with just openvpn and a --auth-user-pass switch pointing to a file with the password in it but I couldn't get that working either.
Two ideas spring to mind:
if you want to embed expect code into a shell script, use the environment
to pass values, and use a quoted heredoc to avoid quoting hell (don't forget
to "hit enter" for the send command)
#!/usr/bin/env bash
source encpass.sh
password=$(get_secret)
export password
/usr/bin/expect <<'EOF'
spawn openvpn /home/pi/client.ovpn
expect "Enter Private Key Password:"
send "$env(password)\r"
interact
EOF
do it all in expect
#!/usr/bin/env expect
set password [exec bash -c {source encpass.sh && get_secret}]
spawn openvpn /home/pi/client.ovpn
expect "Enter Private Key Password:"
send "$password\r"
interact

How to ssh in a non-interactive bash script with the password [duplicate]

I need to create a script that automatically inputs a password to OpenSSH ssh client.
Let's say I need to SSH into myname#somehost with the password a1234b.
I've already tried...
#~/bin/myssh.sh
ssh myname#somehost
a1234b
...but this does not work.
How can I get this functionality into a script?
First you need to install sshpass.
Ubuntu/Debian: apt-get install sshpass
Fedora/CentOS: yum install sshpass
Arch: pacman -S sshpass
Example:
sshpass -p "YOUR_PASSWORD" ssh -o StrictHostKeyChecking=no YOUR_USERNAME#SOME_SITE.COM
Custom port example:
sshpass -p "YOUR_PASSWORD" ssh -o StrictHostKeyChecking=no YOUR_USERNAME#SOME_SITE.COM:2400
Notes:
sshpass can also read a password from a file when the -f flag is passed.
Using -f prevents the password from being visible if the ps command is executed.
The file that the password is stored in should have secure permissions.
After looking for an answer to the question for months, I finally found a better 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 "password:"
send "$password\r";
interact
Put it to /usr/bin/exp, So you can use:
exp <password> ssh <anything>
exp <password> scp <anysrc> <anydst>
Done!
Use public key authentication: https://help.ubuntu.com/community/SSH/OpenSSH/Keys
In the source host run this only once:
ssh-keygen -t rsa # ENTER to every field
ssh-copy-id myname#somehost
That's all, after that you'll be able to do ssh without password.
You could use an expects script. I have not written one in quite some time but it should look like below. You will need to head the script with #!/usr/bin/expect
#!/usr/bin/expect -f
spawn ssh HOSTNAME
expect "login:"
send "username\r"
expect "Password:"
send "password\r"
interact
Variant I
sshpass -p PASSWORD ssh USER#SERVER
Variant II
#!/usr/bin/expect -f
spawn ssh USERNAME#SERVER "touch /home/user/ssh_example"
expect "assword:"
send "PASSWORD\r"
interact
sshpass + autossh
One nice bonus of the already-mentioned sshpass is that you can use it with autossh, eliminating even more of the interactive inefficiency.
sshpass -p mypassword autossh -M0 -t myusername#myserver.mydomain.com
This will allow autoreconnect if, e.g. your wifi is interrupted by closing your laptop.
With a jump host
sshpass -p `cat ~/.sshpass` autossh -M0 -Y -tt -J me#jumphost.mydomain.com:22223 -p 222 me#server.mydomain.com
sshpass with better security
I stumbled on this thread while looking for a way to ssh into a bogged-down server -- it took over a minute to process the SSH connection attempt, and timed out before I could enter a password. In this case, I wanted to be able to supply my password immediately when the prompt was available.
(And if it's not painfully clear: with a server in this state, it's far too late to set up a public key login.)
sshpass to the rescue. However, there are better ways to go about this than sshpass -p.
My implementation skips directly to the interactive password prompt (no time wasted seeing if public key exchange can happen), and never reveals the password as plain text.
#!/bin/sh
# preempt-ssh.sh
# usage: same arguments that you'd pass to ssh normally
echo "You're going to run (with our additions) ssh $#"
# Read password interactively and save it to the environment
read -s -p "Password to use: " SSHPASS
export SSHPASS
# have sshpass load the password from the environment, and skip public key auth
# all other args come directly from the input
sshpass -e ssh -o PreferredAuthentications=keyboard-interactive -o PubkeyAuthentication=no "$#"
# clear the exported variable containing the password
unset SSHPASS
I don't think I saw anyone suggest this and the OP just said "script" so...
I needed to solve the same problem and my most comfortable language is Python.
I used the paramiko library. Furthermore, I also needed to issue commands for which I would need escalated permissions using sudo. It turns out sudo can accept its password via stdin via the "-S" flag! See below:
import paramiko
ssh_client = paramiko.SSHClient()
# To avoid an "unknown hosts" error. Solve this differently if you must...
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# This mechanism uses a private key.
pkey = paramiko.RSAKey.from_private_key_file(PKEY_PATH)
# This mechanism uses a password.
# Get it from cli args or a file or hard code it, whatever works best for you
password = "password"
ssh_client.connect(hostname="my.host.name.com",
username="username",
# Uncomment one of the following...
# password=password
# pkey=pkey
)
# do something restricted
# If you don't need escalated permissions, omit everything before "mkdir"
command = "echo {} | sudo -S mkdir /var/log/test_dir 2>/dev/null".format(password)
# In order to inspect the exit code
# you need go under paramiko's hood a bit
# rather than just using "ssh_client.exec_command()"
chan = ssh_client.get_transport().open_session()
chan.exec_command(command)
exit_status = chan.recv_exit_status()
if exit_status != 0:
stderr = chan.recv_stderr(5000)
# Note that sudo's "-S" flag will send the password prompt to stderr
# so you will see that string here too, as well as the actual error.
# It was because of this behavior that we needed access to the exit code
# to assert success.
logger.error("Uh oh")
logger.error(stderr)
else:
logger.info("Successful!")
Hope this helps someone. My use case was creating directories, sending and untarring files and starting programs on ~300 servers as a time. As such, automation was paramount. I tried sshpass, expect, and then came up with this.
# create a file that echo's out your password .. you may need to get crazy with escape chars or for extra credit put ASCII in your password...
echo "echo YerPasswordhere" > /tmp/1
chmod 777 /tmp/1
# sets some vars for ssh to play nice with something to do with GUI but here we are using it to pass creds.
export SSH_ASKPASS="/tmp/1"
export DISPLAY=YOURDOINGITWRONG
setsid ssh root#owned.com -p 22
reference: https://www.linkedin.com/pulse/youre-doing-wrong-ssh-plain-text-credentials-robert-mccurdy?trk=mp-reader-card
This is how I login to my servers:
ssp <server_ip>
alias ssp='/home/myuser/Documents/ssh_script.sh'
cat /home/myuser/Documents/ssh_script.sh
ssp:
#!/bin/bash
sshpass -p mypassword ssh root#$1
And therefore:
ssp server_ip
This is basically an extension of abbotto's answer, with some additional steps (aimed at beginners) to make starting up your server, from your linux host, very easy:
Write a simple bash script, e.g.:
#!/bin/bash
sshpass -p "YOUR_PASSWORD" ssh -o StrictHostKeyChecking=no <YOUR_USERNAME>#<SEVER_IP>
Save the file, e.g. 'startMyServer', then make the file executable by running this in your terminal:
sudo chmod +x startMyServer
Move the file to a folder which is in your 'PATH' variable (run 'echo $PATH' in your terminal to see those folders). So for example move it to '/usr/bin/'.
And voila, now you are able to get into your server by typing 'startMyServer' into your terminal.
P.S. (1) this is not very secure, look into ssh keys for better security.
P.S. (2) SMshrimant answer is quite similar and might be more elegant to some. But I personally prefer to work in bash scripts.
I am using below solution but for that you have to install sshpass If its not already installed, install it using sudo apt install sshpass
Now you can do this,
sshpass -p *YourPassword* ssh root#IP
You can create a bash alias as well so that you don't have to run the whole command again and again.
Follow below steps
cd ~
sudo nano .bash_profile
at the end of the file add below code
mymachine() { sshpass -p *YourPassword* ssh root#IP }
source .bash_profile
Now just run mymachine command from terminal and you'll enter your machine without password prompt.
Note:
mymachine can be any command of your choice.
If security doesn't matter for you here in this task and you just want to automate the work you can use this method.
If you are doing this on a Windows system, you can use Plink (part of PuTTY).
plink your_username#yourhost -pw your_password
I have a better solution that inclueds login with your account than changing to root user.
It is a bash script
http://felipeferreira.net/index.php/2011/09/ssh-automatic-login/
The answer of #abbotto did not work for me, had to do some things differently:
yum install sshpass changed to - rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/sshpass-1.05-1.el6.x86_64.rpm
the command to use sshpass changed to - sshpass -p "pass" ssh user#mysite -p 2122
I managed to get it working with that:
SSH_ASKPASS="echo \"my-pass-here\""
ssh -tt remotehost -l myusername
This works:
#!/usr/bin/expect -f
spawn ssh USERNAME#SERVER "touch /home/user/ssh_example"
expect "assword:"
send "PASSWORD\r"
interact
BUT!!! If you have an error like below, just start your script with expect, but not bash, as shown here: expect myssh.sh
instead of bash myssh.sh
/bin/myssh.sh: 2: spawn: not found /bin/myssh.sh: 3: expect: not found /bin/myssh.sh: 4: send: not found /bin/myssh.sh: 5: expect: not found /bin/myssh.sh: 6: send: not found
I got this working as follows
.ssh/config was modified to eliminate the yes/no prompt - I'm behind a firewall so I'm not worried about spoofed ssh keys
host *
StrictHostKeyChecking no
Create a response file for expect i.e. answer.expect
set timeout 20
set node [lindex $argv 0]
spawn ssh root#node service hadoop-hdfs-datanode restart
expect "*?assword {
send "password\r" <- your password here.
interact
Create your bash script and just call expect in the file
#!/bin/bash
i=1
while [$i -lt 129] # a few nodes here
expect answer.expect hadoopslave$i
i=[$i + 1]
sleep 5
done
Gets 128 hadoop datanodes refreshed with new config - assuming you are using a NFS mount for the hadoop/conf files
Hope this helps someone - I'm a Windows numpty and this took me about 5 hours to figure out!
In the example bellow I'll write the solution that I used:
The scenario: I want to copy file from a server using sh script:
#!/usr/bin/expect
$PASSWORD=password
my_script=$(expect -c "spawn scp userName#server-name:path/file.txt /home/Amine/Bureau/trash/test/
expect \"password:\"
send \"$PASSWORD\r\"
expect \"#\"
send \"exit \r\"
")
echo "$my_script"
Solution1:use sshpass
#~/bin/myssh.sh
sshpass -p a1234b ssh myname#somehost
You can install by
# Ubuntu/Debian
$ sudo apt-get install sshpass
# Red Hat/Fedora/CentOS
$ sudo yum install sshpass
# Arch Linux
$ sudo pacman -S sshpass
#OS X
brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb
or download the Source Code from here, then
tar xvzf sshpass-1.08.tar.gz
cd sshpass-1.08.tar.gz
./configure
sudo make install
Solution2:Set SSH passwordless login
Let's say you need to SSH into bbb#2.2.2.2(Remote server B) with the password 2b2b2b from aaa#1.1.1.1(Client server A).
Generate the public key(.ssh/id_rsa.pub) and private key(.ssh/id_rsa) in A with the following commands
ssh-keygen -t rsa
[Press enter key]
[Press enter key]
[Press enter key]
Use the following command to distribute the generated public key(.ssh/id_rsa.pub) to server B under bbb‘s .ssh directory as a file name authorized_keys
ssh-copy-id bbb#2.2.2.2
You need to enter a password for the first ssh login, and it will be logged in automatically in the future, no need to enter it again!
ssh bbb#2.2.2.2 [Enter]
2b2b2b
And then your script can be
#~/bin/myssh.sh
ssh myname#somehost
Use this script tossh within script, First argument is the hostname and second will be the password.
#!/usr/bin/expect
set pass [lindex $argv 1]
set host [lindex $argv 0]
spawn ssh -t root#$host echo Hello
expect "*assword: "
send "$pass\n";
interact"
To connect remote machine through shell scripts , use below command:
sshpass -p PASSWORD ssh -o StrictHostKeyChecking=no USERNAME#IPADDRESS
where IPADDRESS, USERNAME and PASSWORD are input values which need to provide in script, or if we want to provide in runtime use "read" command.
This should help in most of the cases (you need to install sshpass first!):
#!/usr/bin/bash
read -p 'Enter Your Username: ' UserName;
read -p 'Enter Your Password: ' Password;
read -p 'Enter Your Domain Name: ' Domain;
sshpass -p "$Password" ssh -o StrictHostKeyChecking=no $UserName#$Domain
In linux/ubuntu
ssh username#server_ip_address -p port_number
Press enter and then enter your server password
if you are not a root user then add sudo in starting of command

How to add carriage return to bash when prompted for ssh password to localhost?

I'm new to bash and was tasked with scripting a check for a compliance process.
From bash (or if python is better), I need to script an ssh connection from within the host running the script.
For example:
ssh -l testaccount localhost
But I need to run this 52 times so that it is trapped by an IPS.
When running this string I am prompted for a password and I have to hit enter in order to make the script complete.
Is there a way to include a password or carriage return to act as manual intervention so that I do not have to hit enter each time?
Here's a sample of what I was able to get working, but it only sequenced 30 attempts:
#!/bin/bash
i=0
while [$i -lt 52]
do
echo | ssh -l testaccount localhost&
i=$[$i+1]
done
Fail2ban configuration and good practice :
//count how password as root failed
cat /var/log/secure.1 | grep "Failed password for root" --count
//check the list for analyst
cat /var/log/secure.1 | grep "Failed password for root"
//setting fail2ban copy for local configuration
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
//open the configuration file and edit some secu
sudo nano /etc/fail2ban/jail.local
maxfailures = 5 //here you have to set to 56 as you said
bantime = 600 //here time during second try login
ignoreip = 192.168.0.0/16 //add the lan and ip online static
[mail]
enabled = false //true if you want to alert you IP blacklisted banned ...
//log traffic
cat /var/log/fail2ban.log
[ssh]
//network protocole protection & supervision
enabled = true
port = ssh,sftp
filter = sshd
logpath = /var/log/auth.log
maxretry = 6
//enable fail2ban
systemctl enable fail2ban
//start fail2ban
systemctl start fail2ban
NOTE: While expect comes in its own package, expect was already in my SLES base installs ... don't know if this would be true for RHEL, too ... ?
Take a look at this answer on how to automate SSH with a password.
I'm thinking you could probably re-use the expect script from that post to simulate a single failed login, eg:
either set pass to a bogus value or don't set at all
if you don't set pass then remove the send -- "$pass\r" clause
if the remote system re-prompts X times for a password then provide multiple copies of the expect/send commands (a few extras will generate some expect related errors but still cause a failed ssh login)
For one of my remote hosts I'm prompted 3 times to enter a password before I'm returned to the command prompt.
I whipped up the following test:
$ cat sshtest
#!/usr/bin/expect -f
set pass xyz
set server myremotehost
set name bob
spawn ssh $name#$server
match_max 100000
expect "*?assword:*"
send -- "\r"
expect "*?assword:*"
send -- "\r"
expect "*?assword:*"
send -- "\r"
expect "*?assword:*"
send -- "\r"
expect "*?assword:*"
send -- "\r"
interact
And the results of running the script:
$ sshtest
spawn ssh bob#myremotehost
Password:
Password:
Password:
Permission denied (publickey,keyboard-interactive).
send: spawn id exp4 not open
while executing
"send -- "\r""
(file "sshtest" line 16)
If you don't have enough expect/send pairs then you'll be left stranded with a Password: prompt, so I added a few extra expect/send pairs, which in turn generated those last 4 lines of the output. [I don't use expect so there may be a more graceful way to do this ... ymmv.]
Obviously your main script could call this script and place said call in the background, and do whatever you want with the output (>/dev/null 2>&1 ??)
I also verified on the remote host that the failed logins were logged in /var/log/warn and /var/log/messages.

SSH in the most convenient way without typing in the ssh command everytime

I constantly need to log into a remote server and do all kinds of work. I am using my Mac and every time I have to type in a long command to do that.
ssh -i ~/key.pem ubuntu#255.255.255.255
I am wondering what would be the easiest way to log into the remote server without typing in the command everyday.
Handy apple apps are also welcome
Updated:
1> Thanks to Matt Bryant's answer. I successfully avoided typing in the whole username and host address by modifying ~/.ssh/config (doesn't exist as default) to
Host <ShortName>
User <UserName>
HostName <HostIP>
IdentityFile <KeyPath>
then I could just type this command to avoid typing in the full name of host and full path of the key:
ssh <ShortName>
2> Anyone knows how to store the pem key or avoid typing in the password if there is no pem key?
Use the ~/.ssh/config file. Try adding the following to it:
Host server
User ubuntu
HostName 255.255.255.255
This will allow you to login using ssh -i ~/key.pem server. If you look up ssh config, there are many other settings that will allow you to simplify and enhance your ssh experience.
Hi B MR W yes I am going to post up an expect script since this comes up a lot:
E2A The instructions are for linux
There is expect on mac https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/expect.1.html
It might be mean messing with the script to meet the criteria of the mac expect I don't have enough knowledge on that but in principal having had a look the below process should work
Which bit was you interested in the ssh-copy-id or a script?
to get a script going you need to
sudo-apt-get install expect or
sudo yum install expect
Once installed
This is a typical script:
#!/usr/bin/expect -f
set force_conservative 1
if {$force_conservative} {
set send_slow {1 .001}
proc send {ignore arg} {
sleep .001
exp_send -s -- $arg
}
}
;# Validate user input - make sure all fields required are given
if {$argc >= 1} {
;# Setting password
set user "MYUSER";
set supass "MYPASSWORD"
set host [lindex $argv 0]
# set command1 [lindex $argv 2]
set prompt "(:|#|%|>|\\\$|% |# |> |\\\$ )"
set prompt1 "(>)"
set timeout -1
;###############################################################
;#connect to specified host given by addstaff or globalstaff.
spawn ssh $user#$host
expect {
"*word:*" {}
"*(yes/no)?*" {
send - "yes\r"
expect "*word:" { }
}
}
send - "$supass\r"
expect eof exit -re $prompt
send - "sudo bash\r"
expect {
"*word:*" {
send "$supass\r"
expect eof exit -re $prompt
}
"*" {
expect eof exit -re $prompt
}
}
send - "whoami\r"
expect eof exit -re $prompt
interact
;#send - "$command\r"
;# expect eof exit -re $prompt
;#Logout of current ssh session
;#send - "exit\r"
;#expect eof exit -re $prompt
;#send - "exit\r"
;#expect eof exit -re $prompt
} else {
send - "echo Sorry require user host command \n\n\r";
exit
}
If you have noticed I have commented out the commands which is not being sent - its using interact mode to allow you to actually log in without a password, user password defined at the top of the script... Once logged in you type as you would per normal ssh
Here is me running it locally:
whoami
myuser
./ssh-connection.exp localhost
spawn ssh myuser#localhost
myuser#localhost's password:
Welcome to Ubuntu 12.04.2 LTS (GNU/Linux XXXX)
* Documentation: https://help.ubuntu.com/
Last login: Sat Sep 7 20:19:53 2013 from localhost
sudo bash
This is BASH 4.2- DISPLAY on localhost:0.0
Sat Sep 7 20:25:09 BST 2013
whoami
[20:25 myuser#myuser-DP ~] > sudo bash
whoami
This is BASH 4.2- DISPLAY on localhost:0.0
Sat Sep 7 20:25:09 BST 2013
[20:25 myuser#myuser-DP ~] > whoami
root
[20:25 myuser#myuser-DP ~] > whoami
root
Within the script it also does a sudo bash which is why it reconnects me to localhost and I become root

Using expect to log into VPN from a bash script

I connect to a VPN from console using something like:
sudo openvpn my.conf
[sudo] password for user:
Enter Auth Username:my_user
Enter Auth Password:
I don't care about entering the admin pwd manually but I want to automate the vpn auth, I think expect is what I need to do this but I don't know how to use it and I never coded a bash script.
Could someone show me a simple bash script using expect so I can use it for this?
Something like that (untested)
#!/bin/usr/expect -f
spawn sudo openvpn my.conf
expect -r "\[sudo\] .*\: " {
send "my_ownpassword\n"
}
expect "Enter Auth Username:" {
send "my_user\n"
}
expect "Enter Auth Password:" {
send "my_vpnpassword\n"
}
interact
Or maybe like this:
#!/usr/bin/expect -f
# Constants
set user "my_user"
set pass "blablabla"
set sudo_pass "blablabla"
set timeout -1
# Options
match_max 100000
log_user 0
# Access to device
spawn sudo openvpn my.conf
expect "[sudo]*"
send -- "$sudo_pass\r"
expect "*?sername:*"
send -- "$user\r"
expect "*?assword:*"
send -- "$pass\r"
interact
close
This script will work with OTP authentication too, but you need to install oathtool.
#!/usr/bin/expect
set USERNAME your_username
set PASSWORD your_password
set KEY YOUR_OTP_KEY
set OVPN_PATH /path_to_your_config_file
set OTP [exec oathtool --totp -b $KEY]
eval spawn openvpn --config $OVPN_PATH
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "$USERNAME\r"
interact -o -nobuffer -re $prompt return
send "$PASSWORD\r"
interact -o -nobuffer -re $prompt return
send "$OTP\r"
interact
#!/usr/bin/expect -f
# creditability
set user "username"
set pass "password"
spawn openvpn file.ovpn
expect "*?sername:*"
send -- "$user\r"
expect "*?assword:*"
send -- "$pass\r"
interact
close

Resources