Translate

Archives

Using Expect to Transfer SSH Keys

TCL (Tool Command Language, typically pronounced as “tickle”) is a scripting language created by John Ousterhout in 1988 while working at the University of California, Berkeley. To provide additional functionality, TCL supports extensions. One of the most popular extensions is Expect which is a tool developed by Don Libes for automating and testing interactive applications such as ftp. telnet and ssh.

I assume you know how to generate SSH keys using ssh-keygen and understand that if you want to be able to ssh to a remote system without entering a password you need to create the keys without a passphrase. Once the private and public keys are generated the public key have to be copied to the the user account on the remote system that you wish to have access to and added to the remote users authorized_keys file. Typically scp or ftp is used for that task followed by ssh’ing to the remote system and then doing something like the following:

$ mkdir .ssh
$ chmod 700 .ssh
$ cd .ssh
$ touch authorized_keys
$ chmod 600 authorized_keys
$ cat ../id_dsa.pub >> authorized_keys
$ rm ../id_dsa.pub


Alternatively you can use the ssh-copy-id script to do all the above work for you. This is an interactive script that requires you to the enter the remote user’s password and possibly enter yes to add your local host to the remote user’s known_hosts file.

Here is a simple shell script which automates the generation of DSA keys with no passphrase for a user and transfers the public DSA key to a remote system using expect. Basically it is a wrapper around ssh-keygen and ssh-copy-id.

#!/bin/bash
#
# Copyright (c) Finnbarr P. Murphy 2006
#

# ---- start configurables ----
PATH=/usr/sbin:/usr/bin:/sbin:/bin
LOCALHOMEDIR=/home
# ----- end configurables ----- 


function usage {
    printf "Usage: setupkeys -U remote-username -P remote-password -H remote-host -u local-username\n"
    exit 2
}


# --- script starts here 
echo

(( $# == 0 )) && usage

(( $EUID != 0 )) && {
    echo "ERROR: You must be root to run this script."
    exit 1
}
username=""
password=""
host=""
localuser=""

while getopts "u:P:U:H:" OPTION
do
    case $OPTION in
        U)  username="$OPTARG";;
        P)  password="$OPTARG";;
        H)  host="$OPTARG";;
        u)  localuser="$OPTARG";;
      esac
done


# --- basic argument checking
if [[ -z "$username" ]]; then
    echo "ERROR - No username entered."
    exit 1
fi
if [[ -z "$password" ]]; then
    echo "ERROR - No passed entered."
    exit 1
fi
if [[ -z "$host" ]]; then
    echo "ERROR - No host entered."
    exit 1
fi
if [[ -z "$localuser" ]]; then
    echo "ERROR - No localuser entered."
    exit 1
fi

# --- do some sanity checking here 
echo -n "Checking if $localuser in /etc/passwd. "
grep "^$localuser:" /etc/passwd > /dev/null 2>&1
RESULT=$?
if (( RESULT == 1 )); then
    echo; echo "ERROR - $localuser not found in /etc/passwd."
    exit 1
fi
echo "Yes"

echo -n "Checking connectivity with $host. "
/bin/ping -q -c 2 $host > /dev/null 2>&1
RESULT=$?
if (( RESULT == 1 )); then
    echo; echo "ERROR - could not ping $host."
    exit 1
fi
echo "System is alive."

# --- check for $localuser public and private ssh keys  
# --- we need to be $localuser here when using ssh-* utilities
echo -n "Checking for $localuser ssh key files. "
SSH_KEYS_FOUND=0
if [[ -d $LOCALHOMEDIR/$localuser ]]; then
   if [[ -s $LOCALHOMEDIR/$localuser/.ssh/id_dsa && -s $LOCALHOMEDIR/$localuser/.ssh/id_dsa.pub ]]; then
      sudo -u $localuser -- /usr/bin/ssh-keygen -e -f $LOCALHOMEDIR/$localuser/.ssh/id_dsa.pub |  grep "1024-bit DSA"  > /dev/null 2>&1
      RESULT=$?
      if (( RESULT == 0 )); then
          SSH_KEYS_FOUND=1
      fi
   fi
fi
if (( SSH_KEYS_FOUND == 1 )); then
   echo "Found"
else
   echo "Not found"
   rm -rf $LOCALHOMEDIR/$localuser/.ssh
   mkdir $LOCALHOMEDIR/$localuser/.ssh
   chmod 700 $LOCALHOMEDIR/$localuser/.ssh
   chown -R $localuser:$localuser $LOCALHOMEDIR/$localuser/.ssh
   sudo -u $localuser -- /usr/bin/ssh-keygen -q -t dsa -N "" -f $LOCALHOMEDIR/$localuser/.ssh/id_dsa
   echo "New ssh key files generated (DSA protocol)"
fi
# --- add $localname's public key to $username@$host authorized keys
TMPUSER=expectscript-user.$$

cat <<EOT > $TMPUSER
#!/usr/bin/expect

if {[llength \$argv] != 4} {
   puts "usage: \$argv0 localuser username password host"
   exit 1
}

log_file -a expectscript-user.log
log_user 0

set localuser [lindex \$argv 0] 
set username  [lindex \$argv 1] 
set password  [lindex \$argv 2] 
set host      [lindex \$argv 3]

set timeout 60 

spawn /usr/bin/ssh-copy-id -i \$localuser/.ssh/id_dsa.pub \$username@\$host
expect {
    "assword: " {
        send "$password\n"
        expect { 
            "again."     { exit 1 }
            "expecting." { }
            timeout      { exit 1 }
        } 
    }
    "(yes/no)? " { 
        send "yes\n" 
        expect {
            "assword: " {
                send "$password\n"
                expect { 
                    "again."     { exit 1 }
                    "expecting." { }
                    timeout      { exit 1 }
                } 
            }
        }
    }
}

exit 0 
EOT

echo -n "Copying $localuser's public key to $host. "
chmod 755 $TMPUSER
sleep 3
./$TMPUSER $LOCALHOMEDIR/$localuser $username $password $host
RESULT=$?
rm -f $TMPUSER
if (( RESULT == 0 )); then
   echo "Succeeded"
   rm -f expectscript-user.log
else
   echo "Failed"
   echo "ERROR: Check expectscript-user.log"
   exit 1
fi

# we are done
echo "Setup completed. Goodbye."
exit 0


You may have to modify it if you use a shell other than bash as I have only tested in on Linux systems. If you want to use RSA, it is easy to modify the script to generate RSA keys.

Comments are closed.