Bash Script SSH Automation Without a Password Prompt

August 2nd, 2014 Permalink

The OpenSSH client used on Linux distributions really, really wants to prompt the user to enter a password. If you spend much time writing Bash scripts, you'll know that most commonly used command line software comes with easy options to suppress the prompts that usually appear. Think of setting the -y flag for apt-get or yum, for example, to suppress "do you wish to continue, y/n" prompts. Since most Bash scripting is automation, running without a terminal and out of sight of any actual user, you usually don't want your script to hang on a prompt. The ssh command provides no such convenience, however. If you find yourself in the position of writing an automation script that must SSH into a remote machine to run a command using password authentication, then you will have to either jump through some hoops or find a different way of achieving the same end.

The obvious and much better choice is to use public key authentication rather than a password, as that will not prompt. All scripts needing to use the ssh command should be set up to use keys, but it isn't always the case that you have either the necessary control over the remote server or ability to persuade its owners to do the right thing.

The technique shown here for using SSH in a script with password authentication but without a password prompt has been tested on recent versions of Ubuntu and CentOS, so should work for most Linux distributions. It uses a combination of (a) setsid to run ssh in a new session and (b) setting the SSH_ASKPASS environment variable. If SSH_ASKPASS is set to the path of a Bash script, OpenSSH will run the script and take the result as the password without a prompt - but only under somewhat contrived circumstances.

#----------------------------------------------------------------------
# Set up values.
#----------------------------------------------------------------------

# User credentials for the remote server.
USER=user
PASS=pass

# The server hostname.
SERVER=example.com

# The script to run on the remote server.
SCRIPT_PATH=/tmp/script.sh
# Desired location of the script on the remote server.
REMOTE_SCRIPT_PATH=/tmp/remote-script.sh

#----------------------------------------------------------------------
# Create a temp script to echo the SSH password, used by SSH_ASKPASS
#----------------------------------------------------------------------

SSH_ASKPASS_SCRIPT=/tmp/ssh-askpass-script
cat > ${SSH_ASKPASS_SCRIPT} <<EOL
#!/bin/bash
echo "${PASS}"
EOL
chmod u+x ${SSH_ASKPASS_SCRIPT}

#----------------------------------------------------------------------
# Set up other items needed for OpenSSH to work.
#----------------------------------------------------------------------

# Set no display, necessary for ssh to play nice with setsid and SSH_ASKPASS.
export DISPLAY=:0

# Tell SSH to read in the output of the provided script as the password.
# We still have to use setsid to eliminate access to a terminal and thus avoid
# it ignoring this and asking for a password.
export SSH_ASKPASS=${SSH_ASKPASS_SCRIPT}

# LogLevel error is to suppress the hosts warning. The others are
# necessary if working with development servers with self-signed
# certificates.
SSH_OPTIONS="-oLogLevel=error"
SSH_OPTIONS="${SSH_OPTIONS} -oStrictHostKeyChecking=no"
SSH_OPTIONS="${SSH_OPTIONS} -oUserKnownHostsFile=/dev/null"

#----------------------------------------------------------------------
# Run the script on the remote server.
#----------------------------------------------------------------------

# Load in a base 64 encoded version of the script.
B64_SCRIPT=`base64 --wrap=0 ${SCRIPT_PATH}`

# The command that will run remotely. This unpacks the
# base64-encoded script, makes it executable, and then
# executes it as a background task.
CMD="base64 -d - > ${REMOTE_SCRIPT_PATH} <<< ${B64_SCRIPT};"
CMD="${CMD} chmod u+x ${REMOTE_SCRIPT_PATH};"
CMD="${CMD} sh -c 'nohup ${REMOTE_SCRIPT_PATH} > /dev/null 2>&1 &'"

# Log in to the remote server and run the above command.
# The use of setsid is a part of the machinations to stop ssh
# prompting for a password.
setsid ssh ${SSH_OPTIONS} ${USER}@${SERVER} "${CMD}"