开发者

Temporarily remove the ssh private key password in a shell scriptI

I am required to deploy some files from server A to server B. I connect to server A via SSH and from there, connect via ssh to server B, using a private key stored on server A, the public key of which resides in server B's authorized_keys file. The connection from A to B happens within a Ba开发者_Python百科sh shell script that resides on server A.

This all works fine, nice and simple, until a security-conscious admin pointed out that my SSH private key stored on server A is not passphrase protected, so that anyone who might conceivably hack into my account on server A would also have access to server B, as well as C, D, E, F, and G. He has a point, I guess.

He suggests a complicated scenario under which I would add a passphrase, then modify my shell script to add a a line at the beginning in which I would call

ssh-keygen -p -f {private key file}  

answer the prompt for my old passphrase with the passphrase and the (two) prompts for my new passphrasw with just return which gets rid of the passphrase, and then at the end, after my scp command calling

ssh-keygen -p -f {private key file} 

again, to put the passphrase back

To which I say "Yecch!".

Well I can improve that a little by first reading the passphrase ONCE in the script with

read -s PASS_PHRASE

then supplying it as needed using the -N and -P parameters of ssh-keygen.

It's almost usable, but I hate interactive prompts in shell scripts. I'd like to get this down to one interactive prompt, but the part that's killing me is the part where I have to press enter twice to get rid of the passphrase

This works from the command line:

ssh-keygen -p -f {private key file} -P {pass phrase} -N ''

but not from the shell script. There, it seems I must remove the -N parameter and accept the need to type two returns.

That is the best I am able to do. Can anyone improve this? Or is there a better way to handle this? I can't believe there isn't.

Best would be some way of handling this securely without ever having to type in the passphrase but that may be asking too much. I would settle for once per script invocation.

Here is a simplified version the whole script in skeleton form

#! /bin/sh
KEYFILE=$HOME/.ssh/id_dsa
PASSPHRASE=''

unset_passphrase() {
        # params
        # oldpassword keyfile
        echo "unset_key_password()"
        cmd="ssh-keygen -p -P $1 -N '' -f $2"
        echo "$cmd"
        $cmd
        echo 
}

reset_passphrase() {
        # params
        # oldpassword keyfile
        echo "reset_key_password()"
        cmd="ssh-keygen -p -N '$1' -f $2" 
        echo "$cmd"
        $cmd
        echo
}

echo "Enter passphrase:"
read -s PASSPHRASE
unset_passphrase $PASSPHRASE $KEYFILE
# do something with ssh
reset_passphrase $PASSPHRASE $KEYFILE


Check out ssh-agent. It caches the passphrase so you can use the keyfile during a certain period regardless of how many sessions you have.

Here are more details about ssh-agent.


OpenSSH supports what's called a "control master" mode, where you can connect once, leave it running in the background, and then have other ssh instances (including scp, rsync, git, etc.) reuse that existing connection. This makes it possible to only type the password once (when setting up the control master) but execute multiple ssh commands to the same destination.

Search for ControlMaster in man ssh_config for details.

Advantages over ssh-agent:

  • You don't have to remember to run ssh-agent
  • You don't have to generate an ssh public/private key pair, which is important if the script will be run by many users (most people don't understand ssh keys, so getting a large group of people to generate them is a tiring exercise)
  • Depending on how it is configured, ssh-agent might time out your keys part-way through the script; this won't
  • Only one TCP session is started, so it is much faster if you're connecting over and over again (e.g., copying many small files one at a time)

Example usage (forgive Stack Overflow's broken syntax highlighting):

REMOTE_HOST=server

log() { printf '%s\n' "$*"; }
error() { log "ERROR: $*" >&2; }
fatal() { error "$*"; exit 1; }
try() { "$@" || fatal "'$@' failed"; }

controlmaster_start() {
    CONTROLPATH=/tmp/$(basename "$0").$$.%l_%h_%p_%r
    # same as CONTROLPATH but with special characters (quotes,
    # spaces) escaped in a way that rsync understands
    CONTROLPATH_E=$(
        printf '%s\n' "${CONTROLPATH}" |
        sed -e 's/'\''/"'\''"/g' -e 's/"/'\''"'\''/g' -e 's/ /" "/g'
    )
    log "Starting ssh control master..."
    ssh -f -M -N -S "${CONTROLPATH}" "${REMOTE_HOST}" \
        || fatal "couldn't start ssh control master"
    # automatically close the control master at exit, even if
    # killed or interrupted with ctrl-c
    trap 'controlmaster_stop' 0
    trap 'exit 1' HUP INT QUIT TERM
}

controlmaster_stop() {
    log "Closing ssh control master..."
    ssh -O exit -S "${CONTROLPATH}" "${REMOTE_HOST}" >/dev/null \
        || fatal "couldn't close ssh control master"
}

controlmaster_start
try ssh -S "${CONTROLPATH}" "${REMOTE_HOST}" some_command
try scp -o ControlPath="${CONTROLPATH}" \
    some_file "${REMOTE_HOST}":some_path
try rsync -e "ssh -S ${CONTROLPATH_E}" -avz \
    some_dir "${REMOTE_HOST}":some_path

# the control master will automatically close once the script exits


I could point out an alternative solution for this. Instead of having the key stored on server A I would keep the key locally. Now I would create a local port forward to server B on port 4000.

ssh -L 4000:B:22 usernam@A

And then in a new terminal connect through the tunnel to server B.

ssh -p 4000 -i key_copied_from_a user_on_b@localhost

I don't know how feasible this is to you though.


Building up commands as a string is tricky, as you've discovered. Much more robust to use arrays:

cmd=( ssh-keygen -p -P "$1" -N "" -f "$2" )
echo "${cmd[@]}"
"${cmd[@]}"

Or even use the positional parameters

passphrase="$1"
keyfile="$2"
set -- ssh-keygen -p -P "$passphrase" -N "" -f "$keyfile"
echo "$@"
"$@"

The empty argument won't be echoed surrounded by quotes, but it's there

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜