6

How can I solve this problem? I have a bash script (under Ubuntu Server), doing several SSH connections, executes some remote transfers, like rsync multiple times.

My problem is, that I would like to avoid entering password multiple times. But I don't want to use SSH public key exchange, because if anybody has the key, will be able to connect the remote computer.

I would like the following:

  • SSH should ask password when I start the bash script
  • reuse password, so don't ask password again until timeout
  • when timeout is over, ask password again.

Something similar, how sudo is working, just with ssh.

Any idea how to solve this?

EDIT1:

I found a new SSH feature named ssh multiplexing. Maybe using this I can reach the goal I want.

https://unix.stackexchange.com/questions/50508/reusing-ssh-session-for-repeated-rsync-commands

Would this work?

klor
  • 394

5 Answers5

7

It's not what you want to hear, but this is what key-based authentication is for. So long as you put a passphrase on your private key, it's no less secure than password authentication.

You can use ssh-agent to avoid needing to enter the passphrase every time, and the -t option to ssh-agent will give you the timeout behavior you're after.

# start a shell under ssh-agent with a 5-minute timeout for keys loaded with ssh-add
ssh-agent -t 300 /bin/bash

add your key(s) to the agent; ssh-add will prompt for passphrase, if one is set

ssh-add

do some stuff

ssh remote.server cat /some/file rsync file1 file2 klor@remote.server:/some/directory

after 300 seconds, timeout reached, run ssh-add again to re-add your keys

ssh-add

Your script will need some logic to determine when the timeout occurs. One way would be to run ssh and rsync with -o BatchMode=yes, which will prevent interactive authentication methods, so if the key is no longer usable, ssh will exit instead of prompting for a password. You can use the exit code to determine if you need to run ssh-add again; $? should be set to 255 in this case.

You'll still need to work out how to feed the passphrase to ssh-add, because it doesn't provide a way to accept it programmatically. Unless your script will prompt you to enter it by hand, you'll probably need to use expect for that part, and that will mean hard-coding the passphrase somewhere.

ᄂ ᄀ
  • 208
3

It seems I found the perfect solution for my needs:

Reusing ssh session for repeated rsync commands

#!/bin/bash

# Create ssh-mux (SSH multiplex) dir only if not exists
[[ ! -d dir ]] || mkdir ~/.ssh/ssh-mux

apache_site_path_source="/var/www/source_site"
apache_site_path_target="/var/www/target_site"

# Solution: Start SSH multiplexing session (works fine)
# https://unix.stackexchange.com/questions/50508/reusing-ssh-session-for-repeated-rsync-commands
sudo ssh -nNf -o ControlMaster=yes -o ControlPath="~/.ssh/ssh-mux/%L-%r@%h:%p" root@192.168.0.1

sudo rsync -av --progress -e 'ssh -l root -p 22 -o "ControlPath=~/.ssh/ssh-mux/%L-%r@%h:%p"' 192.168.0.1:${apache_site_path_source}/. ${apache_site_path_target}/;
printf "\n\n\n\n\n\n"
sudo rsync -av --progress -e 'ssh -l root -p 22 -o "ControlPath=~/.ssh/ssh-mux/%L-%r@%h:%p"' 192.168.0.1:${apache_site_path_source}/. ${apache_site_path_target}/;
printf "\n\n\n\n\n\n"
sudo rsync -av --progress -e 'ssh -l root -p 22 -o "ControlPath=~/.ssh/ssh-mux/%L-%r@%h:%p"' 192.168.0.1:${apache_site_path_source}/. ${apache_site_path_target}/;
printf "\n\n\n\n\n\n"

# Finish SSH multiplexing session
sudo ssh -O exit -o ControlPath="~/.ssh/ssh-mux/%L-%r@%h:%p" root@192.168.0.1

This solution asks password only once, then doing rsync 3 times without asking password again. Each rsync reuses the SSH connection using SSH multiplexing.

Could be possible to change the SSH config, and store the SSH multiplexing settings, but using this solution there is no need to change the server config, the script works as is.

klor
  • 394
1

If you're using a keyfile, you can use the AddKeyToAgent option (OpenSSH>=7.2).

ssh(1): Add an AddKeysToAgent client option which can be set to 'yes', 'no', 'ask', or 'confirm', and defaults to 'no'. When enabled, a private key that is used during authentication will be added to ssh-agent if it is running (with confirmation enabled if set to 'confirm').

So your script might look like this.

# make sure the ssh agent is running (may not be necessary on some systems)
eval "$(ssh-agent -s)"

ssh commands. only the first command needs AddKeysToAgent option

ssh -o AddKeysToAgent=yes user@host "echo hello" ssh user@host "echo world" ...

Evidlo
  • 123
0

Implementation of James Sneeringer's comment as a profile script.

# This script must be sourced to be useful. For example, if you name this script 
# ~/.bash_autossh, at the end of .bashrc, source $HOME/.bash_autossh
# Tested in bash & zsh
# MIT license, if you need a license.

# An absolute path to a private key:
AUTOLOADSSHID=$HOME/.ssh/id_ecdsa
# Timeout is in seconds, optional:
SSHID_TIMEOUT=60

auto_init_ssh_agent() {
  if [ "${SSH_AGENT_PID}x" = "x" ] || \
     [ "${SSH_AUTH_SOCK}x" = "x" ] || \
     ps -p "${SSH_AGENT_PID}" >& /dev/null || \
     [ ! -e "${SSH_AUTH_SOCK}" ]; then
    # There are problems with the agent, initialize a new agent.
    # Agents started this way should be manually stopped with ssh-agent -k
    source <(ssh-agent -s)
  fi
  if ! ssh-add -l | grep "${AUTOLOADSSHID}" >/dev/null; then
    # The autoload key is not present, try to load it.
    SSH_ADDTL_ARGS=()
    if [ "${SSHID_TIMEOUT}x" != "x" ]; then
      SSH_ADDTL_ARGS+=( -t "${SSHID_TIMEOUT}" )
    fi
    # This may ask for a password, as appropriate
    ssh-add "${SSH_ADDTL_ARGS[@]}" "${AUTOLOADSSHID}"
    unset SSH_ADDTL_ARGS
  fi
}

_autossh() {
  args=( "$@" )
  auto_init_ssh_agent
  ssh "${args[@]}"
}

alias autossh=_autossh

Then you can run the command autossh myserver and it will auto-initialize an agent if one isn't available. The alias is to prevent accidentally calling an interactive function from a script.

Problematically, on systems that don't automatically start an agent with the WM session, this may litter your machine with ssh-agent processes, you may need to ssh-agent -k to end it, or killall ssh-agent. Some additional scripting may be required if you want to share a single ssh-agent across an entire login session or across multiple process sessions.

-1

I'd normally tell you to try expect, but in this case I think it's beter to use sshpass, it is very easy to install and to use, here I post some use cases

sshpass -p<pass> ssh <args> sshpass -pfoobar ssh -o StrictHostKeyChecking=no user@host command_to_run

have fun :)

EDIT 1

on a script

#!/bin/bash
read -p "give me the password" pass
sshpass -p"$pass" ssh -o StrictHostKeyChecking=no user@host command_to_run