2

I have a bash script for copying two files from a remote machine (that I cannot control), stored in a a path that needs root access. Here it is:

ssh administrator@host "mkdir ${DIR}"
ssh -t administrator@host "sudo su - root -c 'cp /path/for-root-only/data1/${FILENAME} ${DIR}/'"
ssh administrator@host "mv ${DIR}/${FILENAME} ${DIR}/data1-${FILENAME}"
ssh -t administrator@host "sudo su - root -c 'cp /path/for-root-only/data2/${FILENAME} ${DIR}/'"
scp administrator@host:$DIR/{${FILENAME},data1-${FILENAME}} .
ssh administrator@host "rm -r ${DIR}"

The script prompt for the same password a lot of time. I tried to merge all commands through here document like this for ssh -t:

ssh -t administrator@host << EOF
    mkdir ${DIR}
    sudo su - root -c 'cp /path/for-root-only/data1/${FILENAME} ${DIR}/'
    mv ${DIR}/${FILENAME} ${DIR}/data1-${FILENAME}
    sudo su - root -c 'cp /path/for-root-only/data2/${FILENAME} ${DIR}/'
EOF
scp administrator@host:$DIR/{${FILENAME},data1-${FILENAME}} .
ssh administrator@host "rm -r ${DIR}"

but there is this warning:

Pseudo-terminal will not be allocated because stdin is not a terminal.

I would like to ask if there is a proper way to write that script to minimize the number of password prompting

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
floatingpurr
  • 455
  • 1
  • 6
  • 9

2 Answers2

3

Open an SSH connection once, and then piggyback on it. This feature of OpenSSH is called master connections. See Using an already established SSH channel

ssh_control_socket="$(mktemp)"
ssh -o ControlPath="$(ssh_control_socket)" -o ControlMaster=yes -o ControlPersist=yes administrator@host "mkdir ${DIR}"
# other commands using ssh (ssh, scp, rsync, …): pass the same ControlPath option
ssh -o ControlPath="$(ssh_control_socket)" -t administrator@host "…"
rsync -e "ssh -o 'ControlPath=$(ssh_control_socket)'" …
ssh -o ControlPath="$(ssh_control_socket)" administrator@host -O exit
rm -f "$(ssh_control_socket)"

Annoyingly, you need to explicitly pass a ControlPath option on every SSH invocation (it's the name of a file that's used to communicate between each ssh invocation and the original SSH client that made the actual connection). Here I pass an explicit socket name so that the script is self-contained. If relying on ~/.ssh/config is acceptable, then add this line to your ~/.ssh/config:

ControlPath ~/.ssh/%l_%h_%p_%r.multiplex

This makes every SSH client able to check whether there is an existing connection to piggyback on. The feature won't actually be used unless there is a listening master connection, you still need to pass -o ControlMaster=yes (or -M for short) on the first connection, i.e. your script can be

# Open the shared connection
ssh -M -o ControlPersist=yes administrator@host :
# … all SSH-relying commands in their basic form, they will go via the shared connection …
# Close the shared connection
ssh -O exit administrator@host
  • Host key verification failed. No ControlPath specified for "-O" command – floatingpurr Jul 20 '16 at 09:17
  • 1
    @superciccio14 Ah, I mistakenly thought that this was no longer necessary but it seems that it is. You need to have a ControlPath option to use the master connection feature, either on the command line or in the configuration file. Sorry. – Gilles 'SO- stop being evil' Jul 20 '16 at 09:46
1

You don't need to do it as a HERE document (which is what the << stuff does).

You can simply do ssh remotehost "command1; command2 ; command3"

e.g.

% ssh localhost "date ; uptime ; echo hello"
sweh@localhost's password: 
Tue Jul 19 08:07:48 EDT 2016
 08:07:48 up 15 days, 31 min,  3 users,  load average: 0.33, 0.33, 0.40
hello

The scp however, won't easily merge that way.

So you may want to look into using public key authentication instead of password authentication (also known as "ssh keys"). That's normally how things like this are automated.